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

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

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

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

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 Decorator pattern.
 * @author TogetherSoft
 */
public class Pattern extends ClassPatternBase implements SciPattern {

    private static final String PATTERN_NAME = "Decorator";

    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();
            implementedForJavaOnly();

            setPropertyValue(SELECTED_CLASS_ROLE, null);
            setPropertyValue(COMPONENT, COMPONENT);
            setPropertyValue(CONCRETE_COMPONENT, null);
            setPropertyValue(DECORATOR, DECORATOR);
            setPropertyValue(CONCRETE_DECORATOR, null);
            setPropertyValue(ATTRIBUTE, COMPONENT.toLowerCase());
            setPropertyValue(INITIALIZATION_VARIANT, CONSTRUCTOR_PARAMETER);
            setPropertyValue(EXPORT_DOCUMENTATION, Boolean.FALSE);
            setPropertyValue(CREATE_PATTERN_LINKS, Boolean.TRUE);

            myClassRoleVector.removeAllElements();

            if (getSelectedClass() == null) {
                addPropertyValue(CONCRETE_COMPONENT, "ConcreteComponent");
                addPropertyValue(CONCRETE_DECORATOR, "ConcreteDecorator");
            }
            else {
                myClassRoleVector.addElement(COMPONENT);
                myClassRoleVector.addElement(CONCRETE_COMPONENT);
                myClassRoleVector.addElement(DECORATOR);

                if (!getSelectedClass().hasProperty(SciProperty.INTERFACE)) {
                    myClassRoleVector.addElement(CONCRETE_DECORATOR);
                }

                String currentRole = null;
                if (findComponentByDecorator(getSelectedClass()) != null) {
                    currentRole = DECORATOR;
                }
                else if (SciPatternUtil.findExtendedPatternLinkClient(getSelectedClass(), PATTERN_NAME, CONCRETE_COMPONENT) != null) {
                    currentRole = CONCRETE_COMPONENT;
                }
                else if (SciPatternUtil.findExtendedPatternLinkClient(getSelectedClass(), PATTERN_NAME, CONCRETE_DECORATOR) != null) {
                    currentRole = CONCRETE_DECORATOR;
                }
                else {
                    currentRole = COMPONENT;
                }
                setPropertyValue(SELECTED_CLASS_ROLE, currentRole);
            }

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

    public boolean canApply()
    {
        try {
            String componentName = classNameShouldBeCorrect(COMPONENT);
            SciClass componentClass = findClassByName(componentName);

            String decoratorName = classNameShouldBeCorrect(DECORATOR);
            SciClass decoratorClass = findClassByName(decoratorName);
            shouldBeWritable(DECORATOR, decoratorClass);

            String attributeName = identifierShouldBeCorrect(ATTRIBUTE);
            if (componentClass != null) {
                checkAttributeName(attributeName, decoratorClass, componentClass);
            }
            else {
                checkAttributeName(attributeName, decoratorClass, componentName);
            }
            checkConcreteElements(componentClass, CONCRETE_COMPONENT, true);

            Enumeration enumeration = propertiesEnumeration(CONCRETE_DECORATOR);
            while (enumeration.hasMoreElements()) {
                String concreteDecoratorName = classNameShouldBeCorrect((Property)enumeration.nextElement());
                if (concreteDecoratorName == null || concreteDecoratorName.length() == 0) {
                    continue;
                }

                SciClass concreteDecoratorClass = findClassByName(concreteDecoratorName);
                cannotBeAnInterface(CONCRETE_DECORATOR, concreteDecoratorClass);
                shouldBeWritable(CONCRETE_DECORATOR, concreteDecoratorClass);
                if (decoratorClass != null) {
                    shouldExtend(concreteDecoratorClass, decoratorClass);
                }
                else {
                    shouldExtend(concreteDecoratorClass, decoratorName);
                }
            }

            if (componentClass != null) {
                shouldExtend(decoratorClass, componentClass);
            }
            else {
                shouldExtend(decoratorClass, componentName);
            }

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

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

        String attributeName = getStringPropertyValue(ATTRIBUTE);

/* Get or create Component class. */

        String componentName = getStringPropertyValue(COMPONENT);
        SciClass componentClass = getClassByName(componentName, true);

/* Get or create Decorator class. Create inheritance from Component,
   if this inheritance doesn't exist. Create attribute for Component. */

        String decoratorName = getStringPropertyValue(DECORATOR);
        SciClass decoratorClass = getClassByName(decoratorName, false);
        SciPatternUtil.createInheritance(decoratorClass, componentClass);
        SciAttribute attribute = SciPatternUtil.createAttribute(decoratorClass, componentClass, attributeName, true);

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

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

/* Create operations in Decorator class. */

        Enumeration componentOperations = SciUtil.allMembers(componentClass, true);
        while (componentOperations.hasMoreElements()) {
            Object element = componentOperations.nextElement();
            if (!(element instanceof SciOperation)) {
                continue;
            }
            SciOperation componentOperation = (SciOperation)element;
            if (componentOperation.hasProperty(SciProperty.CONSTRUCTOR)) {
                continue;
            }
            if (componentOperation.hasProperty(SciProperty.PRIVATE)) {
                continue;
            }
            SciOperation decoratorOperation = (SciOperation)SciUtil.findMemberBySignature(decoratorClass, componentOperation);
            if (decoratorOperation != null) {
                continue;
            }
            decoratorOperation = (SciOperation)decoratorClass.paste(componentOperation, null, false);
            if (!exportDocumentation()) {
                SciTagEnumeration tags = decoratorOperation.getTagList().tags();
                while (tags.hasMoreElements()) {
                    tags.nextSciTag().setValue(null);
                }
            }
            decoratorOperation.setProperty(SciProperty.ABSTRACT, false);
            SciPatternUtil.makeDelegatedImplementation(decoratorOperation, getStringPropertyValue(ATTRIBUTE), false);
        }

/* Add initialization in Decorator class. */

        String initializationVariant = getStringPropertyValue(INITIALIZATION_VARIANT);
        if (CONSTRUCTOR_PARAMETER.equals(initializationVariant)) {
            SciPatternUtil.createStubConstructors(decoratorClass, componentClass);
            SciPatternUtil.addAttributeToConstructors(attribute);
        }
        else if (SET_METHOD.equals(initializationVariant)) {
            SciPatternUtil.createSetMethod(attribute);
        }

/* Create concrete components. */

        Enumeration enumeration = propertiesEnumeration(CONCRETE_COMPONENT);
        while (enumeration.hasMoreElements()) {
            String concreteComponentName = (String)((Property)enumeration.nextElement()).getValue();
            if (concreteComponentName == null || concreteComponentName.length() == 0) {
                continue;
            }
            SciClass concreteComponentClass = getClassByName(concreteComponentName, false);
            SciPatternUtil.makeStubImplementation(concreteComponentClass, componentClass, false, exportDocumentation());
            if (createPatternLinks()) {
                SciPatternUtil.addPatternLink(componentClass, concreteComponentClass, PATTERN_NAME, null, CONCRETE_COMPONENT, false);
            }
        }

/* Create concrete decorators. */

        enumeration = propertiesEnumeration(CONCRETE_DECORATOR);
        while (enumeration.hasMoreElements()) {
            String concreteDecoratorName = (String)((Property)enumeration.nextElement()).getValue();
            SciClass concreteDecoratorClass = getClassByName(concreteDecoratorName, false);
            SciPatternUtil.makeStubImplementation(concreteDecoratorClass, decoratorClass, true, exportDocumentation());
            if (createPatternLinks()) {
                SciPatternUtil.addPatternLink(decoratorClass, concreteDecoratorClass, PATTERN_NAME, null, CONCRETE_DECORATOR, false);
            }
        }

/* Create pattern link. */

        if (createPatternLinks()) {
            SciPatternUtil.addPatternLink(componentClass, decoratorClass, PATTERN_NAME, null, DECORATOR, false);
        }

        myClassRoleVector.removeAllElements();
        myUIBuilder.removeAllListenedInspectorProperties();
        myUIBuilder = null;
    }

    private SciClass findComponentByDecorator(SciClass decorator)
    {
        if (decorator == null) {
            return null;
        }

        SciInheritanceEnumeration inheritances = decorator.getExtendsList().inheritances();
        while (inheritances.hasMoreElements()) {
            SciClass component = (SciClass)inheritances.nextSciInheritance().getReferencedElement();
            if (component != null) {
                SciAttributeEnumeration attributes = decorator.attributes();
                while (attributes.hasMoreElements()) {
                    if (component.equals(attributes.nextSciAttribute().getType().getReferencedElement())) {
                        return component;
                    }
                }
            }
        }

        if (getLanguage().equals(SciLanguage.JAVA) && !decorator.hasProperty(SciProperty.INTERFACE)) {
            inheritances = decorator.getImplementsList().inheritances();
            while (inheritances.hasMoreElements()) {
                SciClass component = (SciClass)inheritances.nextSciInheritance().getReferencedElement();
                if (component != null) {
                    SciAttributeEnumeration attributes = decorator.attributes();
                    while (attributes.hasMoreElements()) {
                        if (component.equals(attributes.nextSciAttribute().getType().getReferencedElement())) {
                            return component;
                        }
                    }
                }
            }
        }

        return null;
    }

    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);
            }

            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);
            }

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

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

            allowInterface = false;
            if (myUIBuilder.addClassPropertyToPage(pageName1, DECORATOR, DECORATOR,
                PatternUIBuilder.PropertyType.SingleValue, allowInterface) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(DECORATOR);

            allowInterface = false;
            if (myUIBuilder.addClassPropertyToPage(pageName1, CONCRETE_DECORATOR, CONCRETE_DECORATOR,
                PatternUIBuilder.PropertyType.MultiValue, allowInterface) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(CONCRETE_DECORATOR);

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

            // Add INITIALIZATION_VARIANT property
            String[] items = new String[] { NONE, CONSTRUCTOR_PARAMETER, SET_METHOD };
            if (myUIBuilder.addPropertyToPage(pageName1, INITIALIZATION_VARIANT, INITIALIZATION_VARIANT,
                PatternUIBuilder.PropertyType.StringChooser, items) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(INITIALIZATION_VARIANT);

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

            if (myUIBuilder.AddCreatePatternLinksProperty(pageName1) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(CREATE_PATTERN_LINKS);

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

    protected void assignActivityResponse() {
        addPropertyMapListener(SELECTED_CLASS_ROLE, new PropertyMapListener() {
            // class PropertyMapListener
            public void propertiesChanged(PropertyMapEvent event) {
                String role = getStringPropertyValue(SELECTED_CLASS_ROLE);
                SciClass componentClass = null;
                SciClass decoratorClass = null;

                if (role != null) {
                    if (role.equals(COMPONENT)) {
                        componentClass = getSelectedClass();
                        decoratorClass = SciPatternUtil.findPatternLinkSupplier(componentClass, PATTERN_NAME, DECORATOR);
                    }
                    else if (role.equals(DECORATOR)) {
                        decoratorClass = getSelectedClass();
                        componentClass = findComponentByDecorator(decoratorClass);
                    }
                    else if (role.equals(CONCRETE_COMPONENT)) {
                        componentClass = SciPatternUtil.findExtendedPatternLinkClient(getSelectedClass(), PATTERN_NAME, CONCRETE_COMPONENT);
                        if (componentClass == null) {
                            setPropertyValue(CONCRETE_COMPONENT, null);
                            addPropertyValue(CONCRETE_COMPONENT, getSelectedClass().getQualifiedName());
                        }
                        else {
                            decoratorClass = SciPatternUtil.findPatternLinkSupplier(componentClass, PATTERN_NAME, DECORATOR);
                        }
                    }
                    else if (role.equals(CONCRETE_DECORATOR)) {
                        decoratorClass = SciPatternUtil.findExtendedPatternLinkClient(getSelectedClass(), PATTERN_NAME, CONCRETE_DECORATOR);
                        if (decoratorClass == null) {
                            setPropertyValue(CONCRETE_DECORATOR, null);
                            addPropertyValue(CONCRETE_DECORATOR, getSelectedClass().getQualifiedName());
                        }
                        else {
                            componentClass = findComponentByDecorator(decoratorClass);
                        }
                    }
                }

                if (decoratorClass != null) {
                    setPropertyValue(INITIALIZATION_VARIANT, NONE);
                    enableItem(INITIALIZATION_VARIANT, false);
                    setPropertyValue(DECORATOR, decoratorClass.getQualifiedName());
                }
                else {
                    enableItem(INITIALIZATION_VARIANT, true);
                }

                if (componentClass != null) {
                    setPropertyValue(COMPONENT, componentClass.getQualifiedName());
                }

                Enumeration concreteComponentEnumeration = SciPatternUtil.findPatternLinkSuppliers(componentClass, PATTERN_NAME, CONCRETE_COMPONENT);
                if (concreteComponentEnumeration.hasMoreElements()) {
                    setPropertyValue(CONCRETE_COMPONENT, null);
                    while (concreteComponentEnumeration.hasMoreElements()) {
                        addPropertyValue(CONCRETE_COMPONENT, ((SciClass)concreteComponentEnumeration.nextElement()).getQualifiedName());
                    }
                }

                Enumeration concreteDecoratorEnumeration = SciPatternUtil.findPatternLinkSuppliers(decoratorClass, PATTERN_NAME, CONCRETE_DECORATOR);
                if (concreteDecoratorEnumeration.hasMoreElements()) {
                    setPropertyValue(CONCRETE_DECORATOR, null);
                    while (concreteDecoratorEnumeration.hasMoreElements()) {
                        addPropertyValue(CONCRETE_DECORATOR, ((SciClass)concreteDecoratorEnumeration.nextElement()).getQualifiedName());
                    }
                }

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

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

    private static final String DECORATOR           = "Decorator";
    private static final String COMPONENT           = "Component";
    private static final String CONCRETE_DECORATOR  = "Concrete decorators";
    private static final String CONCRETE_COMPONENT  = "Concrete components";
}
