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

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

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

import com.togethersoft.openapi.sci.*;

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

import com.togethersoft.openapi.sci.enum.SciInheritanceEnumeration;
import com.togethersoft.openapi.sci.enum.SciTagEnumeration;

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

    private static final String PATTERN_NAME = "Chain of Responsibility";

    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(HANDLER_NAME, HANDLER);
            setPropertyValue(CONCRETE_HANDLER, null);
            setPropertyValue(ATTRIBUTE, SUCCESSOR);
            setPropertyValue(EXPORT_DOCUMENTATION, Boolean.FALSE);
            setPropertyValue(CREATE_PATTERN_LINKS, Boolean.TRUE);

            myClassRoleVector.removeAllElements();

            if (getSelectedClass() == null) {
                addPropertyValue(CONCRETE_HANDLER, CONCRETE_HANDLER_NAME);
            }
            else {
                cannotApplyToInterface(getSelectedClass());

                myClassRoleVector.addElement(HANDLER);
                myClassRoleVector.addElement(CONCRETE_HANDLER);

                String  classRole = null;
                if (SciPatternUtil.findExtendedPatternLinkClient(getSelectedClass(), PATTERN_NAME, CONCRETE_HANDLER) == null) {
                    classRole = HANDLER;
                }
                else {
                    classRole = CONCRETE_HANDLER;
                    addPropertyValue(CONCRETE_HANDLER, getSelectedClass().getQualifiedName());
                }
                setPropertyValue(SELECTED_CLASS_ROLE, classRole);
            }

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

    public boolean canApply() {
        try {
            String handlerName = classNameShouldBeCorrect(HANDLER);
            SciClass handlerClass = findClassByName(handlerName);
            cannotBeAnInterface(HANDLER, handlerClass);
            shouldBeWritable(HANDLER, handlerClass);

            if (getLanguage().equals(SciLanguage.JAVA)) {
                Enumeration enumeration = propertiesEnumeration(CONCRETE_HANDLER);
                while (enumeration.hasMoreElements()) {
                    String concreteHandlerName = classNameShouldBeCorrect((Property)enumeration.nextElement());
                    if (concreteHandlerName == null || concreteHandlerName.length() == 0) {
                        continue;
                    }
                    SciClass concreteHandlerClass = findClassByName(concreteHandlerName);
                    if (concreteHandlerClass == null) {
                        continue;
                    }

                    shouldBeWritable(CONCRETE_HANDLER, concreteHandlerClass);
                    cannotBeAnInterface(CONCRETE_HANDLER, concreteHandlerClass);
                    SciInheritanceEnumeration extendsEnum = concreteHandlerClass.getExtendsList().inheritances();
                    if (extendsEnum.hasMoreElements() &&
                        (handlerClass == null || !handlerClass.equals(extendsEnum.nextSciInheritance().getReferencedElement()))) {
                            Object[] arguments = { concreteHandlerName, handlerName };
                            throw new PatternBaseException(createTextByPattern(PATTERN_CANNOT_EXTEND_CLASS, arguments));
                    }
                }
            }

            String attributeName = identifierShouldBeCorrect(ATTRIBUTE);
            checkAttributeName(attributeName, handlerClass, handlerClass);
            return true;
        }
        catch(PatternBaseException e) {
            return false;
        }
    }

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

        String handlerName = getStringPropertyValue(HANDLER);
        SciClass handlerClass = getClassByName(handlerName, false);

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

        Enumeration enumeration = propertiesEnumeration(CONCRETE_HANDLER);
        while (enumeration.hasMoreElements()) {
            String concreteHandlerName = (String)((Property)enumeration.nextElement()).getValue();
            if (concreteHandlerName == null || concreteHandlerName.length() == 0) {
                continue;
            }
            //SciClass concreteHandlerClass = getClassByName(concreteHandlerName, false);
        }

        if (myAllClassesAreNew) {
            SciOperation sampleOperation = getFactory().newOperation();
            sampleOperation.setName("handleRequest");
            SciPatternUtil.makeDelegatedImplementation(sampleOperation, attributeName, true);
            handlerClass.paste(sampleOperation, null, false);
        }

        SciOperation setMethod = SciPatternUtil.createSetMethod(attribute);
        SciOperation getMethod = SciPatternUtil.createGetMethod(attribute);
        enumeration = propertiesEnumeration(CONCRETE_HANDLER);
        while (enumeration.hasMoreElements()) {
            String concreteHandlerName = (String)((Property)enumeration.nextElement()).getValue();
            if (concreteHandlerName == null || concreteHandlerName.length() == 0) {
                continue;
            }
            SciClass concreteHandlerClass = getClassByName(concreteHandlerName, false);
            SciPatternUtil.createInheritance(concreteHandlerClass, handlerClass);
            if (createPatternLinks()) {
                SciPatternUtil.addPatternLink(handlerClass, concreteHandlerClass, PATTERN_NAME, null, CONCRETE_HANDLER, false);
            }

            Enumeration handlerOperations = SciUtil.allMembers(handlerClass, true);
            while (handlerOperations.hasMoreElements()) {
                Object element = handlerOperations.nextElement();
                if (!(element instanceof SciOperation)) {
                    continue;
                }

                SciOperation handlerOperation = (SciOperation)element;
                if (handlerOperation.equals(getMethod) || handlerOperation.equals(setMethod)) {
                    continue;
                }
                if (handlerOperation.hasProperty(SciProperty.CONSTRUCTOR) ||
                    handlerOperation.hasProperty(SciProperty.DESTRUCTOR)) {
                    continue;
                }
                if (SciUtil.findMemberBySignature(concreteHandlerClass, handlerOperation) != null) {
                    continue;
                }

                SciOperation concreteOperation = (SciOperation)concreteHandlerClass.paste(handlerOperation, null, false);
                if (concreteOperation == null) {
                    continue;
                }
                if (!exportDocumentation()) {
                    SciTagEnumeration tags = concreteOperation.getTagList().tags();
                    while (tags.hasMoreElements()) {
                        tags.nextSciTag().setValue(null);
                    }
                }

                if (handlerOperation.hasProperty(SciProperty.ABSTRACT)) {
                    SciPatternUtil.makeStubImplementation(concreteOperation);
                }
                else {
                    String getOperationName = PREFIX_GET + Character.toUpperCase(attributeName.charAt(0)) + attributeName.substring(1) + "()";
                    SciPatternUtil.makeDelegatedImplementation(concreteOperation, getOperationName, true);
                }
            }
        }

        myClassRoleVector.removeAllElements();
        myUIBuilder.removeAllListenedInspectorProperties();
        myUIBuilder = 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);
            }

            // 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 HANDLER property
            boolean allowInterface = false;
            if (myUIBuilder.addClassPropertyToPage(pageName1, HANDLER, HANDLER,
                PatternUIBuilder.PropertyType.SingleValue, allowInterface) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(HANDLER);

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

            // 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 ex) {
            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 handlerClass = null;

                if (role != null) {
                    if (role.equals(HANDLER)) {
                        handlerClass = getSelectedClass();
                    }
                    else if (role.equals(CONCRETE_HANDLER)) {
                        handlerClass = SciPatternUtil.findExtendedPatternLinkClient(getSelectedClass(), PATTERN_NAME, CONCRETE_HANDLER);
                        if (handlerClass == null) {
                            setPropertyValue(CONCRETE_HANDLER, null);
                            addPropertyValue(CONCRETE_HANDLER, getSelectedClass().getQualifiedName());
                        }
                    }
                }

                if (handlerClass != null) {
                    setPropertyValue(HANDLER, handlerClass.getQualifiedName());
                    Enumeration concreteHandlerEnumeration =
                        SciPatternUtil.findPatternLinkSuppliers(handlerClass, PATTERN_NAME, CONCRETE_HANDLER);
                    if (concreteHandlerEnumeration.hasMoreElements()) {
                        setPropertyValue(CONCRETE_HANDLER, null);
                        while (concreteHandlerEnumeration.hasMoreElements()) {
                            addPropertyValue(CONCRETE_HANDLER, ((SciClass)concreteHandlerEnumeration.nextElement()).getQualifiedName());
                        }
                    }
                    SciAttribute attribute = SciPatternUtil.findAttribute(handlerClass, handlerClass);
                    if (attribute != null) {
                        setPropertyValue(ATTRIBUTE, attribute.getName());
                    }
                }
                //getIdeInspector().update();
            }
        });
    }

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

    private static final String HANDLER                 = "Handler";
    private static final String HANDLER_NAME            = "Handler";
    private static final String CONCRETE_HANDLER        = "Concrete handlers";
    private static final String CONCRETE_HANDLER_NAME   = "ConcreteHandler";
    private static final String SUCCESSOR               = "successor";

    // Message patterns
    private static final String PATTERN_CANNOT_EXTEND_CLASS = "{0} cannot extend class {1}";
}
