/*
 * $Id: EmbeddedWorkflowService.java,v 1.5 2008/04/17 10:01:01 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 * 
 * Created on 06.07.2004
 *
 * $Log: EmbeddedWorkflowService.java,v $
 * Revision 1.5  2008/04/17 10:01:01  marbobo
 * + Velocity-Templates angepasst
 *
 * Revision 1.4  2005/09/13 09:51:45  jonyp
 * cleaning the code
 *
 * Revision 1.3  2004/11/10 15:02:53  koppy
 * Projektweite Überarbeitung von Error-Logs, Stacktraces,..
 *
 * Revision 1.2  2004/09/20 10:26:15  koppy
 * +javadoc Dokumentation
 *
 * Revision 1.1  2004/08/30 14:49:58  koppy
 * Initiale Version der Workflow-Komponente
 *
 */
package vkc.workflow.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.enhydra.shark.Shark;
import org.enhydra.shark.api.client.wfbase.BaseException;
import org.enhydra.shark.api.client.wfmodel.CannotAcceptSuspended;
import org.enhydra.shark.api.client.wfmodel.WfActivity;
import org.enhydra.shark.api.client.wfmodel.WfAssignment;
import org.enhydra.shark.api.client.wfmodel.WfProcess;
import org.enhydra.shark.api.client.wfmodel.WfProcessIterator;
import org.enhydra.shark.api.client.wfmodel.WfProcessMgr;
import org.enhydra.shark.api.client.wfservice.AdminMisc;
import org.enhydra.shark.api.client.wfservice.ConnectFailed;
import org.enhydra.shark.api.client.wfservice.ExecutionAdministration;
import org.enhydra.shark.api.client.wfservice.NotConnected;
import org.enhydra.shark.api.client.wfservice.PackageAdministration;
import org.enhydra.shark.api.client.wfservice.SharkConnection;
import org.enhydra.shark.api.client.wfservice.SharkInterface;

import vkc.misc.VKCLogManager;
import vkc.util.StringUtils;
import vkc.view.WebShare;
import vkc.workflow.model.ProcessDefinitionInfo;
import vkc.workflow.model.ProcessInstanceInfo;
import vkc.workflow.model.WorkItemInfo;
import vkc.workflow.util.JaWEUtils;
import vkc.workflow.util.SharkUtils;

/**
 * This class represents the  
 * implementation of the embedded workflow service
 * using shark as library.
 * <p>
 * <b>Note:</b>the implementation is not complete
 * and must be extended if it really should be used.
 * At the moment only the CORBA client implementation is maintained.
 * 
 * @author Andreas Koop
 */
public class EmbeddedWorkflowService /*implements VKCWorkflowService */{

    private static boolean debug      = true;

    private String         engineName = "Shark";

    private SharkInterface shark      = null;

    /**
     * Constructs the embedded workflow service
     * 
     * @param workflowDir
     */
    protected EmbeddedWorkflowService() {

        File workflowDir = new File(WebShare.getWebApplicationDir().getParentFile(), "workflow");
        // TODO Nach der Testphase wieder umstellen
        String wfPath = "D:/work/eclipse3/Shark/output/Shark"; //workflowDir.getAbsolutePath();

        Properties p = new Properties();
        try {
            p.load(new FileInputStream(workflowDir.getAbsolutePath() + "/conf/Shark.conf"));

            if (p.getProperty("enginename") != null)
                engineName = p.getProperty("enginename");

            String realPath = StringUtils.replaceToken(wfPath, "\\", "/");
            for (Iterator it = p.keySet().iterator(); it.hasNext();) {
                String key = (String) it.next();
                String value = p.getProperty(key);
                if (0 <= value.indexOf("@@")) {
                    if (debug)
                        System.err.print("key is " + key + ", old value is " + value);
                    value = StringUtils.replaceToken(value, "@@", realPath);
                    p.setProperty(key, value);
                    if (debug)
                        System.err.println(", new value is " + value);
                }
            }
        } catch (Exception e) {
            VKCLogManager.errorLog(this, "", e.getMessage(), e);
        }

        Shark.configure(p);
        shark = Shark.getInstance();
    }

    public WfMConnection connect(String username, String pwd) throws BaseException, ConnectFailed {

        SharkConnection sConn = shark.getSharkConnection();
        sConn.connect(username, pwd, engineName, null);

        return new WfMConnection(sConn);
    }

    /**
     * (non-Javadoc)
     * 
     * @see vkc.workflow.service.VKCWorkflowService#completeActivity(org.enhydra.shark.api.client.wfservice.SharkConnection,
     *      java.lang.String)
     */
    public void completeActivity(WfMConnection sConn, String activityId) throws BaseException {

        if (activityId == null)
            return;

        try {
            WfAssignment a = getAssignment(sConn.getSharkLIBConnection(), activityId);
            if (!isMine(a))
                acceptAssignment(a);
            a.activity().complete();
        } catch (Exception e) {
            throw new BaseException(e);
        }

    }

    /**
     * (non-Javadoc)
     * 
     * @see vkc.workflow.service.VKCWorkflowService#getAssignment(org.enhydra.shark.api.client.wfservice.SharkConnection,
     *      java.lang.String)
     */
    private WfAssignment getAssignment(SharkConnection sConn, String activityId) throws BaseException {

        try {
            WfAssignment[] ar = sConn.getResourceObject().get_sequence_work_item(0);
            for (int i = 0; i < ar.length; ++i) {
                if (activityId.equals(ar[i].activity().key())) {
                    return ar[i];
                }
            }
            throw new BaseException("Activity:" + activityId + " not found in " + sConn.getResourceObject().resource_key() + "'s worklist");
        } catch (Exception e) {
            VKCLogManager.errorLog(this, "", e.getMessage(), e);
            throw new BaseException(e);
        }
    }

    public List getProcessesToStart(String userName, String pwd) {

        ExecutionAdministration ea = null;
        try {
            ea = shark.getAdminInterface().getExecutionAdministration();
            ea.connect(userName, null, engineName, null);

            WfProcessMgr[] a = ea.get_iterator_processmgr().get_next_n_sequence(0);

            List processes = new ArrayList(a.length);

            for (int i = 0; i < a.length; ++i) {
                WfProcessMgr pMgr = a[i];

                ProcessDefinitionInfo pInfo = new ProcessDefinitionInfo();
                pInfo.setMgrName(pMgr.name());
                pInfo.setPackageId(this.getAdminMisc().getProcessMgrPkgId(pMgr.name()));
                pInfo.setProcessDefId(this.getAdminMisc().getProcessMgrProcDefId(pMgr.name()));
                pInfo.setName(this.getAdminMisc().getProcessMgrProcDefName(pMgr.name()));
                pInfo.setDescription(pMgr.description());

                if (debug) {
                    System.err.println("--------processToStart");
                    System.err.println(pInfo);
                }

                processes.add(pInfo);
            }
            return processes;

        } catch (Exception e) {
            VKCLogManager.errorLog(this, "", e.getMessage(), e);
        } finally {
            try {
                if (ea != null) {
                    ea.disconnect();
                }
            } catch (BaseException e) {
            } catch (NotConnected e) {
            }
        }
        return new ArrayList(0);

    }

    public List getProcesses(String userName, String pwd, String state) {

        ExecutionAdministration ea = null;
        try {
            ea = shark.getAdminInterface().getExecutionAdministration();
            ea.connect(userName, pwd, engineName, null);

            WfProcessMgr[] a = ea.get_iterator_processmgr().get_next_n_sequence(0);

            List processInstances = new ArrayList(a.length);

            for (int i = 0; i < a.length; ++i) {
                WfProcessIterator iter = a[i].get_iterator_process();
                iter.set_query_expression("state.equals(\"" + state + "\")");
                WfProcess[] ps = iter.get_next_n_sequence(0);

                for (int j = 0; j < ps.length; j++) {
                    WfProcess process = ps[j];

                    ProcessInstanceInfo pInfo = new ProcessInstanceInfo(process);
                    processInstances.add(pInfo);

                    if (debug) {
                        System.err.println("--------process");
                        System.err.println(pInfo);
                    }
                }
            }
            return processInstances;

        } catch (Exception e) {
            VKCLogManager.errorLog(this, "", e.getMessage(), e);
        } finally {
            SharkUtils.disconnect(ea);
        }
        return new ArrayList(0);

    }

    public List getWorkItems(WfMConnection sConn) throws BaseException {

        List items = new ArrayList();

        try {
            WfAssignment[] assignments = sConn.getSharkLIBConnection().getResourceObject().get_sequence_work_item(0);

            for (int i = 0; i < assignments.length; i++) {
                WfAssignment assignment = assignments[i];
                if (!isMine(assignment))
                    continue;
                WfActivity activity = assignment.activity();

                WorkItemInfo item = new WorkItemInfo();
                item.setId(activity.key());
                item.setName(activity.name());
                item.setDescription(activity.description());
                item.setPriority(activity.priority());
                item.setState(activity.state());
                item.setReceivedAt(new Date(activity.last_state_time().getTime()));
                item.setContainer(new ProcessInstanceInfo(activity.container()));
                items.add(item);

            }
        } catch (NotConnected e) {
            VKCLogManager.errorLog(this, "", e.getMessage(), e);
            throw new BaseException(e);
        }

        return items;
    }

    public void writeProcessGraph(String userName, String pwd, String procId, OutputStream out) {

        ExecutionAdministration ea = null;
        try {
            ea = shark.getAdminInterface().getExecutionAdministration();
            ea.connect(userName, pwd, engineName, null);

            WfProcess wfProcess = ea.getProcess(procId);
            if (wfProcess == null)
                return;

            JaWEUtils.writeProcessGraphAsJPG(this.getAdminMisc(), this.getPackageAdministration(), wfProcess, out);

        } catch (Exception e) {
            VKCLogManager.errorLog(this, "", e.getMessage(), e);
        } catch (ThreadDeath e) {
            VKCLogManager.errorLog(this, "", e.getMessage(), e);
        } finally {
            SharkUtils.disconnect(ea);
        }

    }

    private void acceptAssignment(WfAssignment assignment) throws BaseException, CannotAcceptSuspended {
        assignment.set_accepted_status(true);
    }

    private boolean isMine(WfAssignment a) throws BaseException {
        return a.get_accepted_status();
    }

    /** 
     * (non-Javadoc)
     * @see vkc.workflow.service.VKCWorkflowService#startProcess(java.lang.String, java.lang.String)
     */
    public void startProcess(String username, String pwd, String pkgId, String pDefId) throws BaseException {

        if (debug) {
            System.err.println(username + " starts process " + pkgId + "#1#" + pDefId);
        }

        SharkConnection sConn = shark.getSharkConnection();
        try {
            sConn.connect(username, null, engineName, null);
            sConn.createProcess(pkgId, pDefId).start();
            sConn.disconnect();

        } catch (Exception e) {
            VKCLogManager.errorLog(this, "", e.getMessage(), e);
            throw new BaseException(e);
        }

    }

    private PackageAdministration getPackageAdministration() {
        return shark.getAdminInterface().getPackageAdministration();
    }

    private AdminMisc getAdminMisc() {
        return shark.getAdminInterface().getAdminMisc();
    }

}
