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

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

import com.togethersoft.openapi.sci.*;

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

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

import com.togethersoft.openapi.util.propertyMap.PropertyMap;
import com.togethersoft.openapi.util.propertyMap.DefaultPropertyMap;

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

/**
 * Default Inheritance pattern
 * @author TogetherSoft
 */
public class Pattern extends PatternBase implements SciPattern {

    public Pattern() {
        setPropertyValue(SciPatternProperty.HIDE_IN_CHOOSE_LIST, EMPTY_STRING);
    }

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

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

        String sourceLanguage = sourceClass.getLanguage();
        String destinationLanguage = destinationClass.getLanguage();
        if (!sourceLanguage.equals(destinationLanguage)) {
          return false;
        }

        boolean isSourceInterface = sourceClass.hasProperty(SciProperty.INTERFACE) || hasInterfaceTag(sourceClass);
        boolean isDestinationInterface = destinationClass.hasProperty(SciProperty.INTERFACE) || hasInterfaceTag(destinationClass);

        // agreement: class is treated as interface also if it has @interface tag in doc-comment
        if (isSourceInterface && !isDestinationInterface) {
            return false;
        }

        // check for cross-inheritance
        if (!isSourceInterface && !isDestinationInterface) {
            if (SciUtil.isInheritor(destinationClass, sourceClass, /*checkDeepInheritance=*/true)) {
                return false;
            }
        }

        SciElement element = getElement();
        if (element != null) {
            if (!(element instanceof SciInheritance)) {
                return false;
            }

            if (!sourceClass.equals(((SciInheritance)element).getInheritor())) {
                return false;
            }

            boolean implementsFlag = sourceClass.hasProperty(SciProperty.INTERFACE) != destinationClass.hasProperty(SciProperty.INTERFACE);
            if (implementsFlag != ((SciInheritance)element).isImplementation()) {
                return false;
            }
        }

        SciInheritance oldInh = (SciInheritance) element;
        SciInheritance newInh = getFactory().newInheritance();
        newInh.setReferencedElement(destinationClass);

        boolean implementsFlag = sourceLanguage.equals(SciLanguage.JAVA) &&
          (sourceClass.hasProperty(SciProperty.INTERFACE) != destinationClass.hasProperty(SciProperty.INTERFACE));
        if (oldInh == null) {
            SciInheritanceList list = implementsFlag ? sourceClass.getImplementsList() : sourceClass.getExtendsList();
            return list.canPaste(newInh, null, false);
        }
        else {
            return oldInh.canReplace(newInh);
        }
    }

    public boolean canApply() {
        SciClass sourceClass = (SciClass) getProperties().getPropertyValue(SciPatternProperty.LINK_SOURCE_CLASS);
        SciClass destinationClass = (SciClass) getProperties().getPropertyValue(SciPatternProperty.LINK_DESTINATION_CLASS);
        SciInheritance oldInh = (SciInheritance) getProperties().getPropertyValue(SciPatternProperty.ELEMENT);

        boolean implementsFlag = sourceClass.hasProperty(SciProperty.INTERFACE) != destinationClass.hasProperty(SciProperty.INTERFACE);
        if (oldInh == null) {
            SciInheritanceList list = implementsFlag ? sourceClass.getImplementsList() : sourceClass.getExtendsList();
            // [VZ] 14.11.00 TO BE CHANGED !!!
            // see SCR# 25142
            if (list == null) {
                return false;
            }
        }

        return true;
    }

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

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

        SciInheritance oldInh = (SciInheritance) getProperties().getPropertyValue(SciPatternProperty.ELEMENT);
        SciInheritance newInh = getFactory().newInheritance();

        newInh.setReferencedElement(destinationClass);

        boolean implementsFlag = sourceClass.hasProperty(SciProperty.INTERFACE) != destinationClass.hasProperty(SciProperty.INTERFACE);
        if (oldInh == null) {
            SciInheritanceList list = implementsFlag ? sourceClass.getImplementsList() : sourceClass.getExtendsList();
            if (list != null) {
                newInh = (SciInheritance) list.paste(newInh, null, false);
            }
        }
        else {
            newInh = (SciInheritance)oldInh.replace(newInh);
        }

        setPropertyValue(SciPatternProperty.RESULT_ELEMENT, newInh);
    }
}
