// TestIFWebServices.java
// This class implements com.fujitsu.iflow.testframe.TestSet interface.
// This class consists of all possible positive testcases for IFWebServices module.
// NOTE: All methods are atomic.
package com.fujitsu.iflowqa.testframe;

import java.awt.Point;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Properties;
import java.util.StringTokenizer;

import com.fujitsu.iflow.model.workflow.Arrow;
import com.fujitsu.iflow.model.workflow.DataItemRef;
import com.fujitsu.iflow.model.workflow.Filter;
import com.fujitsu.iflow.model.workflow.Node;
import com.fujitsu.iflow.model.workflow.Plan;
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.WFSession;
import com.fujitsu.iflow.model.workflow.WorkItem;
import com.fujitsu.iflow.webservices.ObjList;
import com.fujitsu.iflow.webservices.UdaStruct;

public class TestIFWebServices implements TestSet
{
    TestRecorder tr;
    Properties serverProperties;
    WFSession wfSession;
    IFWSClient wsClient;

    public
    TestIFWebServices()
        throws Exception
    {
        serverProperties = new Properties();
    }

    public
    void
    runTests(
        TestRecorder ntr) throws Exception
    {
        tr = ntr;
        String webserviceurl = tr.getProperty("webserviceurl", "http://localhost:49950/iflowws/services/IFWebServices?wsdl");
        String webserviceurn = tr.getProperty("webserviceurn", "http://www.fsw.fujitsu.com");
        tr.log("Using webserviceurl = "+webserviceurl);
        tr.log("Using webserviceurn = "+webserviceurn);

        serverProperties.setProperty("role",tr.getRequiredProperty("role"));
        wsClient = new IFWSClient(tr.getRequiredProperty("username"),
                        tr.getRequiredProperty("password"),
                        webserviceurl, webserviceurn);
// This test incluces
// 1. getMyPlansTest
// 2. getMyPlanStateTest
        getMyPlansTest(tr);

// This test incluces
// 1. getMyProcessesTest(tr);
// 2. getMyProcessStateTest
        startProcessTest(tr);
// This test incluces
// 1. getUserDataTest
// 2. modifyUserDataItemTest
        modifyUserDataItemTest(tr);

// This test incluces
// 1. getUserDataTest
// 2. addUserDataItemTest
        addUserDataItemTest(tr);
// This test incluces
// 1. getMyWorkItemsTest(tr);
// 2. getWorkItemStateTest
// 3. getWorkItemChoicesTest
        submitWorkItemTest(tr);

    }

    void
    getMyPlansTest(
        TestRecorder tr)
        throws Exception
    {
        debug("getMyPlansTest");
        String errorDetail = "";
        ObjList[] objs = null;
        boolean idMatched = false;
        doLogin();
        try {
            long planId = createPlan();
            debug("Plan Id= "+planId);
            try {
                objs = wsClient.getMyPlans();
            }
            catch(Exception wse) {
                errorDetail = stringify(wse);
                tr.markFailed("getMyPlansTest, failed to get plans", errorDetail);
                throw wse;
            }
            if (objs != null) {
                for (int i=0;i<objs.length;i++) {
                    long id = objs[i].getId();
                    if (planId == id) {
                        idMatched = true;
                        break;
                    }
                }
            }
            if (idMatched) {
                tr.markPassed("Web Services Test: getMyPlansTest");
                serverProperties.setProperty("planid",Long.toString(planId));
                setProperties(serverProperties);
                // check getPlanStateTest case.
                getPlanStateTest(tr);
            } else {
                tr.markFailed("getMyPlansTest", "no plan with id "+planId+" found");
            }
        }
        catch(Exception e) {
            logError("getMyPlansTest", e);
        }
        finally {
            doLogout();
        }
    }

    void
    getPlanStateTest(
        TestRecorder tr)
        throws Exception
    {
        long planId = Long.parseLong(serverProperties.getProperty("planid"));
        String planState = "";
        try {
            planState = wsClient.getTemplateState(planId);
        }
        catch(Exception e) {
            logError("getPlanStateTest",e);
        }
        if (planState != null) {
            if (planState.equals("DRAFT")) {
                tr.markPassed("Web Services Test: getPlanStateTest");
            }
            else {
                tr.markFailed("getPlanStateTest", "Possibly an error");
            }
        }
    }

    void
    getMyProcessesTest(
        TestRecorder tr)
        throws Exception
    {
        String errorDetail = "";
        ObjList[] objs = null;
        boolean idMatched = false;
        try {
            doLogin();
            long planId = Long.parseLong(serverProperties.getProperty("planid"));
            long processId = Long.parseLong(serverProperties.getProperty("processid"));
            try {
                objs = wsClient.getMyProcesses();
            }
            catch(Exception e1) {
                errorDetail = stringify(e1);
            }
            if (objs != null) {
                for (int i=0;i<objs.length;i++) {
                    long id = objs[i].getId();
                    if (processId == id) {
                        idMatched = true;
                        break;
                    }
                }
            }
            if (idMatched) {
                tr.markPassed("Web Services Test: getMyProcessesTest");
                serverProperties.setProperty("processid",Long.toString(processId));
                setProperties(serverProperties);
            } else {
                tr.markFailed("getMyProcessesTest", errorDetail);
            }
        }
        catch(Exception e) {
            logError("getMyProcessesTest",e);
        }
        finally {
            doLogout();
        }
    }

    void
    startProcessTest(
        TestRecorder tr)
        throws Exception
    {
        String errorDetail = "";
        boolean started = false;
        String piState = "";
        try {            
            long planId = Long.parseLong(serverProperties.getProperty("planid"));
            long processId = wsClient.startProcess(planId);
            if (processId > 0) {
                serverProperties.setProperty("processid",Long.toString(processId));
                setProperties(serverProperties);
                tr.markPassed("Web Services Test: startProcessTest");
            }
            else {
                tr.markFailed("startProcessTest","Possibly an error");
            }
            // Test getMyProcesses.
            getMyProcessesTest(tr);
            // check getProcessStateTest case.
            getProcessStateTest(tr);
        }
        catch(Exception e) {
            logError("startProcessTest",e);
        }
    }

    void
    getProcessStateTest(
        TestRecorder tr)
        throws Exception
    {
        long processId = Long.parseLong(serverProperties.getProperty("processid"));
        String processState = "";
        try {
            processState = wsClient.getProcessState(processId);
        }
        catch(Exception e) {
            logError("getProcessStateTest",e);
        }
        if (processState != null) {
            if (processState.equals("RUNNING")) {
                tr.markPassed("Web Services Test: getProcessStateTest");
            }
            else {
                tr.markFailed("getProcessStateTest", "Possibly an error");
            }
        }
    }

    long
    getMyWorkItemsTest(
        TestRecorder tr)
        throws Exception
    {
        String errorDetail = "";
        ObjList[] objs = null;
        long objWorkItemId = -1;
        try {
            doLogin();
            long processId = Long.parseLong(serverProperties.getProperty("processid"));
            long workItemId = getMyWorkItem(processId);
            try {
                objs = wsClient.getMyWorkItems();
            }
            catch(Exception e1) {
                errorDetail = stringify(e1);
            }
            if (objs != null) {
                objWorkItemId = objs[0].getId();
                if (objWorkItemId == workItemId) {
                    tr.markPassed("Web Services Test: getMyWorkItemsTest");
                    serverProperties.setProperty("workitemid",Long.toString(objWorkItemId));
                    setProperties(serverProperties);
                }
            } else {
                tr.markFailed("getMyWorkItemsTest", errorDetail);
            }
        }
        catch(Exception e) {
            logError("getMyWorkItemsTest",e);
        }
        finally {
            doLogout();
        }
        return objWorkItemId;
    }

    void
    getWorkItemStateTest(
        TestRecorder tr,
        long workItemId)
        throws Exception
    {
        String workItemState = "";
        try {
            workItemState = wsClient.getWorkItemState(workItemId);
        }
        catch(Exception e) {
            logError("getWorkItemStateTest",e);
        }
        if (workItemState != null) {
            if (workItemState.equals("ACTIVE")) {
                serverProperties.setProperty("workitemstate",workItemState);
                setProperties(serverProperties);
                tr.markPassed("Web Services Test: getWorkItemStateTest");
            }
            else {
                tr.markFailed("getWorkItemStateTest",workItemState);
            }
        }
        else {
            tr.markFailed("getWorkItemStateTest", "Possibly an error");
        }
    }

    void
    modifyUserDataItemTest(
        TestRecorder tr)
        throws Exception
    {
        boolean success = false;
        long processId = Long.parseLong(serverProperties.getProperty("processid"));
        try {
            // Run getUserDataTest.
            getUserDataTest(tr);
            String[] udaNames = parseCSV(serverProperties.getProperty("udaname"));
            String[] udaTypes = parseCSV(serverProperties.getProperty("udatype"));
            String[] udaValues = parseCSV(serverProperties.getProperty("udavalue"));
            if (udaNames != null && udaTypes != null && udaValues != null) {
                for (int i=0;i<udaNames.length;i++) {
                    if (udaNames[i].equals("uda1")) {
                        udaValues[i] = "Changed";
                    }
                    if (udaNames[i].equals("uda2")) {
                        udaValues[i] = "false";
                    }
                    if (udaNames[i].equals("uda3")) {
                        udaValues[i] = "1111";
                    }
                    if (udaNames[i].equals("uda4")) {
                        udaValues[i] = "22222";
                    }
                    if (udaNames[i].equals("uda5")) {
                        udaValues[i] = "3333.3";
                    }
                    wsClient.modifyUserDataItem(processId, udaNames[i], udaValues[i]);
                }
                tr.markPassed("Web Services Test: modifyUserDataItemTest");
            } else {
                tr.markFailed("modifyUserDataItemTest","Possibly an error");
            }
        }
        catch(Exception e) {
            logError("modifyUserDataItemTest",e);
        }
    }

    void
    getUserDataTest(
        TestRecorder tr)
        throws Exception
    {
        String errorDetail = "";
        boolean success = false;
        UdaStruct[] udas = null;
        String udaName = "";
        String udaType = "";
        String udaValue = "";
        long processId = Long.parseLong(serverProperties.getProperty("processid"));
        try {
            udas = wsClient.getUserData(processId);
        }
        catch(Exception e1) {
            errorDetail = stringify(e1);
        }
        if (udas != null) {
            tr.markPassed("Web Services Test: getUserDataTest");
            for (int i=0;i<udas.length-1;i++) {
                udaName += udas[i].getName()+",";
                udaType += udas[i].getType()+",";
                udaValue += udas[i].getValue()+",";
            }
            udaName += udas[udas.length-1].getName();
            udaType += udas[udas.length-1].getType();
            udaValue += udas[udas.length-1].getValue();
            serverProperties.setProperty("udaname",udaName);
            serverProperties.setProperty("udatype",udaType);
            serverProperties.setProperty("udavalue",udaValue);
            setProperties(serverProperties);
        }
        else {
            tr.markFailed("getUserDataTest", errorDetail);
        }
    }

    void
    addUserDataItemTest(
        TestRecorder tr)
        throws Exception
    {
        String errorDetail = "";
        boolean success = false;
        long processId = Long.parseLong(serverProperties.getProperty("processid"));
        try {
            String udaName = "uda6";
            String udaType = "STRING";
            String udaValue = "new UDA";
            try {
                success = wsClient.addUserDataItem(processId, udaName, udaType, udaValue);
            }
            catch(Exception e1) {
                errorDetail = stringify(e1);
            }
            if (success) {
                getUserDataTest(tr);
                tr.markPassed("Web Services Test: addUserDataItemTest");
            }
            else {
                tr.markFailed("addUserDataItemTest", "Possibly an error");
            }
        }
        catch(Exception e) {
            logError("addUserDataItemTest",e);
        }
    }

    String
    getWorkItemChoicesTest(
        TestRecorder tr,
        long workItemId)
        throws Exception
    {
        String choice = "";
        try {
            choice = wsClient.getWorkItemChoices(workItemId);
        }
        catch(Exception e) {
            logError("getWorkItemChoicesTest",e);
        }
        if (choice != null) {
            tr.markPassed("Web Services Test: getWorkItemChoicesTest");
            serverProperties.setProperty("choice",choice);
            setProperties(serverProperties);
        }
        else {
            tr.markFailed("getWorkItemChoicesTest", "Possibly an error");
        }
        return choice;
    }

    void
    submitWorkItemTest(
        TestRecorder tr)
        throws Exception
    {
        try {
            // Test getMyWorkItems.
            long workItemId = getMyWorkItemsTest(tr);
            // Test getWorkItemState.
            getWorkItemStateTest(tr, workItemId);
            // Test getWorkItemChoices.
            String choice = getWorkItemChoicesTest(tr, workItemId);
            wsClient.submitWorkItem(workItemId, choice);
            doLogin();
            if (checkCompletedWorkItem(workItemId)) {
                tr.markPassed("Web Services Test: submitWorkItemTest");
            }
            else {
                tr.markFailed("submitWorkItemTest", "Possibly an error");
            }
        }
        catch(Exception e) {
            logError("submitWorkItemTest",e);
        }
        finally {
            doLogout();
        }
    }

    long
    createPlan()
        throws Exception
    {
        long planId = -1;
        String planName = "TestIFWebServicesPlan1";
        try {
            Plan plan = WFObjectFactory.getPlan();
            plan.setWFSession(wfSession);
            plan.startEdit();
            plan.setName(planName);
            plan.setTitle(planName+" - Title");
            // Start Node.
            Node startNode = plan.addNode("Start", Node.TYPE_START);
            startNode.setPosition(new Point(100, 150));
            // Activity Node1.
            Node node1 = plan.addNode(planName+"-Act1",Node.TYPE_ACTIVITY);
            node1.setPosition(new Point(200, 250));
            node1.setRole(serverProperties.getProperty("role"));
            // UDAs
            DataItemRef diRef1 = plan.addDataItemRef("uda1", DataItemRef.TYPE_STRING, "default");
            DataItemRef diRef2 = plan.addDataItemRef("uda2", DataItemRef.TYPE_BOOLEAN, "true");
            DataItemRef diRef3 = plan.addDataItemRef("uda3", DataItemRef.TYPE_INTEGER, "999");
            DataItemRef diRef4 = plan.addDataItemRef("uda4", DataItemRef.TYPE_LONG, "77777777");
            DataItemRef diRef5 = plan.addDataItemRef("uda5", DataItemRef.TYPE_FLOAT, "888.8");
            // Exit Node.
            Node exitNode = plan.addNode("Exit", Node.TYPE_EXIT);
            exitNode.setPosition(new Point(300,350));
            // Add Arrows.
            Arrow goArrow = plan.addArrow("Go", startNode, node1);
            Arrow stopArrow = plan.addArrow("Stop", node1, exitNode);
            Plan newPlan = plan.createProcessDef();
            Thread.sleep(500);
            planId = newPlan.getId();
        }
        catch(Exception e) {
            logError("createPlan", e);
        }
        return planId;
    }

    long
    createProcess(
        long planId)
        throws Exception
    {
        long processId = -1;
        try {
            ProcessInstance pi = WFObjectFactory.createProcessInstance(planId, wfSession);
            processId = pi.getId();
        }
        catch(Exception e) {
            logError("createProcess",e);
        }
        return processId;
    }

    boolean
    startProcess(
        long processId)
        throws Exception
    {
        boolean started = false;
        try {
            ProcessInstance pi = WFObjectFactory.getProcessInstance(processId, wfSession);
            pi.start();
            started = true;
        }
        catch(Exception e) {
            logError("startProcess",e);
        }
        return started;
    }

    long
    getMyWorkItem(
        long processId)
        throws Exception
    {
        long workItemId = -1;
        try {
            WFObjectList objList = WFObjectFactory.getWFObjectList(wfSession);
            objList.addFilter(WFObjectList.LISTFIELD_PROCESSINSTANCE_ID,
                    WFObjectList.SQLOP_EQUALTO,
                    "'"+Long.toString(processId)+"'");
            Object[] objs = null;
            int batchSize = 5;
            objList.openBatchedList(Filter.MyActiveWorkItems);
            while ((objs = objList.getNextBatch(5)) != null) {
                WorkItem wi = (WorkItem) objs[0];
                workItemId = wi.getId();
            }
            objList.closeList();
        }
        catch(Exception e) {
            logError("getMyWorkItem",e);
        }
        return workItemId;
    }

    private
    void
    addAttachment(
        long processId)
        throws Exception
    {
        String attachmentName = serverProperties.getProperty("attachmentname");
        String attachmentPath = serverProperties.getProperty("attachmentpath");
        try {
            ProcessInstance pi = WFObjectFactory.getProcessInstance(processId,wfSession);
            pi.startEdit();
            pi.addAttachment(attachmentName, attachmentPath);
            pi.commitEdit();
        }
        catch(Exception e) {
            logError("addAttachment",e);
        }
    }

    private
    boolean
    checkCompletedWorkItem(
        long workitemid)
        throws Exception
    {
        boolean success = false;
        WorkItem workItem = null;
        try {
            WFObjectList objList = WFObjectFactory.getWFObjectList(wfSession);
            objList.openList(Filter.MyCompletedWorkItems);
            Object[] listElements = objList.getAllObjects();
            if (listElements != null) {
                for (int i=0;i<listElements.length;i++) {
                    workItem = (WorkItem) listElements[i];
                    if (workitemid == workItem.getId()) {
                        success = true;
                        break;
                    }
                }
            }
            objList.closeList();
        }
        catch(Exception e) {
            logError("checkCompletedWorkItem",e);
        }
        return success;
    }

    private
    void
    doLogin()
        throws Exception
    {
        try {
            wfSession = WFObjectFactory.getWFSession();
            wfSession.initForApplication(null, tr.getProps());
            wfSession.logIn(tr.getRequiredProperty("servername")+"Flow",
                        tr.getRequiredProperty("username"),
                        tr.getRequiredProperty("password"));
         }
        catch(Exception e) {
            logError("doLogin", e);
        }
    }

    private
    void
    doLogout()
        throws Exception
    {
        debug("doLogout()");
        try {
            wfSession.logOut();
        }
        catch(Exception e) {
            logError("doLogout",e);
        }
    }

    void
    setProperties(
        Properties srvProperties)
        throws Exception
    {
/*
        debug("setProperties(srvProperties)");
        try {
            FileOutputStream fos = new FileOutputStream("server.properties");
            srvProperties.store(fos, "server.properties");
            fos.flush();
            fos.close();
        }
        catch(Exception e) {
            logError("setProperties",e);
        }
*/
    }

    String[]
    parseCSV(
        String source)
        throws Exception
    {
        StringTokenizer st = new StringTokenizer(source, ",");
        String[] tokens = new String[st.countTokens()];
        int i=0;
        while (st.hasMoreTokens()) {
            tokens[i] = st.nextToken();
            i++;
        }
        return tokens;
    }

    // this converts an exception stack trace to a string.  Seems like
    // the wrong thing to be doing, since the TestRecorder can be made to
    // print out stack traces.  The exceptoin objects already has the
    // stack trace within it, so why convert it to a string???????
    // Consider removing this.
    String
    stringify(
        Exception e)
    {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        return sw.getBuffer().toString();
    }

    void
    debug(
        String msg)
    {
        ;
    }

    void
    logError(String msg, Exception e)
        throws Exception
    {
        throw new ExceptionWrapper(msg,e);
    }

    /**
    * TestDriver is the class that knows how to run these tests.
    * So just call it with the name of this class in there.
    */
    public
    static
    void
    main(String[] args) {
        String[] newArgs = new String[1];
        newArgs[0] = "com.fujitsu.iflowqa.testframe.TestIFWebServices";
        TestDriver.main(newArgs);
    }

}
