/********************************************************************************
 *                                                                              *
 *  COPYRIGHT (C) 1997-2003 FUJITSU SOFTWARE CORPORATION.  ALL RIGHTS RESERVED. *
 *                                                                              *
 ********************************************************************************/

package com.fujitsu.iflowqa.testframe;

import java.util.StringTokenizer;
import java.util.Vector;

import com.fujitsu.iflow.model.workflow.ArrowInstance;
import com.fujitsu.iflow.model.workflow.DataItem;
import com.fujitsu.iflow.model.workflow.NodeInstance;
import com.fujitsu.iflow.model.workflow.Plan;
import com.fujitsu.iflow.model.workflow.ProcessInstance;
import com.fujitsu.iflow.model.workflow.ProcessMigrationResult;
import com.fujitsu.iflow.model.workflow.WFObjectFactory;

/*
 * This will Test The Process Migation. The old template has two uda and
 * one regular Activity, The new Template has two Activity
 * and has two uda. One is same like the old Template other is new.
 */

public class ProcessMigrationTest extends BPMTestBase
{
    private static final String IN_MIGRATION_PROC_DEF_XPDL  = "PiMigrationOriginal.xpdl";
    private static final String OUT_MIGRATION_PROC_DEF_XPDL = "PiMigrationNew.xpdl";

    private static final int NO_OF_PROCESS = 5;

    private String finalStringUdaName =  "uda5";
    private String finalStringUdaValue = "abcd";

    private String finalIntUdaName =  "uda6";
    private String finalIntUdaValue = "5";

    private String existingNode = "act_1";
    private String newNode = "act_2";

    private String arrowTodelete = "arw_2";
    private String arrowToadd = "arw_3";
    private String exitArrow = "arw_4";

     private long oldPlanId = -1;
     private long newPlanId = -1;

     private long waitTime = 500;


    TestRecorder              tr;

    public ProcessMigrationTest()
    {
    }

    public void runTests(TestRecorder ntr)
    throws Exception
    {
        boolean dleteProcDef = true;
        try {
            tr = ntr;
            init(tr) ;
            login() ;
            String[] commandLineArgs = tr.getCommandLineArgs();
            if(commandLineArgs.length >= 4) {
               testMigration(commandLineArgs);
               dleteProcDef = false;
               return;
            }
            String[] names = new String[2];
            names[0] = IN_MIGRATION_PROC_DEF_XPDL;
            names[1] = OUT_MIGRATION_PROC_DEF_XPDL;
            long[] planIds = importTemplates(tr.getTestDir(), names);
            try {
                Thread.sleep(waitTime);
            }
            catch(Exception e)
            {
                //Interupted Exception
            }
            Plan originalPlan = null;
            Plan newPlan = null;

            for(int i=0; i<2; i++){
                Plan p = WFObjectFactory.getPlan(planIds[i], wfSession);
                if(p.getName().equals("PiMigrationOriginal"))
                {
                    originalPlan =  p;
                }
                else if(p.getName().equals("PiMigrationNew"))
                {
                    newPlan =  p;
                    newPlanId = p.getId();
                }
            }
            Vector procList = startProcesses(originalPlan.getId(), NO_OF_PROCESS);

            long[] instanceIds = new long[procList.size()];
            for(int i=0; i<procList.size(); i++)
            {
                instanceIds[i] =  ((ProcessInstance)procList.get(i)).getId();

            }

            //For slow machine, sometime server need longer time the activate an Activity.
            //Need to verify this before migrate process so that mogration will not be failed.
            checkForActiveNode(instanceIds);

            //Migration
            ProcessMigrationResult pmrl =
                    this.wfSession.migrateProcess(instanceIds, newPlan.getId(), true);
            printResult(pmrl);
            verifyMigrationResult(procList,pmrl,newPlan);
/*
            //EnactmentEdit
            ProcessMigrationResult pmr1 = testEnactmentEdit((ProcessInstance)procList.get(0));
            printResult(pmr1);
            verifyEnactmentResult(procList,pmr1);

            //Structurul Edit
            ProcessMigrationResult pmr2 = testStructuralEdit((ProcessInstance)procList.get(0));
            printResult(pmr2);
            verifyStructureResult(procList,pmr2);
  */
        }
        catch (Exception e) {
            tr.log("ProcessMigrationTest(Multiple Edit) caught exception: ") ;
            throw e ;
        }
        finally
        {
            if(dleteProcDef){
                close();
            }
            else
                logOut();
        }
    }

    private
    Vector
    startProcesses(long planId, int numberOfProcess)
    {
        String testCtxt = "Start Process Instance" ;
        String idList = "";
        Vector processList = new Vector();
        try {
            Plan pln = WFObjectFactory.getPlan(planId, wfSession);
            for(int i=0; i<numberOfProcess; i++)
            {
                ProcessInstance pi = pln.createProcessInstance();
                pi.start();
                processList.add(pi);
                if(idList == "")
                    idList = String.valueOf(pi.getId());
                else
                    idList = idList + "," + String.valueOf(pi.getId());

            }
        }
        catch(Exception e)
        {
            //

        }

        if (processList.size() !=  numberOfProcess)
            tr.markFailed(testCtxt, "Failed to Start All Process.");
        else
            tr.markPassed(testCtxt +" started " + numberOfProcess + " process ids:" + idList);

        return processList;
    }

    private
    ProcessMigrationResult
    testEnactmentEdit(ProcessInstance pi)throws Exception
    {
        pi.startEdit();
        addDataItem(pi);
        //return pi.commitMulipleEdit(null, true);
        return null;
    }

    private
    ProcessMigrationResult
    testStructuralEdit(ProcessInstance pi)throws Exception
    {
        pi.startEdit();
        pi.startStructuralEdit();
        NodeInstance dni = pi.addNodeInstance(newNode, NodeInstance.TYPE_ACTIVITY);
        NodeInstance sni = getNodeInstance(pi, existingNode, NodeInstance.TYPE_ACTIVITY);
        NodeInstance eni = getNodeInstance(pi, null, NodeInstance.TYPE_EXIT);
        ArrowInstance ai =  getArrowInstance(pi, arrowTodelete);
        pi.removeArrowInstance(ai.getArrowInstanceId());
        pi.addArrowInstance(arrowToadd, ArrowInstance.TYPE_REGULAR, sni,dni);
        pi.addArrowInstance(exitArrow, ArrowInstance.TYPE_REGULAR, sni,dni);
        //return pi.commitMulipleEdit(null, true);
        return null;
    }

    private
    void
    printResult(ProcessMigrationResult pmr)
    {
        long ids[] = pmr.getAllProcessId();
        tr.log("********* ProcessMigrationResult *********");
        for(int i=0; i<ids.length; i++)
        {
            long id = ids[i];
            String message = "PID: " + id + " SUCCESS: " + pmr.isSuccess(id)
                + " NAME: " + pmr.getProcessName(id)
                + " PLANID: " +  pmr.getTemplateId(id)
                + " STATE: " + pmr.getProcessState(id)
                + " EXCEPTION: " + pmr.getException(id);
            tr.log(message);
        }
        tr.log("********* ProcessMigrationResult *********");
    }

    private
    void
    verifyMigrationResult(
        Vector procList,
        ProcessMigrationResult pmr,
        Plan newPlan)throws Exception
    {
        String testCtxt = " Process Migration " ;
        long planId = newPlan.getId();
        int noOfActivity = newPlan.getNodes().length;
        int noOfArrow = newPlan.getArrows().length;
        String newUda = "uda3";
        String oldUda = "uda2";
        for(int i=0; i<procList.size(); i++)
        {
            ProcessInstance pi = (ProcessInstance)procList.get(i);
            long pid = pi.getId();
            pi = WFObjectFactory.getProcessInstance(pid, wfSession);
            long tmpPlanId = pi.getTemplateId();
            String ctx1 = "PID: " +  pid + testCtxt + " Matching PlanId.";
            testAndReport(ctx1, String.valueOf(tmpPlanId), String.valueOf(planId));

            String ctx2 = "PID: " +  pid + testCtxt + " Matching No Of Activity.";
            testAndReport(ctx2,pi.getNodeInstances().length, noOfActivity);

            String ctx3 = "PID: " +  pid + testCtxt + " Matching No Of Arrows.";
            testAndReport(ctx3, pi.getArrowInstances().length, noOfArrow);

            DataItem di = null;
            try {
                di = pi.getDataItem(newUda);
            }
            catch(Exception e)
            {

            }
            String ctx4 = "PID: " +  pid + testCtxt + " Matching Added Uda Name.";
            if(di != null)
            {
                testAndReport(ctx4,di.getName(), newUda);
            }
            else
                testAndReport(ctx4, "NOUDA", newUda);

            DataItem deletedDi = null;
            try {
                deletedDi = pi.getDataItem(oldUda);
            }
            catch(Exception e)
            {

            }
            String ctx6 = "PID: " +  pid + testCtxt + " Confirm Uda Deletion.";
            if(deletedDi != null)
            {
                testAndReport(ctx6, deletedDi.getName(),"NOUDA");
            }
            else
            {
                testAndReport(ctx6, "NOUDA", "NOUDA");
            }
        }
    }


    private
    void
    verifyEnactmentResult(Vector procList, ProcessMigrationResult pmr)throws Exception
    {

        String testCtxt = " Multiple Enactment Edit Add Data item." ;
        for(int i=0; i<procList.size(); i++)
        {
            ProcessInstance pi = (ProcessInstance)procList.get(i);
            pi = WFObjectFactory.getProcessInstance(pi.getId(), wfSession);
            String tmpCtxt = "PID: " +  pi.getId() + testCtxt;
            String value = pi.getDataItem(finalStringUdaName).getValue();
            testAndReport(tmpCtxt, value, finalStringUdaValue);
            String intValue = pi.getDataItem(finalStringUdaName).getValue();
            testAndReport(tmpCtxt, intValue, String.valueOf(finalIntUdaValue));

            String ctx1 = "PID: " +  pi.getId() + " Process Migration For Enactment Edit.";
            String.valueOf(finalIntUdaValue);
            testAndReport(ctx1, "true", String.valueOf(pmr.isSuccess(pi.getId())));
        }
    }

    private
    void
    verifyStructureResult(Vector procList, ProcessMigrationResult pmr)throws Exception
    {
        String testCtxt = " Multiple Structural Edit " ;
        long planId = -1;
        for(int i=0; i<procList.size(); i++)
        {
            ProcessInstance pi = (ProcessInstance)procList.get(i);
            long pid = pi.getId();
            pi = WFObjectFactory.getProcessInstance(pid, wfSession);
            Plan pl = pi.getPlan();
            long tmpPlanId = pl.getId();
            if(i==0){
                planId = tmpPlanId;
                newPlanId = planId;
            }
            String ctx1 = "PID: " +  pid + testCtxt + " Matching PlanId.";
            testAndReport(ctx1, String.valueOf(tmpPlanId), String.valueOf(planId));

            boolean acFound = false;
            NodeInstance dni = getNodeInstance(pi, newNode, NodeInstance.TYPE_ACTIVITY);
            if(dni != null)
                acFound = false;
            ctx1 = "PID: " +  pid + testCtxt + " New NodeInstance Added.";
            testAndReport(ctx1, String.valueOf(acFound), "true");

            boolean arFound = false;
            ArrowInstance ai =  getArrowInstance(pi, arrowToadd);
            NodeInstance sni = getNodeInstance(pi, existingNode, NodeInstance.TYPE_ACTIVITY);
            if(ai != null){
                if(ai.getSourceNodeInstance().getNodeInstanceId() == sni.getNodeInstanceId()
                            &&
                   ai.getTargetNodeInstance().getNodeInstanceId() == dni.getNodeInstanceId() )
                arFound = true;
            }

            ctx1 = "PID: " +  pid + testCtxt + " New ArrowInstance Between Nodes Added.";
            testAndReport(ctx1, String.valueOf(arFound), "true");

            boolean earFound = false;
            ArrowInstance eai =  getArrowInstance(pi, exitArrow);
            NodeInstance eni = getNodeInstance(pi, null, NodeInstance.TYPE_EXIT);
            if(eai != null){
                if(eai.getSourceNodeInstance().getNodeInstanceId() == dni.getNodeInstanceId()
                            &&
                   eai.getTargetNodeInstance().getNodeInstanceId() == eni.getNodeInstanceId() )
                earFound = true;
            }

            ctx1 = "PID: " +  pid + testCtxt + " New ArrowInstance To Exit Node Added.";
            testAndReport(ctx1, String.valueOf(earFound), "true");
        }
    }

    private
    NodeInstance
    getNodeInstance(ProcessInstance pi, String nodeName, int nodeType)throws Exception
    {
        NodeInstance[] nis = pi.getNodeInstances();
        for(int i=0; i<nis.length; i++)
        {
            NodeInstance ni = nis[i];
            if(ni.getType() == nodeType)
            {
                if(nodeName == null || ni.getName()== nodeName)
                    return ni;
            }

        }
        return null;
    }

    private
    ArrowInstance
    getArrowInstance(ProcessInstance pi, String arrowName )throws Exception
    {
        ArrowInstance[] ais = pi.getArrowInstances();
        for(int i=0; i<ais.length; i++)
        {
            if(arrowName == ais[i].getName())
                return ais[i];

        }

        return null;
    }

    private
    void
    addDataItem(ProcessInstance pi)throws Exception
    {
        DataItem[] ditList = pi.getDataItems();
        pi.addDataItem(finalStringUdaName, DataItem.TYPE_STRING, finalStringUdaValue);
        pi.addDataItem(finalIntUdaName, DataItem.TYPE_INTEGER, finalIntUdaValue);
    }

    private
    void
    testMigration(String[] args)throws Exception
    {
        String Pids = args[1];
        long planId = Long.parseLong(args[2]);
        boolean markAsError = Boolean.valueOf(args[3].trim()).booleanValue();
        StringTokenizer st = new StringTokenizer(Pids, ",");
        int size = st.countTokens();
        long[] piIds = new long[size];
        for(int i =0; i<size; i++)
        {
            String x = st.nextToken();
            System.out.println("CCCC " + x);
            piIds[i] = Long.parseLong(x);
        }

        ProcessMigrationResult pmrl =
                    this.wfSession.migrateProcess(piIds, planId, true);
        printResult(pmrl);
    }

    private
    void
    checkForActiveNode(long[] piIds)
    {
        try {
            for(int i=0; i<piIds.length; i++)
            {
                int count = 0;
                while(count < 100){
                    count++;
                    try
                    {
                        Thread.sleep(waitTime);
                    }
                    catch(Exception e)
                    {
                        //interupted exception.
                    }
                    ProcessInstance pi = WFObjectFactory.getProcessInstance(piIds[i], wfSession);
                    NodeInstance[] tmpNodes = pi.getMemberNodeInstances();
                    for(int j=0; j<tmpNodes.length; j++)
                    {
                        if(tmpNodes[j].getState() == NodeInstance.STATE_RUNNING)
                        {
                            count = 100;
                            break;
                        }

                    }
                }
            }
        }
        catch(Exception e)
        {
            //Ignore this exception as this is for verify.
        }


    }


}
