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

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

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

import com.togethersoft.openapi.sci.*;

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

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

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

    private static final String PATTERN_NAME = "Proxy";

    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(PROXY, PROXY);
            setPropertyValue(SUBJECT, SUBJECT);
            setPropertyValue(REAL_SUBJECT, "RealSubject");
            setPropertyValue(ATTRIBUTE, "realSubject");
            setPropertyValue(INITIALIZATION_VARIANT, NONE);
            setPropertyValue(EXPORT_DOCUMENTATION, Boolean.FALSE);
            setPropertyValue(CREATE_PATTERN_LINKS, Boolean.TRUE);

            myClassRoleVector.removeAllElements();

            if (getSelectedClass() != null) {
                myClassRoleVector.addElement(SUBJECT);
                myClassRoleVector.addElement(REAL_SUBJECT);

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

                String  classRole = null;
                if (SciPatternUtil.findExtendedPatternLinkClient(getSelectedClass(), PATTERN_NAME, PROXY) != null) {
                    classRole = PROXY;
                }
                else if (SciPatternUtil.findExtendedPatternLinkClient(getSelectedClass(), PATTERN_NAME, REAL_SUBJECT) != null) {
                    classRole = REAL_SUBJECT;
                }
                else {
                    classRole = SUBJECT;
                }
                setPropertyValue(SELECTED_CLASS_ROLE, classRole);
            }

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

    public boolean canApply() {
        try {
            String proxyName = classNameShouldBeCorrect(PROXY);
            SciClass proxyClass = findClassByName(proxyName);
            shouldBeWritable(PROXY, proxyClass);
            cannotBeAnInterface(PROXY, proxyClass);

            String subjectName = classNameShouldBeCorrect(SUBJECT);
            SciClass subjectClass = findClassByName(subjectName);
            shouldBeWritable(SUBJECT, subjectClass);

            String realSubjectName = classNameShouldBeCorrect(REAL_SUBJECT);
            SciClass realSubjectClass = findClassByName(realSubjectName);
            shouldBeWritable(REAL_SUBJECT, realSubjectClass);

            if (subjectClass != null) {
                shouldExtend(realSubjectClass, subjectClass);
                shouldExtend(proxyClass, subjectClass);
            }

            String attributeName = identifierShouldBeCorrect(ATTRIBUTE);
            if (realSubjectClass != null) {
                checkAttributeName(attributeName, proxyClass, realSubjectClass);
            }
            else {
                checkAttributeName(attributeName, proxyClass, realSubjectName);
            }

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

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

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

    /* Get or create Proxy class. */

        String proxyName = getStringPropertyValue(PROXY);
        SciClass proxyClass = getClassByName(proxyName, false);

    /* Get or create Subject class. */

        String subjectName = getStringPropertyValue(SUBJECT);
        SciClass subjectClass = getClassByName(subjectName, true);
        if (createPatternLinks()) {
            SciPatternUtil.addPatternLink(subjectClass, proxyClass, PATTERN_NAME, null, PROXY, false);
        }

    /* Get or create Real subject class. */

        String realSubjectName = getStringPropertyValue(REAL_SUBJECT);
        SciClass realSubjectClass = getClassByName(realSubjectName, false);
        if (createPatternLinks()) {
            SciPatternUtil.addPatternLink(subjectClass, realSubjectClass, PATTERN_NAME, null, REAL_SUBJECT, false);
        }

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

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

        if (!SciUtil.isInheritor(realSubjectClass, subjectClass, true)) {
            SciPatternUtil.makeStubInterface(subjectClass, realSubjectClass, exportDocumentation());
        }

        SciPatternUtil.createInheritance(proxyClass, subjectClass);
        String attributeName = getStringPropertyValue(ATTRIBUTE);

        Enumeration subjectOperations = SciUtil.allMembers(subjectClass, true);
        while (subjectOperations.hasMoreElements()) {
            Object element = subjectOperations.nextElement();
            if (!(element instanceof SciOperation)) {
                continue;
            }
            SciOperation subjectOperation = (SciOperation)element;
            if (!subjectOperation.hasProperty(SciProperty.ABSTRACT)) {
                continue;
            }

            SciOperation proxyOperation = (SciOperation)SciUtil.findMemberBySignature(proxyClass, subjectOperation, true);
            if (proxyOperation != null && !proxyOperation.hasProperty(SciProperty.ABSTRACT)) {
                continue;
            }

            proxyOperation = (SciOperation)proxyClass.paste(subjectOperation, null, false);
            if (!exportDocumentation()) {
                SciTagEnumeration tags = proxyOperation.getTagList().tags();
                while (tags.hasMoreElements()) {
                    tags.nextSciTag().setValue(null);
                }
            }

            if (SciUtil.findMemberBySignature(realSubjectClass, subjectOperation, true) == null) {
                SciPatternUtil.makeStubImplementation(proxyOperation);
            }
            else {
                SciPatternUtil.makeDelegatedImplementation(proxyOperation, attributeName, false);
            }
        }

        SciAttribute attribute = SciPatternUtil.createAttribute(proxyClass, realSubjectClass, attributeName, false);
        attribute.setType( getLanguageHelper().makePointerType(attribute.getType()) );

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

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

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

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

            // Add ATTRIBUTE property
            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);

            // 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 proxyClass = null;
                SciClass subjectClass = null;
                SciClass realSubjectClass = null;

                if (role != null) {
                    if (role.equals(PROXY)) {
                        proxyClass = getSelectedClass();
                        subjectClass = SciPatternUtil.findExtendedPatternLinkClient(proxyClass, PATTERN_NAME, PROXY);
                        realSubjectClass = SciPatternUtil.findPatternLinkSupplier(subjectClass, PATTERN_NAME, REAL_SUBJECT);
                    }
                    else if (role.equals(SUBJECT)) {
                        subjectClass = getSelectedClass();
                        proxyClass = SciPatternUtil.findPatternLinkSupplier(subjectClass, PATTERN_NAME, PROXY);
                        realSubjectClass = SciPatternUtil.findPatternLinkSupplier(subjectClass, PATTERN_NAME, REAL_SUBJECT);
                    }
                    else if (role.equals(REAL_SUBJECT)) {
                        realSubjectClass = getSelectedClass();
                        subjectClass = SciPatternUtil.findExtendedPatternLinkClient(realSubjectClass, PATTERN_NAME, REAL_SUBJECT);
                        proxyClass = SciPatternUtil.findPatternLinkSupplier(subjectClass, PATTERN_NAME, PROXY);
                    }
                }

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

                if (subjectClass != null) {
                    setPropertyValue(SUBJECT, subjectClass.getQualifiedName());
                }
                if (realSubjectClass != null) {
                    setPropertyValue(REAL_SUBJECT, realSubjectClass.getQualifiedName());
                }
                SciAttribute attribute = SciPatternUtil.findAttribute(proxyClass, realSubjectClass);
                if (attribute != null) {
                    setPropertyValue(ATTRIBUTE, attribute.getName());
                }

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

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

    private static final String PROXY           = "Proxy";
    private static final String SUBJECT         = "Subject";
    private static final String REAL_SUBJECT    = "Real subject";
    private static final String SAMPLE_OPERATION = "sampleOperation";
}
