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

package com.togethersoft.modules.patterns.UNIVERSAL;

import java.util.Vector;
import javax.swing.DefaultComboBoxModel;
import javax.swing.MutableComboBoxModel;
import java.beans.PropertyEditor;

import com.togethersoft.openapi.sci.pattern.SciPatternProperty;
import com.togethersoft.openapi.sci.pattern.InspectorPatternUI;

import com.togethersoft.openapi.util.propertyMap.PropertyMapListener;
import com.togethersoft.openapi.util.propertyMap.PropertyMapEvent;

import com.togethersoft.openapi.ide.util.UIComponentService;

import com.togethersoft.openapi.ide.inspector.IdeInspector;
import com.togethersoft.openapi.ide.inspector.IdeInspectorProperty;
import com.togethersoft.openapi.ide.inspector.IdeInspectorComponent;
import com.togethersoft.openapi.ide.inspector.IdeInspectorPropertySetComponent;

import com.togethersoft.openapi.ide.inspector.util.table.PropertyTableComponentUI;
import com.togethersoft.openapi.ide.inspector.util.table.PropertyLineSetComponentUI;

import com.togethersoft.openapi.ide.inspector.util.property.PropertyMapInspectorProperty;
import com.togethersoft.openapi.ide.inspector.util.property.MultiplePropertyMapInspectorProperty;
import com.togethersoft.openapi.ide.inspector.util.property.MultipleDirectPropertyMapInspectorProperty;

import com.togethersoft.openapi.ide.window.IdeSelectorDialogType;
// Property editors
import com.togethersoft.openapi.ide.inspector.util.editors.SwingComboBoxEditor;
import com.togethersoft.openapi.ide.inspector.util.editors.ExtendedFileNameEditor;
import com.togethersoft.openapi.ide.inspector.util.editors.PropertyMapMultiValueElementChooserEditor;
import com.togethersoft.openapi.ide.inspector.util.editors.PropertyMapMultiValueEditor;
import com.togethersoft.openapi.ide.inspector.util.editors.SingleElementChooserEditor;

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

/**
 * Class <code>PatternUIBuilder</code> is an auxiliary class used in the patterns
 * to create User Interface in the Choose Pattern Panel. This class stores constants
 * that define the types of visible UI properties and creates a data set for the 
 * <code>IdeInspector</code> to be displayed on the screen. 
 *
 * @author TogetherSoft
 */
public class PatternUIBuilder
{
    /**
     * Defines the type of the user interface. UI can be a Table of two columns,  
     * one containing the property name,  and the other containing edit area; or
     * a LineSet,  that displays property name in a separate line and has screenwide 
     * edit area underneath. 
     */
    public interface UIPresentation {
        int Table           = 0;
        int LineSet         = 1;
        int Default         = Table;
    }
    /**
     * Defines possible types of user interface properties (comboboxes, edit areas,  multi edit etc). 
     */
    public interface PropertyType {
        int String          = 0;
        int Boolean         = 1;
        int StringChooser   = 2;
        int MultiValue      = 3;
        int SingleValue     = 4;
        int FileName        = 5;
        int ComboBox        = 6;
        int MultiString     = 7;
        int Default         = String;
    }

    /**
     * Constructs an instance of <code>PatternUIBuilder</code> with the specified pattern. 
     *
     * @param pattern <code>PatternBase</code> object 
     * @param clear <code>boolean</code> flag specifies if the inspector should be cleared
     */
    public PatternUIBuilder(PatternBase pattern, boolean clear) {
        myPattern = pattern;
        if (clear) {
          clearInspector();
        }
    }

    /**
     * Constructs a default instance of <code>PatternUIBuilder</code> with the specified pattern. 
     *
     * @param pattern <code>PatternBase</code> object 
     */
    public PatternUIBuilder(PatternBase pattern) {
        myPattern = pattern;
        clearInspector();
    }

    protected void finalize() throws Throwable {
        myPattern = null;
    }

    /**
     * <code>PatternBase</code> object
     */
    public final PatternBase getPattern() {
        return myPattern;
    }
    /**
     * Gets the reference to the <code>IdeInspector</code>. <br>
     * The builder does not store the reference to the <code>IdeInspector</code> 
     * and requests this reference from the pattern. 
     *
     * @return <code>IdeInspector</code> 
     */
    public final IdeInspector getIdeInspector() {
        return getPattern().getIdeInspector();
    }

    public final void clearInspector() {
        getIdeInspector().clear();
    }

 
    public void setCreatedPatternUI() {
        setCreatedPatternUI(null);
    }

    /** 
     * Writes data to the property map. <code>IdeInspector</code> expects to find property in 
     * the specified pattern. 
     * 
     * @param patternUI <code>InspectorPatternUI</code> value to be written for the pre-defined PATTERN_UI property
     */

    public void setCreatedPatternUI(InspectorPatternUI patternUI) {
        if(getPattern() != null) {
            getPattern().setPropertyValue(SciPatternProperty.PATTERN_UI,
                (patternUI == null) ? new InspectorPatternUI(getIdeInspector()) : patternUI);
        }
    }


   /*
    * Adds a new page with the specified name to the inspector. <br>
    * UI may be multi page. If UI of a pattern has more than one page, then the buttons "prev" and "next" 
    * are enabled. This method defines a mechanism for creating additional pages. 
    *
    * @param pageName  <code>String</code> internal name of the page. The page can be requested from the inspector by this name
    * @param pagePresentation <code>int</code> constant from <code>UIpresentation()</code>
    * @return <code>IdeInspectorPropertySetComponent</code> new page
    */
    public IdeInspectorPropertySetComponent addInspectorPage(String pageName, int pagePresentation) {
        return addInspectorPage(pageName, pageName, pagePresentation);
    }


   /*
    * Adds a new page with the specified name to the inspector. <br>
    * UI may be multi page. If UI of a pattern has more than one page, then the buttons "prev" and "next" 
    * are enabled. This method defines a mechanism for creating additional pages. 
    *
    * @param pageName  <code>String</code> internal name of the page. The page can be requested from the inspector by this name
    * @param pageDisplayName <code>String</code> name of the page visible to a user. This name is displayed in the page header. 
    * @param pagePresentation <code>int</code> constant from <code>UIpresentation()</code>
    * @return <code>IdeInspectorPropertySetComponent</code> new page
    */

    public IdeInspectorPropertySetComponent addInspectorPage(String pageName, String pageDisplayName, int pagePresentation) {
        IdeInspectorPropertySetComponent propertyPage = new IdeInspectorPropertySetComponent(null);
        propertyPage.setName(pageName);
        propertyPage.setDisplayName(pageDisplayName);
        getPattern().setCurrentPageTitle(pageDisplayName);

        if (pagePresentation == UIPresentation.Table) {
            propertyPage.setService(UIComponentService.class, new PropertyTableComponentUI(propertyPage));
        }
        else if (pagePresentation == UIPresentation.LineSet) {
            propertyPage.setService(UIComponentService.class, new PropertyLineSetComponentUI(propertyPage));
        }
        else {
            propertyPage.setService(UIComponentService.class, new PropertyTableComponentUI(propertyPage));
        }
        getIdeInspector().addComponent(propertyPage, null);
        return propertyPage;
    }

    /**
     * Adds specified control to the specified page. <br>
     * When a page is created,  we begin to populate it with the specified properties (controls). 
     *
     * @param pageName 	 <code>String</code> internal name of the page,  where the specified prop is inserted
     * @param propertyKey <code>String</code> key of the prop in the proprty map to be inserted
     * @param  propertyName <code>String</code> to be displayed at a control element (property) as it's name
     * @param  propertyType <code>int</code> constant defined above (combobox,  multi edit, edit etc)
     * @return <code>IdeInspectorProperty</code> added to the page
     */
   
 public IdeInspectorProperty addPropertyToPage(String pageName, String propertyKey, String propertyName, int propertyType) {
        return addPropertyToPage(pageName, propertyKey, propertyName, propertyType, null);
    }

    /**
     * Adds specified control to the specified page. <br>
     * When a page is created,  we begin to populate it with the specified properties (controls). 
     *
     * @param pageName 	 <code>String</code> internal name of the page,  where the specified prop is inserted
     * @param propertyKey <code>String</code> key of the prop in the proprty map to be inserted
     * @param  propertyName <code>String</code> to be displayed at a control element (property) as it's name
     * @param  propertyType <code>int</code> constant defined above (combobox,  multi edit, edit etc)
     * @param items <code>Objects[]</code> array required to initialize some properties. This array contains references
     * 		    to some initialization objects. The type of initialization depends upon the type of this property. 
     * @return <code>IdeInspectorProperty</code> created property,  or
     *	       <code>null</code>,  if wrong property type specified, or failed to create property
     */

    public IdeInspectorProperty addPropertyToPage(String pageName, String propertyKey, String propertyName, int propertyType, Object[] items) {
        if (pageName == null || pageName.length() == 0) {
          return null;
        }
        if (propertyKey == null || propertyKey.length() == 0) {
          return null;
        }

        IdeInspectorProperty property = null;

        if (propertyType == PropertyType.Boolean) {
            property = createBooleanProperty(propertyKey);
        }
        else if (propertyType == PropertyType.String) {
            property = createStringProperty(propertyKey);
        }
        else if (propertyType == PropertyType.StringChooser) {
            if (items == null) {
              return null;
            }
            property = createComboBoxProperty(propertyKey, items, false);
        }
        else if (propertyType == PropertyType.FileName) {
            property = createFileNameProperty(propertyKey);
        }
        else if (propertyType == PropertyType.ComboBox) {
            if (items == null) {
              return null;
            }
            property = createComboBoxProperty(propertyKey, items, true);
        }
        else if (propertyType == PropertyType.MultiString) {
            property = createMultiValueStringProperty(propertyKey);
        }
        else {
            return null;
        }

        IdeInspectorPropertySetComponent propertyPage = (IdeInspectorPropertySetComponent) getIdeInspector().findComponent(pageName);
        if (propertyPage == null) {
            return null;
        }
        property.setName(propertyName);
        propertyPage.addProperty(property, /*Condition=*/null);
        return property;
    }
    /**
     * Adds a <code>CREATE_PATTERN_LINKS</code> property to the specified page.  <br>
     * There are some pre-defined properties. For these properties the keys are pre-defined and special methods
     * are used to create these particular properties (CREATE_PATTERN_LINKS and EXPORT_DOCUMENTATION). 
     * 
     * @param pageName <code>String</code> internal name of the page where this property is added
     * @return <code>IdeInspectorProperty</code> object added to this page
     */
    public IdeInspectorProperty AddCreatePatternLinksProperty(String pageName) {
        return addPropertyToPage(pageName, getPattern().CREATE_PATTERN_LINKS, getPattern().CREATE_PATTERN_LINKS,
                PatternUIBuilder.PropertyType.Boolean);
    }

    /**
     * Adds an <code>EXPORT_DOCUMENTATION</code> property to the specified page.  <br>
     * There are some pre-defined properties. For these properties the keys are pre-defined and special methods
     * are used to create these particular properties (CREATE_PATTERN_LINKS and EXPORT_DOCUMENTATION). 
     * 
     * @param pageName <code>String</code> internal name of the page where this property is added
     * @return <code>IdeInspectorProperty</code> object added to this page
     */
    public IdeInspectorProperty AddExportDocumentationProperty(String pageName) {
        return addPropertyToPage(pageName, getPattern().EXPORT_DOCUMENTATION, getPattern().EXPORT_DOCUMENTATION,
                PatternUIBuilder.PropertyType.Boolean);
    }

   /**
    * Adds specified class property to the specified page. <br>
    * Some of the properties have specific controls and behaviour. These are so-called class properties, 
    * used when we want to select classes from a list. This method creates controls for class properties. 
    *
    * @param pageName <code>String</code> name of the page where the control is added
    * @param propertyKey <code>String</code> key of the property to be added
    * @param propertyName <code>String</code> name of the property to be added
    * @param multiple <code>int</code>	toggles between multi and single value that can be selected from the class chooser or entered manually
    * @param allowInterface <code>boolean</code> flag that defines if only classes,  or classes and interfaces are allowed in the class chooser
    * @return <code>IdeInspectorProperty</code> created or <code>null</code> 
    */
    public IdeInspectorProperty addClassPropertyToPage(String pageName, String propertyKey, String propertyName,
            int multiple, boolean allowInterface) {
        IdeInspectorPropertySetComponent propertyPage = (IdeInspectorPropertySetComponent) getIdeInspector().findComponent(pageName);
        if (propertyPage == null) {
            return null;
        }

        IdeInspectorProperty property = null;
        if (multiple == PropertyType.MultiValue) {
            property = createMultiValueClassProperty(propertyKey, allowInterface);
        }
        else if (multiple == PropertyType.SingleValue) {
            property = createSingleValueClassProperty(propertyKey, allowInterface);
        }
        else {
        }

        if (property == null)
            return null;

        property.setName(propertyName);
        propertyPage.addProperty(property, /*Condition=*/null);
        return property;
    }



   /**
    * Allows to enter or select from the class chooser interfaces only. <br>
    *
    * @param pageName <code>String</code> name of the page where the control is added
    * @param propertyKey <code>String</code> key of the property to be added
    * @param propertyName <code>String</code> name of the property to be added
    * @param multiple <code>int</code>	toggles between multi and single value that can be selected from the class chooser or entered manually
    * @return <code>IdeInspectorProperty</code> created or <code>null</code> 
    */
    public IdeInspectorProperty addInterfacePropertyToPage(String pageName, String propertyKey, String propertyName, int multiple) {
        IdeInspectorPropertySetComponent propertyPage = (IdeInspectorPropertySetComponent) getIdeInspector().findComponent(pageName);
        if (propertyPage == null) {
            return null;
        }

        IdeInspectorProperty property = null;
        if (multiple == PropertyType.MultiValue) {
            property = createMultiValueInterfaceProperty(propertyKey);
        }
        else if (multiple == PropertyType.SingleValue) {
            property = createSingleValueInterfaceProperty(propertyKey);
        }
        else {
        }

        if (property == null)
            return null;

        property.setName(propertyName);
        propertyPage.addProperty(property, /*Condition=*/null);
        return property;
    }

   /**
    * Assigns listener for the specified properties. All these properties are listened
    * and if any changes occur, a user-defined method is called that responds to the changes. 
    *
    * @param propertiesVector <code>Vector</code> that stores string keys to the properties
    */
    public void assignListenedInspectorProperties(Vector propertiesVector) {
        getPattern().assignListenedInspectorProperties(propertiesVector);
    }

    /**
     * Closes all listeners when patterns terminates. 
     */
    public void removeAllListenedInspectorProperties() {
        getPattern().removeAllListenedInspectorProperties();
    }

    /**
     * Populates combobox (as one of the possible controls) with the specified array of values. 
     *
     * @param propertyKey <code>String</code> key of the property where the selected value is stored
     * @param items <code>Object[]</code> array of possible values of a combobox
     * @return <code>false</code> if failed to populate the combobox
     */
    public boolean setComboBoxItems(String propertyKey, Object[] items) {
        String currentItem = getPattern().getStringPropertyValue(propertyKey);

        IdeInspectorProperty inspectorProperty = getPattern().findIdeInspectorProperty(propertyKey);
        if (inspectorProperty == null) {
            return false;
        }
        PropertyEditor editor = inspectorProperty.getPropertyEditor();
        if (!(editor instanceof SwingComboBoxEditor)) {
            return false;
        }

        MutableComboBoxModel model = (MutableComboBoxModel) ((SwingComboBoxEditor)editor).getModel();
        // empty and refill model with new items
        while (model.getSize() > 0) {
            model.removeElementAt(0);
        }
        for (int i=0; i < items.length; i++) {
            model.addElement(items[i]);
        }
        model.setSelectedItem(currentItem);
        return true;
    }

    /**
     * Populates combobox (as one of the possible controls) with the specified vector of values. 
     *
     * @param propertyKey <code>String</code> key of the property where the selected value is stored
     * @param itemVector <code>Vector</code> of possible values of a combobox
     * @return <code>false</code> if failed to populate the combobox
     */
    public boolean setComboBoxItems(String propertyKey, Vector itemVector) {
        if (itemVector == null)
            return false;
        else
            return setComboBoxItems(propertyKey, itemVector.toArray());
    }


    protected IdeInspectorProperty createBooleanProperty(String propertyKey) {
        try {
            return new PropertyMapInspectorProperty(getPattern().getProperties(), propertyKey, Boolean.class);
        }
        catch(Exception ex) {
            return null;
        }
    }

    protected IdeInspectorProperty createStringProperty(String propertyKey) {
        try {
            return new PropertyMapInspectorProperty(getPattern().getProperties(), propertyKey, String.class);
        }
        catch(Exception ex) {
            return null;
        }
    }

    protected IdeInspectorProperty createFileNameProperty(String propertyKey) {
        IdeInspectorProperty property = null;
        property = new PropertyMapInspectorProperty(getPattern().getProperties(), propertyKey, String.class);
        property.setPropertyEditor(new ExtendedFileNameEditor());
        return property;
    }

    protected IdeInspectorProperty createComboBoxProperty(String propertyKey, Object[] items, boolean editable) {
        SwingComboBoxEditor editor = new SwingComboBoxEditor(new DefaultComboBoxModel(items));
        editor.setValueEditable(editable);

        IdeInspectorProperty property = new PropertyMapInspectorProperty(getPattern().getProperties(),
            propertyKey, String.class);
        property.setPropertyEditor(editor);
        return property;
    }

    protected IdeInspectorProperty createMultiValueStringProperty(String propertyKey) {
        IdeInspectorProperty property = null;
        property = new MultipleDirectPropertyMapInspectorProperty(getPattern().getProperties(), propertyKey);
        property.setPropertyEditor(new PropertyMapMultiValueEditor(propertyKey));
        return property;
    }

    protected IdeInspectorProperty createMultiValueClassProperty(String propertyKey, boolean allowInterface) {
        int selectorType = allowInterface ?
            IdeSelectorDialogType.MULTIPLE_CLASSES_AND_INTERFACES : IdeSelectorDialogType.MULTIPLE_CLASSES;

        IdeInspectorProperty property = null;
        property = new MultipleDirectPropertyMapInspectorProperty(getPattern().getProperties(), propertyKey);
        property.setPropertyEditor(new PropertyMapMultiValueElementChooserEditor(null, propertyKey, selectorType));
        return property;
    }

    protected IdeInspectorProperty createMultiValueInterfaceProperty(String propertyKey) {
        int selectorType = IdeSelectorDialogType.MULTIPLE_INTERFACES;

        IdeInspectorProperty property = null;
        property = new MultipleDirectPropertyMapInspectorProperty(getPattern().getProperties(), propertyKey);
        property.setPropertyEditor(new PropertyMapMultiValueElementChooserEditor(null, propertyKey, selectorType));
        return property;
    }

    protected IdeInspectorProperty createSingleValueClassProperty(String propertyKey, boolean allowInterface) {
        int selectorType = allowInterface ?
            IdeSelectorDialogType.CLASSES_AND_INTERFACES : IdeSelectorDialogType.CLASSES;

        IdeInspectorProperty property = null;
        property = new PropertyMapInspectorProperty(getPattern().getProperties(), propertyKey, String.class);
        property.setPropertyEditor(new SingleElementChooserEditor(null, selectorType));
        return property;
    }

    protected IdeInspectorProperty createSingleValueInterfaceProperty(String propertyKey) {
        int selectorType = IdeSelectorDialogType.INTERFACES;

        IdeInspectorProperty property = null;
        property = new PropertyMapInspectorProperty(getPattern().getProperties(), propertyKey, String.class);
        property.setPropertyEditor(new SingleElementChooserEditor(null, selectorType));
        return property;
    }

    protected PatternBase myPattern = null;
}
