/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/09/08, 16:02
!  AUTHOR(S): KOGA, Junichiro
!  File : InputInterfaceTableColumns.java
!  
!  Contact address :  Phase System Consortium
!                     E-mail: phase_system@nims.go.jp URL https://azuma.nims.go.jp
!
!
!   Since 2002, this program set had been intensively developed as a part of the following 
!  national projects supported by the Ministry of Education, Culture, Sports, Science and
!  Technology (MEXT) of Japan; "Frontier Simulation Software for Industrial Science
!  (FSIS)" from 2002 to 2005, "Revolutionary Simulation Software (RSS21)" from 2006 to
!  2008. "Research and Development of Innovative Simulation Software (RISS)" from 2008
!  to 2013. These projects is lead by the Center for Research on Innovative Simulation 
!  Software (CISS), the Institute of Industrial Science (IIS), the University of Tokyo.
!   Since 2013, this program set has been further developed centering on PHASE System
!  Consortium. 
!   The activity of development of this program set has been supervised by Takahisa Ohno.
!
!=======================================================================
 */
package ciss.phase_viewer.inputinterface.inputinterfacetable;

import java.lang.reflect.Constructor;
import java.text.DecimalFormat;

import org.apache.log4j.Logger;
import org.jdom.Element;

/**
 * encapsulates column information for an InputInterfaceTable object.
 * 
 * @author KOGA, Junichiro
 */
public class InputInterfaceTableColumns {
    private Logger logger = Logger.getLogger(InputInterfaceTableColumns.class
            .getName());

    /**
     * specify that the corresponding celleditor is a subclass of jtextfield.
     */
    public static String TEXT_FIELD = "textfield";

    /**
     * specify that the corresponding celleditor is a subclass of jcombobox.
     */
    public static String COMBO_BOX = "combobox";

    /**
     * specify that the corresponding celleditor is a subclass of jcheckbox.
     */
    public static String CHECK_BOX = "checkbox";

    private String name;
    private boolean first_letter_is_capital = false;
    private String input_specification;
    private boolean ignore_on_save = false;
    private String cell_editor;
    private String[] choices = new String[] { "" };
    private String defaultValue;
    private String toolTipText;
    private boolean hideable = false;
    private boolean has_apply_to_all = false;
    private boolean is_editable = true;

    private InputInterfaceTableSpec parent;

    private ComboBoxChoices choi;

    private boolean format = false;
    private String formatter = "0.000000";
    private DecimalFormat decimalFormat;

    private String defaultEditor;

    /**
     * Creates a new instance of InputInterfaceTableColumns.
     * 
     * @param element
     *            Element object holding table column information.
     */
    protected InputInterfaceTableColumns(Element element,
            InputInterfaceTableSpec parent) {
        this.name = element.getChildTextTrim("name");
        this.input_specification = this.name;
        this.parent = parent;
        parse(element);
    }

    /**
     * returns the InputInterfaceTableSpec Object to which the instance of this
     * class belongs.
     * 
     * @return the InputInterfaceTableSpecObject to which this class is
     *         associated.
     */
    public InputInterfaceTableSpec getTableSpec() {
        return this.parent;
    }

    /**
     * returns the name of this table column element.
     * 
     * @return name of this table column.
     */
    public String getName() {
        return this.name;
    }

    /**
     * sometimes, the name of a column does not match the string for the
     * input-file specifications. in such a case, a different string is used,
     * which is returned by this method. most of the time, this is equivalent to
     * 'getName()'.
     * 
     * @return the input-file specification string.
     */
    public String getInputSpecifications() {
        return this.input_specification;
    }

    /**
     * returns the cell editor you want to use for this column. will always
     * return one of the following: TEXT_FIELD, COMBO_BOX, or CHECK_BOX.
     * 
     * @return the type of cell editor used to edit the entries of this column.
     */
    public String getCellEditor() {
        return this.cell_editor;
    }

    /**
     * returns the default value that the editor should show.
     * 
     * @return the default value for the editor component
     */
    public String getDefaultValue() {
        if (defaultValue == null) {
            return "";
        }
        return this.defaultValue;
    }

    /**
     * the selections for combo boxes. Meaningfull only when
     * getCellEditor().equals(COMBO_BOX).
     * 
     * @return combo box choices.
     */
    public String[] getChoices() {
        if (choi != null) {
            choices = (String[]) choi.getChoices();
            for (int i = 0; i < choices.length; i++) {
                logger.debug(i + " th choice: " + choices[i]);
            }
        }
        return this.choices;
    }

    /**
     * returns the tooltip text associated with the 'ComboBoxChoice' object.
     * 
     * @return a tooltip text.
     */
    public String getToolTipAssociatedWithChoice(int index) {
        if (choi == null) {
            return "";
        }
        return choi.getToolTipTexts(index);
    }

    /**
     * returns the tool tip text for the associated column.
     * 
     * @return tool tip text for the column represented by this class.
     */
    public String getToolTip() {
        return this.toolTipText;
    }

    /**
     * all values are stored in small caps, but some look better when their
     * first letter is expressed in capitals (for example, element names). in
     * such a case, this method will return true.
     * 
     * @return true if the first letter should be capitalized.
     */
    public boolean isFirstLetterCaps() {
        return this.first_letter_is_capital;
    }

    /**
     * some columns are not as important as others, and we may want to hide it
     * from the data table at startup. this is possible if this method returns
     * true.
     * 
     * @return true if the column element represented by this class is hideable.
     */
    public boolean isHideable() {
        return this.hideable;
    }

    /**
     * for some attributes, such as the 'mobile' parameter in phase, it is
     * preferable that all entries may be modified at once. a check box is
     * deployed in such a case when this method returns true.
     * 
     * @return will create an 'apply-to-all' check box.
     */
    public boolean hasApplytoAll() {
        return this.has_apply_to_all;
    }

    /**
     * some attributes are murely tags used within this program only. these
     * attributes should not be editable, and this method will return false for
     * such attributes.
     * 
     * @return false if the corresponding attribute should not be editable.
     */
    public boolean isEditable() {
        return this.is_editable;
    }

    /**
     * some attributes should not be saved to the corresponding input file. this
     * method will return true for such attributes.
     * 
     * @return true if the corresponding attribute should be ignored on save.
     */
    public boolean ignoreOnSave() {
        return this.ignore_on_save;
    }

    /**
     * specifies whether to format numerical data or not.
     * 
     * @return true if you want to format numerical data.
     */
    public boolean format() {
        return format;
    }

    /**
     * externally set the editor for this column.
     * 
     * @param cellEditor
     *            the cell editor to which this column is associated.
     */
    public void setCellEditor(String cellEditor) {
        cell_editor = cellEditor;
    }

    /**
     * returns the formatter for numerical data.
     * 
     * @return the formatter for numerical data.
     */
    public DecimalFormat getFormatter() {
        return decimalFormat;
    }

    private void parse(Element element) {
        Element tmp = element.getChild("cell_editor");
        parseCellEditor(tmp);

        input_specification = parseString("input_specification", element,
                input_specification);
        toolTipText = parseString("tooltiptext", element, toolTipText);

        first_letter_is_capital = parseBool("first_letter_is_capital", element,
                first_letter_is_capital);
        ignore_on_save = parseBool("ignore_on_save", element, ignore_on_save);
        hideable = parseBool("hideable", element, hideable);
        has_apply_to_all = parseBool("has_apply_to_all", element,
                has_apply_to_all);

        format = parseBool("format", element, format);
        if (format) {
            String str = parseString("formatter", element, formatter);
            if (str != null) {
                formatter = str;
                decimalFormat = new DecimalFormat(formatter);
            }
        }
        logger.debug(this);
    }

    private String parseString(String tag, Element element, String defaultString) {
        if (element == null) {
            return defaultString;
        }
        String ret = defaultString;
        Element tmp = element.getChild(tag);
        if (tmp != null) {
            ret = tmp.getValue().trim();
        }
        return ret;
    }

    private boolean parseBool(String tag, Element element, boolean defaultBool) {
        boolean ret = defaultBool;
        if (element == null) {
            return defaultBool;
        }
        Element tmp = element.getChild(tag);
        if (tmp != null) {
            logger.debug(getName() + " at parse Bool: " + tag + " "
                    + tmp.getValue());
            ret = new Boolean(tmp.getValue().trim()).booleanValue();
        }

        return ret;
    }

    private void parseCellEditor(Element element) {
        if (element == null) {
            return;
        }
        cell_editor = element.getChildTextTrim("type");
        if (cell_editor == null
                || (!cell_editor.equalsIgnoreCase(TEXT_FIELD)
                        && !cell_editor.equalsIgnoreCase(COMBO_BOX) && !cell_editor
                            .equalsIgnoreCase(CHECK_BOX))) {
            cell_editor = TEXT_FIELD;
        }

        if (cell_editor.equalsIgnoreCase(COMBO_BOX)) {
            is_editable = false; // コンボボックスの場合, デフォルトではeditableはfalseとすべし
            String foo = element.getChildTextTrim("choices");
            if (foo != null) {
                if (foo.equalsIgnoreCase("FROM_CLASS")) {
                    Object obj = loadFrom(
                            element.getChildTextTrim("loadclass"),
                            element.getChildTextTrim("args"));
                    if (obj != null && obj instanceof ComboBoxChoices) {
                        choi = (ComboBoxChoices) obj;
                    }
                } else {
                    String[] bar = foo.split(",");
                    choices = bar;
                }
            }
        }

        is_editable = parseBool("editable", element, is_editable);
        defaultValue = element.getChildTextTrim("default_value");
    }

    private Object loadFrom(String loadClass, String arg) {
        Object obj = null;
        Object[] oargs = null;

        if (arg != null) {
            String[] args = arg.split(",");
            oargs = new Object[args.length];
            for (int i = 0; i < args.length; i++) {
                if (args[i].trim().equals("INPUT_INTERFACE")) {
                    oargs[i] = parent.getInputInterfaceTableSpecManager()
                            .getInputInterface();
                } else {
                    // we assume that the allowed args are basically strings.
                    oargs[i] = args[i];
                }
            }
        }

        if (oargs != null) {
            for (int i = 0; i < oargs.length; i++) {
                logger.debug("arg: " + oargs[i]);
            }
        }

        try {
            Class cla = getClass().getClassLoader().loadClass(loadClass);
            Constructor[] constructor = cla.getDeclaredConstructors();
            obj = constructor[0].newInstance(oargs);
        } catch (ClassNotFoundException cnfe) {
        } catch (Exception ex) {
            logger.error("failed to load class: " + loadClass);
            ex.printStackTrace();
        }

        return obj;
    }

    public String toString() {
        String ret = getName();
        ret += " has apply-to-all check box? "
                + Boolean.valueOf(has_apply_to_all);
        return ret;
    }

}
