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

package com.togethersoft.modules.patterns.UNIVERSAL.LINK.Stub_Implementation;

import java.util.Enumeration;

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

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

import com.togethersoft.openapi.ide.message.IdeMessageType;
import com.togethersoft.openapi.ide.message.IdeMessageManagerAccess;

import com.togethersoft.modules.patterns.UNIVERSAL.PatternBase;
import com.togethersoft.modules.patterns.UNIVERSAL.PatternBaseException;
import com.togethersoft.modules.patterns.UNIVERSAL.PatternUIBuilder;

/**
 * Link Stub implementation pattern
 * @author TogetherSoft
 */
public class Pattern extends PatternBase implements SciPattern {

    private static final String PATTERN_NAME = "Stub Implementation";

    public Pattern() {
        setPatternDisplayName(PATTERN_NAME);
    }

    protected void finalize() throws Throwable {
        super.finalize();
        myUIBuilder = null;
    }

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

        setPropertyValue(EXPORT_DOCUMENTATION, Boolean.FALSE);

        SciClass sourceClass = (SciClass) getPropertyValue(SciPatternProperty.LINK_SOURCE_CLASS);
        SciClass destinationClass = (SciClass) getPropertyValue(SciPatternProperty.LINK_DESTINATION_CLASS);
        if (sourceClass == null || destinationClass == null) {
            return false;
        }
        if (sourceClass.hasProperty(SciProperty.INTERFACE)) {
            return false;
        }

        SciElement sciElement = getElement();
        if (sciElement != null) {
            if (!(sciElement instanceof SciInheritance)) {
                return false;
            }
            if (!sourceClass.equals(((SciInheritance)sciElement).getInheritor())) {
                return false;
            }
            boolean implementsFlag = destinationClass.hasProperty(SciProperty.INTERFACE);
            if (implementsFlag != ((SciInheritance)sciElement).isImplementation()) {
                return false;
            }
        }

        SciInheritance oldInh = (SciInheritance)sciElement;
        SciInheritance inh = getFactory().newInheritance();
        inh.setReferencedElement(destinationClass);
        boolean implementsFlag = sourceClass.hasProperty(SciProperty.INTERFACE) != destinationClass.hasProperty(SciProperty.INTERFACE);
        boolean success;
        if (oldInh == null) {
            SciInheritanceList list = implementsFlag ? sourceClass.getImplementsList() : sourceClass.getExtendsList();
            success = list.canPaste(inh, null, false);
        }
        else {
            success = oldInh.canReplace(inh);
        }

        if (success) {
            initializeInspector();
        }
        return success;
    }

    public boolean canApply() {
        return true;
    }

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

        SciClass sourceClass = (SciClass) getPropertyValue(SciPatternProperty.LINK_SOURCE_CLASS);
        SciClass destinationClass = (SciClass) getPropertyValue(SciPatternProperty.LINK_DESTINATION_CLASS);

        SciInheritance oldInh = (SciInheritance) getElement();
        SciInheritance inh = getFactory().newInheritance();
        inh.setReferencedElement(destinationClass);

        boolean isSourceInterface = sourceClass.hasProperty(SciProperty.INTERFACE);
        boolean isDestinationInterface = destinationClass.hasProperty(SciProperty.INTERFACE);
        boolean implementsFlag = isSourceInterface != isDestinationInterface;

        if (oldInh == null) {
            SciInheritanceList list = implementsFlag ? sourceClass.getImplementsList() : sourceClass.getExtendsList();
            inh = (SciInheritance)list.paste(inh, null, false);
        }
        else {
            inh = (SciInheritance)oldInh.replace(inh);
        }

        SciClass baseClass = null;
        if (implementsFlag) {
            SciInheritanceEnumeration extendsEnum = sourceClass.getExtendsList().inheritances();
            if (extendsEnum.hasMoreElements()) {
                baseClass = (SciClass)extendsEnum.nextSciInheritance().getReferencedElement();
            }
        }
        // set result
        setPropertyValue(SciPatternProperty.RESULT_ELEMENT, inh);

        Enumeration allMembers = SciUtil.allMembers(destinationClass, true);
        while (allMembers.hasMoreElements()) {
            SciMember destMember = (SciMember) allMembers.nextElement();
            if (!(destMember instanceof SciOperation)) {
                continue;
            }
            if (!destMember.hasProperty(SciProperty.ABSTRACT)) {
                continue;
            }

            SciOperation implOp = (SciOperation) SciUtil.findMemberBySignature(sourceClass, destMember);
            if (implOp != null) {
                // Change access modifiers of an existed operation
                if (isDestinationInterface) {
                    implOp.setProperty(SciProperty.PUBLIC, true);
                }
                else {
                    int accessModifier = SciProperty.PUBLIC;
                    if (destMember.hasProperty(SciProperty.PUBLIC)) {
                        accessModifier = SciProperty.PUBLIC;
                    }
                    else if (destMember.hasProperty(SciProperty.PROTECTED)) {
                        accessModifier = SciProperty.PROTECTED;
                    }
                    else if (destMember.hasProperty(SciProperty.PRIVATE)) {
                        accessModifier = SciProperty.PRIVATE;
                    }

                    implOp.setProperty(accessModifier, true);

                    // destMember.hasProperty(SciProperty.FINAL) - ???
                    if (implOp.hasProperty(SciProperty.ABSTRACT)) {
                        implOp.setProperty(SciProperty.ABSTRACT, false);
                    }
                    // C++ only !
                    if (implOp.hasProperty(SciProperty.VIRTUAL)) {
                        if (!destMember.hasProperty(SciProperty.VIRTUAL)) {
                            implOp.setProperty(SciProperty.VIRTUAL, false);
                        }
                    }
                }

                SciPatternUtil.makeStubImplementation(implOp);
            }
            else {
                // Create new operation
                if (baseClass != null) {
                    SciOperation baseOp = (SciOperation) SciUtil.findMemberBySignature(baseClass, destMember, true);
                    if (baseOp != null && !baseOp.hasProperty(SciProperty.ABSTRACT)) {
                        continue;
                    }
                }

                implOp = (SciOperation)sourceClass.paste(destMember, null, false);
                if (!exportDocumentation()) {
                    SciTagEnumeration tags = implOp.getTagList().tags();
                    while (tags.hasMoreElements()) {
                        tags.nextSciTag().setValue(null);
                    }
                }

                SciPatternUtil.makeStubImplementation(implOp);
            }
        }

        if (!implementsFlag && !destinationClass.hasProperty(SciProperty.INTERFACE)) {
            SciOperationEnumeration operations = destinationClass.operations();
            while (operations.hasMoreElements()) {
                SciOperation anOperation = (SciOperation)operations.nextElement();

                if (SciUtil.findMemberBySignature(sourceClass, anOperation) != null) {
                    continue;
                }

                if (!anOperation.hasProperty(SciProperty.CONSTRUCTOR) &&
                    !anOperation.hasProperty(SciProperty.DESTRUCTOR)) {
                    continue;
                }

                if (anOperation.hasProperty(SciProperty.PRIVATE) ||
                    anOperation.hasProperty(SciProperty.PROTECTED)) {
                    continue;
                }

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

                try {
                    String body = "\nsuper(";
                    SciParameterEnumeration parameters = anOperation.getParameterList().parameters();
                    while (parameters.hasMoreElements()) {
                        body += parameters.nextSciParameter().getName();
                        if (parameters.hasMoreElements()) {
                            body += ", ";
                        }
                    }
                    body += ");";
                    anOperation.setBody(getGenericFactory().newCodeBlock(body));
                }
                catch(SciGenericFactoryException e) {
                    IdeMessageManagerAccess.printMessage(IdeMessageType.ERROR, CODE_GENERATION_ERROR);
                }
            }
        }
        myUIBuilder = null;
    }

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

            // Create page
            String pageName1 = "Pattern properties";
            if (myUIBuilder.addInspectorPage(pageName1, PatternUIBuilder.UIPresentation.Table) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }

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

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

    private PatternUIBuilder myUIBuilder = null;
}
