/*
 * $Id: CORBAWorklistManager.java,v 1.5 2004/10/08 14:01:26 koppy Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 * 
 * Created on 13.07.2004
 *
 * $Log: CORBAWorklistManager.java,v $
 * Revision 1.5  2004/10/08 14:01:26  koppy
 * bugfixes
 *
 * Revision 1.4  2004/10/07 12:40:58  koppy
 * Bugfix: Aktualisierung von Booleanvariablen
 *
 * Revision 1.3  2004/09/20 10:26:11  koppy
 * +javadoc Dokumentation
 *
 * Revision 1.2  2004/09/01 15:26:48  koppy
 * bugfix beim Setzen von Context-Variablen
 *
 * Revision 1.1  2004/08/30 14:49:55  koppy
 * Initiale Version der Workflow-Komponente
 *
 */
package vkc.workflow.service.corba;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.enhydra.shark.api.client.wfbase.BaseException;
import org.enhydra.shark.corbaclient.WorkflowUtilities;
import org.omg.CORBA.Any;

import vkc.misc.VKCLogManager;
import vkc.workflow.WorkflowException;
import vkc.workflow.model.ProcessInstanceInfo;
import vkc.workflow.model.VariableInfo;
import vkc.workflow.model.WorkItemInfo;
import vkc.workflow.service.CORBAWorkflowService;
import vkc.workflow.service.WfMConnection;
import vkc.workflow.service.manager.WorklistManager;
import vkc.workflow.util.SharkUtils;
import WfBase.NameValue;
import WorkflowModel.CannotAcceptSuspended;
import WorkflowModel.InvalidResource;
import WorkflowModel.WfActivity;
import WorkflowModel.WfAssignment;
import WorkflowModel.WfResource;
import WorkflowService.AdminMisc;
import WorkflowService.ExecutionAdministration;
import WorkflowService.NotConnected;
import WorkflowService.SharkConnection;

/**
 * This class represents the CORBA client version
 * of the worklist manager.
 * 
 * @author Andreas Koop
 */
public class CORBAWorklistManager implements WorklistManager {

    private CORBAWorkflowService wfService = null;

    public CORBAWorklistManager(CORBAWorkflowService wfService) {
        this.wfService = wfService;
    }

    private List getWorkItems(WfMConnection sConn, boolean mine)
            throws BaseException {

        List items = new ArrayList();

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

            for (int i = 0; i < assignments.length; i++) {
                WfAssignment assignment = assignments[i];
                if (isMine(assignment) == mine)
                    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().time));
                item.setContainer(new ProcessInstanceInfo(activity.container()));
                items.add(item);
                
            }
        } catch (WorkflowService.NotConnected e) {
            throw new BaseException(e);
        } catch (WfBase.BaseException e) {
            throw new BaseException(e);
        }

        return items;
    }

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

    private WfAssignment getAssignment(SharkConnection sConn, String activityId) 
    	throws WfBase.BaseException, NotConnected, WorkflowException {

            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];
                }
            }
            
            VKCLogManager.infoLog( this, "Activity:" + activityId + " not found in "
                    + sConn.getResourceObject().resource_key() + "'s worklist" );
            throw new WorkflowException( "wf.error.notFoundInWorklist" );
        
    }

    /**
     * (non-Javadoc)
     * 
     * @see vkc.workflow.service.manager.WorklistManager#completeActivity(vkc.workflow.service.WfMConnection,
     *      java.lang.String)
     */
    public void completeActivity(WfMConnection sConn, String activityId)
            throws BaseException {
        if (activityId == null)
            return;

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

    }

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

    /**
     * (non-Javadoc)
     * 
     * @see vkc.workflow.service.manager.WorklistManager#getAcceptedWorkItems(vkc.workflow.service.WfMConnection)
     */
    public List getAcceptedWorkItems(WfMConnection sConn) throws BaseException {

        return this.getWorkItems(sConn, false);
    }

    /**
     * (non-Javadoc)
     * 
     * @see vkc.workflow.service.manager.WorklistManager#getOpenWorkItems(vkc.workflow.service.WfMConnection)
     */
    public List getOpenWorkItems(WfMConnection sConn) throws BaseException {

        return this.getWorkItems(sConn, true);
    }

    /** 
     * (non-Javadoc)
     * @see vkc.workflow.service.manager.WorklistManager#acceptActivity(vkc.workflow.service.WfMConnection, java.lang.String)
     */
    public void acceptActivity(WfMConnection conn, String activityId) 
    	throws WorkflowException {
        try {
            acceptAssignment( 
                    getAssignment(conn.getSharkCORBAConnection(), activityId));
        } catch (Exception e) {
            
            if(e instanceof WorkflowException)
                throw (WorkflowException)e;
            
            throw new WorkflowException( "wf.error.general", e);
        }
    }


    /** 
     * (non-Javadoc)
     * @see vkc.workflow.service.manager.WorklistManager#getContextVariables(vkc.workflow.service.WfMConnection, java.util.Locale, java.lang.String)
     */
    public List getContextVariables(WfMConnection sConn, Locale locale, String activityId) throws WorkflowException {
        
        AdminMisc am = wfService.getShark().getAdminInterface().getAdminMisc();
        List l = new ArrayList();
        try {
            am.connect( sConn.getUsername(), sConn.getPassword(), sConn.getEngine(), "");
            
            WfActivity activity = this.getAssignment( 
                    	sConn.getSharkCORBAConnection(), activityId).activity();
            
            NameValue [] actContext = this.getActivityContext( am,
                    activity.process_context(),
                    activity,WorkflowUtilities.VARIABLE_TO_PROCESS_ALL);
            
            if(actContext == null || actContext.length == 0)
                return l;
            
            NameValue[] ronlyCon = 
                this.getActivityContext(am,
                        actContext,activity,WorkflowUtilities.VARIABLE_TO_PROCESS_VIEW);
            List readOnlyContext = new ArrayList();
            readOnlyContext.addAll(Arrays.asList(ronlyCon));
            
            for (int i = 0; i < actContext.length; i++) {
                NameValue nameValue = actContext[i];
                String procId = activity.container().key();
                String varId = nameValue.the_name;
                Any varVal= nameValue.the_value;
                
                String varType = this.getVarType( varVal, procId, varId, am); 
                    
                VariableInfo varInfo = new VariableInfo(locale);
                varInfo.setId(varId);
                varInfo.setName( am.getVariableName(procId,varId));
                varInfo.setTypeKey(varType);
                varInfo.setDescription(am.getVariableDescription(procId,varId));
                varInfo.setReadOnly( readOnlyContext.contains( nameValue ));
                
                if (varType.equals(WorkflowUtilities.UNKNOWN_KEY)) {
                    varInfo.setValue( varVal.toString() );
                    
                }else if (varType.equals(WorkflowUtilities.ENUMERATION_KEY)) {
                    varInfo.setValue( WorkflowUtilities.
                            extractChoicesFromDynEnum(varVal) );
                }else{
                    varInfo.setValue(WorkflowUtilities.
                            exstractValueOfAnyObject(nameValue.the_value,varType).toString());
                }

                
                l.add( varInfo );
            }
        } catch (Exception e) {
            if(e instanceof WorkflowException)
                throw (WorkflowException)e;
            
            throw new WorkflowException( "wf.error.general", e);
        } finally{
            SharkUtils.disconnect(am);
        }
        return l;
    }

    /**
     * @param varVal
     * @param procId
     * @param varId
     * @return
     * @throws NotConnected
     * @throws WfBase.BaseException
     */
    protected String getVarType(Any varVal, String procId, String varId, AdminMisc am)
    	throws WfBase.BaseException, NotConnected {
        
        String varType = WorkflowUtilities.UNKNOWN_KEY;
        
        if ( varVal != null ) {
           varType = WorkflowUtilities.getTypeKeyOfAnyObject(varVal);
        }
        if ( varType==null || varType.equals(WorkflowUtilities.UNKNOWN_KEY)) {
           varType=WorkflowUtilities.getTypeKeyOfAnyObject(am.getVariableJavaClassName(procId,varId));
        }
        return varType;
    }

    /** 
     * (non-Javadoc)
     * @see vkc.workflow.service.manager.WorklistManager#updateVariables(vkc.workflow.service.WfMConnection, java.lang.String, java.util.Map)
     */
    public void updateVariables(WfMConnection sConn, String activityId, Map vars) throws BaseException {
        
        if(vars == null)
            return;
        
        AdminMisc am = wfService.getShark().getAdminInterface().getAdminMisc();
        
        try {
            am.connect( sConn.getUsername(), sConn.getPassword(), sConn.getEngine(), "");
            
            WfAssignment ass = this.getAssignment( sConn.getSharkCORBAConnection(), activityId);
            WfActivity activity = ass.activity();
            if (!isMine(ass))
                throw new Exception("I don't own activity "+ activityId);
            
            NameValue [] nm = this.getActivityContext( am,
                    activity.process_context(),
                    activity,WorkflowUtilities.VARIABLE_TO_PROCESS_UPDATE);
            
            
            for(int i = 0;i < nm.length;i++){
                NameValue nameValue = nm[i];
                
                Object newValue = vars.get(nameValue.the_name);
                
                String varType = this.getVarType( nameValue.the_value,
                        activity.container().key(),
                        nameValue.the_name, am);
                
                if(newValue == null){
                    
                    if( !WorkflowUtilities.BOOLEAN_KEY.equals(varType))
                        continue;
                    // Boolean, ist nicht in varMap enthalten, also auf false setzen
                    newValue = new String("false");
                }
                
                Any a=WorkflowUtilities.insertValueIntoAnyObject(
                       nameValue.the_value, newValue.toString(), varType);
                if (a!=null) {
                   nameValue.the_value=a;
                }
                 
            }
            activity.set_result(nm);
            
        } catch (Exception e) {
            throw new BaseException(e);
        }finally{
            SharkUtils.disconnect(am);
        }
    }
    
    /**
     * Gets the context for the given activity that could be updated, viewed
     * by the user, or all activity context.
     * The context is determined based on activities extended attributes.
     */
    private NameValue[] getActivityContext (AdminMisc adminMisc,NameValue[] contextToSearch,
    WfActivity act,String type) {
       NameValue[] extAttribs=null;
       try {
          extAttribs=adminMisc.getActivitiesExtendedAttributeNameValuePairs(act.container().key(),act.key());
       } catch (Exception ex) {}
       List nvl=new ArrayList();
       if (extAttribs!=null && extAttribs.length>0) {
          for (int i=0; i<extAttribs.length; i++) {
             String eaName=extAttribs[i].the_name;
             if (type.equals(WorkflowUtilities.VARIABLE_TO_PROCESS_ALL)) {
                if (eaName.equalsIgnoreCase(WorkflowUtilities.VARIABLE_TO_PROCESS_UPDATE) ||
                eaName.equalsIgnoreCase(WorkflowUtilities.VARIABLE_TO_PROCESS_VIEW)) {
                   String variableId=extAttribs[i].the_value.extract_wstring();
                   NameValue nv=WorkflowUtilities.getNameValue(contextToSearch,variableId);
                   if (nv!=null) {
                      nvl.add(nv);
                   }
                }
             } else {
                if (eaName.equalsIgnoreCase(type)) {
                   String variableId=extAttribs[i].the_value.extract_wstring();
                   NameValue nv=WorkflowUtilities.getNameValue(contextToSearch,variableId);
                   if (nv!=null) {
                      nvl.add(nv);
                   }
                }
             }
          }
       }
       NameValue[] updateContext=new NameValue[nvl.size()];
       nvl.toArray(updateContext);

       return updateContext;
    }

    /** 
     * (non-Javadoc)
     * @see vkc.workflow.service.manager.WorklistManager#reassign(vkc.workflow.service.WfMConnection, java.lang.String, java.lang.String)
     */
    public void reassign(WfMConnection conn, String activityId, String newAssigneeName) throws WorkflowException {
        
        ExecutionAdministration ea = wfService.getShark().getAdminInterface()
                .getExecutionAdministration();
        try {
            
            ea.connect(conn.getUsername(), conn.getPassword(), wfService.getEngine(), "");
            
            WfAssignment assignment = getAssignment(conn.getSharkCORBAConnection(), activityId);
            if(assignment.assignee().resource_key().equals(newAssigneeName))
                throw new InvalidResource( "Such assignment exists already!");
            
            
            WfResource newAssignee = ea.getResource( newAssigneeName );
            assignment.set_assignee(newAssignee); 
            
        } catch (NotConnected e) {
            throw new WorkflowException( "wf.error.notConnected", e);
        } catch (InvalidResource e) {
            throw new WorkflowException("wf.error.invalidResource", e);
        } catch (Exception e) {
            if( e instanceof WorkflowException)
                throw (WorkflowException)e;
            
            throw new WorkflowException("wf.error.general", e);
        } finally {

            SharkUtils.disconnect(ea);
        }
        
        
    }
}