package com.fujitsu.iflowqa.testframe;

// iflow classes.
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileReader;
import java.net.URL;
import java.util.Random;

import javax.xml.parsers.DocumentBuilder;

import org.apache.soap.Constants;
import org.apache.soap.Envelope;
import org.apache.soap.SOAPException;
import org.apache.soap.messaging.Message;
import org.apache.soap.transport.SOAPTransport;
import org.apache.soap.util.xml.XMLParserUtils;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;

import com.fujitsu.iflow.model.workflow.DataItem;
import com.fujitsu.iflow.model.workflow.Filter;
import com.fujitsu.iflow.model.workflow.ProcessInstance;
import com.fujitsu.iflow.model.workflow.WFObjectFactory;
import com.fujitsu.iflow.model.workflow.WFObjectList;
import com.fujitsu.iflow.model.workflow.WorkItem;
// jdk classes.

public class SoapTest extends BPMTestBase implements TestSet
{

    private final static long SLEEP_TIME = 10000; // 10 seconds.
    private String RESULT_UDA = "TestResult";
    private String RESULT_PASSED = "Passed";
    private String RESULT_FAILED = "Failed";
    private String clientListnerHR = "id=urn:HRClientListener";
    private String clientListnerPO = "id=urn:POClientListener";
    private String soapRouterUrl;

    /*
     * Zero argument constructor.
     */
    public SoapTest() {
    }

    /*
     * This method runs the rest.
     */
    public void runTests(TestRecorder tRecorder) throws Exception
    {
        init(tRecorder);

        try {
            // check if the soap web app is deployed and available.
            // routine will report error if false is returned
            // no point in testing further.
            if (isSoapWebAppDeployment() == false) {
                return;
            }

            login();
            // the fist argument from the tr will be the xpdl file name.
            String[] commandLineArgs = tRecorder.getCommandLineArgs();
            createPlanFromXPDL(commandLineArgs[1]);

            // the second argument from the tr will be the test soap envelope file name.
            long piId = postTestSoapMessage(commandLineArgs[2]);

            handleWorkItems(piId);
            // finally check the value of the Result UDA.
            checkResultUDA(piId);
            logOut();
        } catch (Exception e) {
            logException("Exception caught in main test level", e);
        }
    }


    public
    boolean
    isSoapWebAppDeployment()
        throws Exception
    {
        debug("isSoapWebAppDeployment()");
        try {

            // example "http://raja:9004/soap/admin/list.jsp"
            String soapAdminServiceListingUrl =  tr.getRequiredProperty("SoapAdminServiceListingUrl");

            // example - http://localhost:9004/soap/servlet/messagerouter
            soapRouterUrl =  tr.getRequiredProperty("SoapRouterUrl");

            // get the soap admin's service listing.
            if (soapAdminServiceListingUrl == null || soapAdminServiceListingUrl.length() == 0) {
                tr.markFailed("Deployment of SOAP Services.", "Unable get the to get 'SoapAdminServiceListingUrl' param from the configuration file, May be the parm is missing");
                return false;
            }

            // get the soap admin's service listing.
            if (soapRouterUrl == null || soapRouterUrl.length() == 0) {
                tr.markFailed("Deployment of SOAP Services.", "Unable get the to get 'SoapRouterUrl' param from the configuration file, May be the parm is missing");
                return false;
            }

            URL url = new URL(soapAdminServiceListingUrl);
            BufferedInputStream bis = new BufferedInputStream(url.openStream());
            int len = bis.available();
            byte[] ba = new byte[len];
            bis.read(ba);
            String output = new String(ba);

            // check if the output has some information about the deployed services.
            if (output != null && output.length() >0) {
                if ( (output.indexOf(clientListnerHR) != -1) &&
                     (output.indexOf(clientListnerPO) != -1) )
                {
                    tr.markPassed("Deployment of SOAP Services.");
                    return true;
                }
                else {
                    tr.markFailed("Deployment of SOAP Services.", "Possibly iflow soap Web App is not deployed");
                    return false;

                }
            }
        }
        catch (Exception e) {
            logException("isSoapWebAppDeployment() - Possibly iflow soap Web App is not deployed", e);
        }
        return false;
    }


    /**
     * This class sends a message by taking in an XML file that has a full
     * SOAP envelope in it and dumping it to the given message router URL.
     * Any content from the response is dumped to stdout.
     *
     */
    public
    long
    postTestSoapMessage(
        String fileName)
        throws Exception
    {
        debug("postTestSoapMessage( " +fileName +")");

        long id = -1;
        FileReader fr = null;
        try {
            // get the envelope to send
            fr = new FileReader (fileName);
            DocumentBuilder xdb = XMLParserUtils.getXMLDocBuilder();
            Document doc = xdb.parse (new InputSource (fr));
            if (doc == null) {
              throw new SOAPException (Constants.FAULT_CODE_CLIENT, "Soap Envelope parsing error");
            }
            Envelope msgEnv = Envelope.unmarshall (doc.getDocumentElement ());

            // send the message
            Message msg = new Message ();
            msg.send (new URL (soapRouterUrl), "urn:this-is-the-action-uri", msgEnv);

            // receive whatever from the transport and dump it to the screen
            SOAPTransport st = msg.getSOAPTransport ();
            BufferedReader br = st.receive ();
            String line;
            StringBuffer sb = new StringBuffer();
            while ((line = br.readLine ()) != null) {
                sb.append(line);
            }

            // get the process id that was created out of the soap request.
            id = new Long(getValueFromSoapResponse(sb.toString(), "returnvalue")).longValue(); // which is the processid.
            tr.markPassed("Process was created sucessfully with the id " + id);
        }
        catch (Exception e) {
            logException("postTestSoapMessage() - Possibly because of Plan.convertFromXPDL() / WFAdminSession.publishPlan()" , e);
        }
        finally {
            if (fr != null)
                fr.close();
        }
        return id;
    }

    private
    String
    getValueFromSoapResponse(
        String response,
        String tagName)
        throws Exception
    {
        if (response == null || response.length() == 0)
            return "";

        // NOTE: this is unsafe, should be parsed to a DOM, and then the DOM examined!
        int endPos = response.lastIndexOf("</"+ tagName +">");
        int startPos = response.lastIndexOf(">", endPos);
        response.lastIndexOf(">", endPos);
        String value = response.substring(startPos+1, endPos);
        return value;
    }

    /*
     * This method handles all the workitems for the give user and on the given process.
     */
    private
    void
    handleWorkItems(
        long procInstId)
        throws Exception
    {
        debug("handleWorkItems()");
        Random randNo = new Random();
        try {
            while (true) {
                WFObjectList wfol = WFObjectFactory.getWFObjectList(wfSession);
                wfol.addFilter(WFObjectList.LISTFIELD_PROCESSINSTANCE_ID , "=", "'"+ procInstId +"'");
                wfol.openBatchedList(Filter.MyActiveWorkItems);
                Object[] workItemList = wfol.getNextBatch(0, 99);

                if ( (workItemList == null) || (workItemList.length == 0) ) {
                    debug("No more Wotkitems on this process for this user to make choice on.");
                    return ;
                }

                WorkItem workItem = (WorkItem) workItemList[randNo.nextInt(workItemList.length )];
                String[] choices = workItem.getChoices();
                workItem.makeChoice(choices[randNo.nextInt(choices.length)]); // always select the first choice.
                Thread.sleep(SLEEP_TIME);
            }
        }
        catch (Exception e) {
            logException("handleWorkItems() - Possibly because of WorkItem.makeChoice()", e);
        }
    }

    /*
     * This mehtod handles all the workitems for the give user and on the given process.
     */
    private
    void
    checkResultUDA(
        long procInstId)
        throws Exception
    {
        debug("checkResultUDA()");
        try {
            ProcessInstance pi = WFObjectFactory.getProcessInstance(procInstId, wfSession);
            DataItem di = null;
            try
            {
                di = pi.getDataItem(RESULT_UDA);
            } catch (Exception e) {
                tr.markFailed("Check Restult UDA", "Unable to get UDA named '"+ RESULT_UDA +"'.  because: "+e.toString());
                return;
            }

            testAndReport("Check Restult UDA", di.getValue(), RESULT_PASSED);
        } catch (Exception e) {
            logException("checkResultUDA() - Possibly because of ProcessInstance.getDataItem()", e);
        }
    }



    public
    void
    debug(String msg)
    {
        tr.log(msg);
    }

    public
    void
    logException(
        String message,
        Exception e)
        throws Exception
    {
        tr.log(message);
        throw new ExceptionWrapper(message, e);
    }

}

