/*-------------------------------------------------------------------------------
Copyright (c)2000 TogetherSoft Corporation. Patents pending. All rights reserved.
-------------------------------------------------------------------------------*/

package com.togethersoft.modules.patterns.UNIVERSAL.CLASS.GoF.State;

import java.util.Enumeration;
import java.util.Vector;

import com.togethersoft.openapi.sci.SciUtil;
import com.togethersoft.openapi.sci.SciClass;
import com.togethersoft.openapi.sci.SciOperation;
import com.togethersoft.openapi.sci.SciAttribute;
import com.togethersoft.openapi.sci.SciProperty;
import com.togethersoft.openapi.sci.SciLanguage;

import com.togethersoft.openapi.sci.enum.SciOperationEnumeration;

import com.togethersoft.openapi.sci.pattern.SciPattern;
import com.togethersoft.openapi.sci.pattern.SciPatternUtil;

import com.togethersoft.openapi.util.propertyMap.Property;
import com.togethersoft.openapi.util.propertyMap.PropertyMapEvent;
import com.togethersoft.openapi.util.propertyMap.PropertyMapListener;

import com.togethersoft.modules.patterns.UNIVERSAL.CLASS.ClassPatternBase;
import com.togethersoft.modules.patterns.UNIVERSAL.PatternBaseException;
import com.togethersoft.modules.patterns.UNIVERSAL.PatternUIBuilder;

/**
 * GoF State pattern.
 * @author TogetherSoft
 */
public class Pattern extends ClassPatternBase implements SciPattern {

    private static final String PATTERN_NAME = "State";

    public Pattern() {
        setPatternDisplayName(PATTERN_NAME);
    }

    protected void finalize() throws Throwable {
        super.finalize();
        if (myUIBuilder != null) {
            myUIBuilder.removeAllListenedInspectorProperties();
            myUIBuilder = null;
        }
        myClassRoleVector.removeAllElements();
        myClassRoleVector = null;
    }

    public boolean prepare() {
        try {
            super.prepare();
            notImplementedForIDL();

            setPropertyValue(SELECTED_CLASS_ROLE, null);
            setPropertyValue(CONTEXT, CONTEXT);
            setPropertyValue(STATE, STATE);
            setPropertyValue(CONCRETE_STATE, null);
            setPropertyValue(ATTRIBUTE, STATE.toLowerCase());
            setPropertyValue(EXPORT_DOCUMENTATION, Boolean.FALSE);
            setPropertyValue(CREATE_PATTERN_LINKS, Boolean.TRUE);

            myClassRoleVector.removeAllElements();

            if (getSelectedClass() == null) {
                addPropertyValue(CONCRETE_STATE, "ConcreteState");
            }
            else {
                myClassRoleVector.addElement(STATE);
                myClassRoleVector.addElement(CONCRETE_STATE);

                if (!getSelectedClass().hasProperty(SciProperty.INTERFACE)) {
                    myClassRoleVector.insertElementAt(CONTEXT, 0);
                }

                String  classRole = null;
                if (SciPatternUtil.findExtendedPatternLinkClient(getSelectedClass(), PATTERN_NAME, CONCRETE_STATE) != null) {
                    classRole = CONCRETE_STATE;
                }
                else if (SciPatternUtil.findAssociatedPatternLinkClient(getSelectedClass(), PATTERN_NAME, CONTEXT) != null) {
                    classRole = CONTEXT;
                }
                else {
                    classRole = STATE;
                }
                setPropertyValue(SELECTED_CLASS_ROLE, classRole);
            }

            initializeInspector();
            assignActivityResponse();
            return true;
        }
        catch(PatternBaseException e) {
            return false;
        }
    }

    public boolean canApply() {
        try {
            String contextName = classNameShouldBeCorrect(CONTEXT);
            SciClass contextClass = findClassByName(contextName);
            cannotBeAnInterface(CONTEXT, contextClass);
            shouldBeWritable(CONTEXT, contextClass);

            String stateName = classNameShouldBeCorrect(STATE);
            SciClass stateClass = findClassByName(stateName);
            shouldBeWritable(STATE, stateClass);
            checkConcreteElements(stateClass, CONCRETE_STATE, true);

            String attributeName = identifierShouldBeCorrect(ATTRIBUTE);
            if (stateClass != null) {
                checkAttributeName(attributeName, contextClass, stateClass);
            }
            else {
                checkAttributeName(attributeName, contextClass, stateName);
            }

            return true;
        }
        catch(PatternBaseException e) {
            return false;
        }
    }

    public void apply() {
        if (!canApply()) {
            return;
        }
        super.apply();

        // remove interface objects
        myClassRoleVector.removeAllElements();
        myUIBuilder.removeAllListenedInspectorProperties();
        myUIBuilder = null;

    /* Get or create Context class. */

        String contextName = getStringPropertyValue(CONTEXT);
        SciClass contextClass = getClassByName(contextName, false);

    /* Get or create State class. */

        String stateName = getStringPropertyValue(STATE);
        SciClass stateClass = getClassByName(stateName, true);

        if (createPatternLinks()) {
            SciPatternUtil.addPatternLink(stateClass, contextClass, PATTERN_NAME, null, CONTEXT, false);
        }

    /* Create state attribute. */

        String attributeName = getStringPropertyValue(ATTRIBUTE);
        SciAttribute attribute = SciPatternUtil.createAttribute(contextClass, stateClass, attributeName, true);
        attribute.setType( getLanguageHelper().makePointerType(attribute.getType()) );

        String concreteStateName = null;
        SciClass concreteStateClass = null;
        Enumeration concreteStates = propertiesEnumeration(CONCRETE_STATE);
        while (concreteStates.hasMoreElements()) {
            concreteStateName = (String) ((Property)concreteStates.nextElement()).getValue();
            if (concreteStateName == null || concreteStateName.length() == 0) {
                continue;
            }

            concreteStateClass = getClassByName(concreteStateName, false);
            SciPatternUtil.makeStubImplementation(concreteStateClass, stateClass, false, exportDocumentation());

            if (createPatternLinks()) {
                SciPatternUtil.addPatternLink(stateClass, concreteStateClass, PATTERN_NAME, null, CONCRETE_STATE, false);
            }
        }


    /* Create sample operation, if all classes are new. */

        if (myAllClassesAreNew) {
            SciOperation sampleOperation = getFactory().newOperation();
            sampleOperation.setName(SAMPLE_OPERATION);
            contextClass.paste(sampleOperation, null, false);
        }

        SciOperation setMethod = SciPatternUtil.createSetMethod(attribute);
        SciOperationEnumeration contextOperations = contextClass.operations();
        while (contextOperations.hasMoreElements()) {
            SciOperation contextOperation = contextOperations.nextSciOperation();

            // skip constructors/destructors
            if (contextOperation.hasProperty(SciProperty.CONSTRUCTOR) ||
                contextOperation.hasProperty(SciProperty.DESTRUCTOR)) {
                continue;
            }

            if (SciPatternUtil.isOperator(contextOperation)) {
                continue;
            }

            if (contextOperation.equals(setMethod)) {
                continue;
            }

            if (SciUtil.findMemberBySignature(stateClass, contextOperation) != null) {
                continue;
            }

            Enumeration concreteStateNames = propertiesEnumeration(CONCRETE_STATE);
            while (concreteStateNames.hasMoreElements()) {
                concreteStateName = (String)((Property)concreteStateNames.nextElement()).getValue();
                if (concreteStateName == null || concreteStateName.length() == 0) {
                    continue;
                }

                concreteStateClass = getClassByName(concreteStateName, false);
                if (SciUtil.findMemberBySignature(concreteStateClass, contextOperation) == null) {
                    SciOperation concreteStateOperation = (SciOperation) concreteStateClass.paste(contextOperation, null, false);
                    concreteStateOperation.setProperty(SciProperty.PUBLIC, true);
                }
            }

            if (SciUtil.findMemberBySignature(stateClass, contextOperation) == null) {
                SciOperation stateOperation = (SciOperation) stateClass.paste(contextOperation, null, false);
                stateOperation.setProperty(SciProperty.PUBLIC, true);
                stateOperation.setProperty(SciProperty.VIRTUAL, true);
            }

            SciPatternUtil.makeDelegatedImplementation(contextOperation, attributeName, false);
        }
    }

    protected void initializeInspector() {
        try {
            myUIBuilder = null;
            myUIBuilder = createUIBuilder(true);

            Vector inspectorPropertiesVector = new Vector();

            // Create page
            String pageName1 = "Pattern properties";
            if (myUIBuilder.addInspectorPage(pageName1, PatternUIBuilder.UIPresentation.Table) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            // Add SELECTED_CLASS_ROLE property
            if (getStringPropertyValue(SELECTED_CLASS_ROLE) != null) {
                if (myUIBuilder.addPropertyToPage(pageName1,
                    SELECTED_CLASS_ROLE, SELECTED_CLASS_ROLE,
                    PatternUIBuilder.PropertyType.StringChooser, myClassRoleVector.toArray()) == null) {
                    throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
                }
                inspectorPropertiesVector.addElement(SELECTED_CLASS_ROLE);
            }

            // Add CONTEXT property
            boolean allowInterface = true;
            if (myUIBuilder.addClassPropertyToPage(pageName1, CONTEXT, CONTEXT,
                PatternUIBuilder.PropertyType.SingleValue, allowInterface) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(CONTEXT);

            // Add STATE property
            allowInterface = true;
            if (myUIBuilder.addClassPropertyToPage(pageName1, STATE, STATE,
                PatternUIBuilder.PropertyType.SingleValue, allowInterface) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(STATE);

            // Add CONCRETE_STATE property
            allowInterface = true;
            if (myUIBuilder.addClassPropertyToPage(pageName1, CONCRETE_STATE, CONCRETE_STATE,
                PatternUIBuilder.PropertyType.MultiValue, true) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(CONCRETE_STATE);

            // Add ATTRIBUTE property
            if (myUIBuilder.addPropertyToPage(pageName1, ATTRIBUTE, ATTRIBUTE,
                PatternUIBuilder.PropertyType.String) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(ATTRIBUTE);

            // Add EXPORT_DOCUMENTATION property
            if (myUIBuilder.AddExportDocumentationProperty(pageName1) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(EXPORT_DOCUMENTATION);

            // Add "Create pattern links" property
            if (myUIBuilder.AddCreatePatternLinksProperty(pageName1) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(CREATE_PATTERN_LINKS);

            myUIBuilder.setCreatedPatternUI();
            myUIBuilder.assignListenedInspectorProperties(inspectorPropertiesVector);
        }
        catch (PatternBaseException e) {
            myUIBuilder = null;
            getIdeInspector().clear();
            return;
        }
    }

    protected void assignActivityResponse() {
        addPropertyMapListener(SELECTED_CLASS_ROLE, new PropertyMapListener() {
            public void propertiesChanged(PropertyMapEvent event) {
                String role = getStringPropertyValue(SELECTED_CLASS_ROLE);
                SciClass stateClass = null;
                SciClass contextClass = null;

                if (role != null) {
                    if (role.equals(STATE)) {
                        stateClass = getSelectedClass();
                        contextClass = SciPatternUtil.findPatternLinkSupplier(stateClass, PATTERN_NAME, CONTEXT);
                    }
                    else if (role.equals(CONTEXT)) {
                        contextClass = getSelectedClass();
                        stateClass = SciPatternUtil.findAssociatedPatternLinkClient(contextClass, PATTERN_NAME, CONTEXT);
                    }
                    else if (role.equals(CONCRETE_STATE)) {
                        stateClass = SciPatternUtil.findExtendedPatternLinkClient(getSelectedClass(), PATTERN_NAME, CONCRETE_STATE);
                        if (stateClass == null) {
                            setPropertyValue(CONCRETE_STATE, null);
                            addPropertyValue(CONCRETE_STATE, getSelectedClass().getQualifiedName());
                        }
                        else {
                            contextClass = SciPatternUtil.findPatternLinkSupplier(stateClass, PATTERN_NAME, CONTEXT);
                        }
                    }
                }

                if (stateClass != null) {
                    setPropertyValue(STATE, stateClass.getQualifiedName());
                }
                if (contextClass != null) {
                    setPropertyValue(CONTEXT, contextClass.getQualifiedName());
                }

                Enumeration concreteStateEnumeration = SciPatternUtil.findPatternLinkSuppliers(stateClass, PATTERN_NAME, CONCRETE_STATE);
                if (concreteStateEnumeration.hasMoreElements()) {
                    setPropertyValue(CONCRETE_STATE, null);
                    while (concreteStateEnumeration.hasMoreElements()) {
                        addPropertyValue(CONCRETE_STATE, ((SciClass)concreteStateEnumeration.nextElement()).getQualifiedName());
                    }
                }

                SciAttribute attribute = SciPatternUtil.findAttribute(contextClass, stateClass);
                if (attribute != null) {
                    setPropertyValue(ATTRIBUTE, attribute.getName());
                }

                getIdeInspector().update();
            }
        });
    }

    private PatternUIBuilder    myUIBuilder = null;
    private Vector              myClassRoleVector = new Vector();

    private static final String STATE           = "State";
    private static final String CONCRETE_STATE  = "Concrete states";
    private static final String CONTEXT         = "Context";
    private static final String SAMPLE_OPERATION = "sampleOperation";
}
