package com.fujitsu.iflowqa.testframe;

// iflow classes.
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.util.Random;

import com.fujitsu.iflow.model.workflow.DataItem;
import com.fujitsu.iflow.model.workflow.Filter;
import com.fujitsu.iflow.model.workflow.Plan;
import com.fujitsu.iflow.model.workflow.ProcessInstance;
import com.fujitsu.iflow.model.workflow.TriggerResult;
import com.fujitsu.iflow.model.workflow.WFAdminSession;
import com.fujitsu.iflow.model.workflow.WFObjectFactory;
import com.fujitsu.iflow.model.workflow.WFObjectList;
import com.fujitsu.iflow.model.workflow.WorkItem;

/*
 * This class is to test the API WFObjectFactory.processTriggerEvent().
 * This class will import a given xpdl file and will create a plan out of it,
 * checks if there is a process created by the trigger event and if so commits all the
 * workitem for that process.
 */
public class TriggerAPITest implements TestSet {

    private final static long SLEEP_TIME = 10000; // 10 seconds.
    private TestRecorder tRecorder;
    private WFAdminSession wfSession;
    private String RESULT_UDA = "TestResult";
    private String RESULT_PASSED = "Passed";
    private String RESULT_FAILED = "Failed";

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

    /*
     * This method runs the rest.
     */
    public void runTests(TestRecorder tRecorder) throws Exception
    {
        this.tRecorder = tRecorder;
        debug("runTests()");

        try {
            logIn();
            // the fist argument from the tRecorder will be the xpdl file name.
            String[] commandLineArgs = tRecorder.getCommandLineArgs();
            long planId = createPlanFromXPDL(commandLineArgs[1]);

            // second argument is the trigger event xml file name.
            postTriggerEvent(commandLineArgs[2]);

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

    /*
     * This method creates the template from the XPDL
     */
    public
    long
    createPlanFromXPDL(
        String fileName)
        throws Exception
    {
        debug("createPlanFromXPDL()");
        FileInputStream fis = null;
        long id = -1;
        try {
            Plan plan = WFObjectFactory.getPlan();
            plan.setWFSession(wfSession);
            fis = new  FileInputStream(fileName);
            plan.convertFromXPDL(fis);
            Plan newPlan = plan.createProcessDef();
            tRecorder.markPassed("Plan.createPlanFromXPDL()");
            id = newPlan.getId();
        }
        catch (Exception e) {
            logException("createPlanFromXPDL() - Possibly because of Plan.convertFromXPDL()" , e);
        }
        finally {
            if (fis != null)
                fis.close();
        }
        return id;
    }

    /*
     * This method creates the Process for the given template.
     */
    public
    boolean
    postTriggerEvent(
        String fileName)
        throws Exception
    {
        debug("postTriggerEvent(" + fileName + ")");

        try {
            StringBuffer sb = new StringBuffer();
            BufferedReader in = new BufferedReader(new FileReader(fileName));
            String line = "";
            while ((line = in.readLine()) != null)
                sb.append(line.trim());

            in.close();

            if (sb.toString().length() > 0) {
                debug(" firing trigger event");
                TriggerResult triggerResult = WFObjectFactory.processTriggerEvent(sb.toString(), wfSession);

                if (triggerResult.succeeded()) {
                    tRecorder.markPassed("WFObjectFactory.processTriggerEvent()");
                }
                else {
                    tRecorder.markFailed("WFObjectFactory.processTriggerEvent()", "");
                }
            }
            else {
                tRecorder.markFailed(" Event xml is empty", "Invalid xml file content");
            }
            Thread.sleep(SLEEP_TIME);
        }
        catch (Exception e) {
            logException("postTriggerEvent() - Possibly because of WFSesion.processTriggerEvent()", e);
        }

        return true;
    }

    /*
     * This method creates the Process for the given template.
     */
    public
    long
    locateTheActiveProcessForGivenPlanId(
        long planId)
        throws Exception
    {
        debug("locateTheActiveProcessForGivenPlanId()");
        long id = -1;
        try {
            WFObjectList wfol = WFObjectFactory.getWFObjectList(wfSession);
            wfol.addFilter(WFObjectList.LISTFIELD_PLAN_ID , "=", "'"+ planId +"'");
            wfol.openBatchedList(Filter.MyActiveProcesses);
            tRecorder.markPassed("WFObjectList.openBatchedList()");
            Object[] actProcessList = wfol.getNextBatch(0, 99);

            if ( (actProcessList == null) || (actProcessList.length == 0) ) {
                tRecorder.markFailed("locateTheActiveProcessForGivenPlanId()", "Possibly the trigger event was not fired successfully");
            }

            Random randNo = new Random();
            ProcessInstance pi = (ProcessInstance) actProcessList[randNo.nextInt(actProcessList.length )];
            id = pi.getId();
        }
        catch (Exception e) {
            logException("locateTheActiveProcessForGivenPlanId() - Possibly because of WFObjectList.openBatchedList())", e);
        }
        return id;
    }

    /*
     * This mehtod handles all the workitems for the give user and on the given process.
     */
    private
    void
    handleWorkItems(
        long procInstId)
        throws Exception
    {
        debug("handleWorkItems()");
        try {
            while (true) {
                WFObjectList wfol = WFObjectFactory.getWFObjectList(wfSession);
                wfol.addFilter(WFObjectList.LISTFIELD_PROCESSINSTANCE_ID , "=", "'"+ procInstId +"'");
                wfol.openBatchedList(Filter.MyActiveWorkItems);
                //tRecorder.markPassed("WFObjectList.openBatchedList()");
                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 ;
                }

                Random randNo = new Random();
                WorkItem workItem = (WorkItem) workItemList[randNo.nextInt(workItemList.length )];
                String[] choices = workItem.getChoices();
                //tRecorder.markPassed("WorkItem.getChoices()");
                workItem.makeChoice(choices[randNo.nextInt(choices.length)]); // always select the first choice.
                //tRecorder.markPassed("WorkItem.makeChoice()");
                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) {
                tRecorder.markFailed("Unable to get UDA named '"+ RESULT_UDA +"'", "May be the UDA is missing");
                return;
            }

            if (di.getValue().equalsIgnoreCase(RESULT_PASSED)) {
                tRecorder.markPassed("Test case passed");
            }
            else {
                tRecorder.markFailed("Test case failed", "The Result UDA value is wrong");
            }
        } catch (Exception e) {
            logException("checkResultUDA() - Possibly because of ProcessInstance.getDataItem()", e);
        }
    }


    /*
     * Login
     */
    public
    void
    logIn()
        throws Exception
    {
        debug("logIn(server=" + tRecorder.getRequiredProperty("servername") +
                  ", user=" + tRecorder.getRequiredProperty("username") + ")");
        try {
            String transPortType = tRecorder.getRequiredProperty("TWFTransportType");
            wfSession = WFObjectFactory.getWFAdminSession();
            wfSession.initForApplication(null, tRecorder.getProps());
            wfSession.logIn(tRecorder.getRequiredProperty("servername") +"Flow",
                            tRecorder.getRequiredProperty("username"),
                            tRecorder.getRequiredProperty("password"));
        }
        catch (Exception e) {
            logException("Failed to Login", e);
        }
    }

    /*
     * LogOut
     */
    public
    void
    logOut()
        throws Exception
    {
        debug("logOut()");
        try {
            wfSession.logOut();
            tRecorder.markPassed("WFSession.logOut()");
        } catch(Exception e) {
            logException("Failed to Logout ", e);
        }
    }

    public
    void
    debug(String msg)
    {
        // uncomment if you want debug output
        // tRecorder.log(msg);
    }

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

}

