/*
 * CellStyle.java
 *
 * Created on June 8, 2006, 12:08 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package usda.weru.util.table;

import com.klg.jclass.cell.JCCellEditor;
import com.klg.jclass.cell.JCCellRenderer;
import com.klg.jclass.table.JCCellBorder;
import com.klg.jclass.table.JCTableEnum;
import java.awt.Color;
import java.awt.Font;
import java.lang.reflect.InvocationTargetException;
import java.util.Vector;
import java.util.List;
import org.jdom2.Element;

/**
 * Wrapper class for a JCCellStyle containing additional information required of the WepsTable.
 * @author Joseph Levin
 */
public class CellStyle extends com.klg.jclass.table.JCCellStyle {

    private static final long serialVersionUID = 1L;

    /**
     *
     */
    protected String c_id;

    /**
     *
     */
    protected WepsTableMeta c_meta;

    /**
     *
     */
    protected boolean c_anonymous;

    /**
     *
     */
    protected boolean c_conditional;

    /**
     *
     */
    protected String c_parentId;

    /**
     *
     */
    protected CellStyle c_parent;

    /**
     *
     */
    protected List<CellStyle> c_conditionalStyles;

    /**
     *
     */
    protected String c_expression;

    /**
     * Creates a default CellStyle with the specified WepsTableMeta.
     * @param meta The WepsTableMeta that owns this CellStyle.
     */
    public CellStyle(WepsTableMeta meta) {
        super();
        c_meta = meta;
    }

    /**
     * Creates a CellStyle with the specified WepsTableMeta and parent CellStyle.
     * @param meta The WepsTableMeta that owns this CellStyle.
     * @param parent The parent CellStyle from which the CellStyle will inherit all of its propertiesl.
     */
    public CellStyle(WepsTableMeta meta, CellStyle parent) {
        super(parent);
        c_parent = parent;
        c_meta = meta;
    }

    /**
     * Return this CellStyles ID.
     * @return This CellStyles ID.
     */
    public String getId() {
        return c_id;
    }

    /**
     * Check to see if this CellStyle is anonymous.
     * @return <b>true</b> if this is an anonymous CellStyle, <b>false</b> otherwise.
     */
    public boolean isAnonymous() {
        return c_anonymous;
    }

    /**
     * Check to see if this CellStyle is conditional.
     * @return <b>true</b> if this is a conditional CellStyle, <b>false</b> otherwise.
     */
    public boolean isConditional() {
        return c_conditional;
    }

    /**
     * Load the CellStyles configuration from an jdom Element.
     * @param node An org.jdom2.Element containing configuration information for this CellStyle.
     */
    public void fromXml(Element node) {
        loadId(node);
        loadParent(node);
        loadTraversable(node);
        loadEditable(node);
        loadBackgroundColors(node);
        loadForegroundColors(node);
        loadBorder(node);
        loadClipHints(node);
        loadAlignments(node);
        loadFont(node);
        loadRenderer(node);
        loadEditor(node);
        loadConditionalStyles(node);
        if (c_parent != null) {
            loadExpression(node);
        }
    }

    /**
     * Reload the parent style for this CellStyle.
     */
    public void refreshParentStyle() {
        if (c_parentId != null) {
            CellStyle parentStyle = c_meta.getCellStyle(c_parentId);
            if (parentStyle != null && parentStyle != this) {
                setParentStyle(parentStyle);
            }
        }
    }

    /**
     * Return the parent CellStyle associated with this CellStyle.
     * @return The parent CellStyle associated with this CellStyle.
     */
    @Override
    public CellStyle getParentStyle() {
        return c_parent;
    }

    private void loadId(Element node) {
        //Get Id                
        String id = node.getAttributeValue(WepsTableEnum.XML_id);
        if (id != null) {
            c_id = id;
        } else {
            //Get Id from counter
            c_id = c_meta.getAnonymousId();
            c_anonymous = true;
        }
    }

    private void loadParent(Element node) {
        //Set Parent Style
        c_parentId = node.getAttributeValue(WepsTableEnum.XML_parent);
        if (c_parentId != null) {
            CellStyle parentStyle = c_meta.getCellStyle(c_parentId);
            //Has the parent been loaded?
            if (parentStyle != null) {
                setParentStyle(parentStyle);
            }
        }
    }

    private void loadExpression(Element node) {
        c_expression = node.getAttributeValue(WepsTableEnum.XML_expression);
        if (c_expression != null && c_expression.length() > 0) {
            c_conditional = true;
        }
    }

    private void loadTraversable(Element node) {
        String traversableText = node.getChildTextTrim(WepsTableEnum.XML_traversable);
        setTraversable(Helper.isTrue(traversableText));
    }

    private void loadEditable(Element node) {
        String editableText = node.getChildTextTrim(WepsTableEnum.XML_editable);
        setEditable(Helper.isTrue(editableText));
    }

    @Override
    public boolean isEditable() {
        boolean t = super.isEditable();
        return t;
    }

    private void loadBackgroundColors(Element node) {
        List<Element> backgroundColors = node.getChildren(WepsTableEnum.XML_background);

        //Clear defaults
        setBackground(null);
        setRepeatBackgroundColors(null);

        //Set the background color to the first color        
        if (backgroundColors.size() == 1) {
            String value = backgroundColors.get(0).getText();
            Color color = Helper.parseColor(value);
            setBackground(color);
            setRepeatBackground(JCTableEnum.REPEAT_NONE);
        } else if (backgroundColors.size() > 1) {
            //Might be a repeat policy
            try {

                //We have a valid policy, set the repeating colors
                setRepeatBackgroundColors(Helper.parseColorList(backgroundColors));

                //Convert policy to JC Constant Ennum
                String repeatText = node.getChildTextTrim(WepsTableEnum.XML_repeatbackground);
                if (repeatText == null || repeatText.length() == 0) {
                    return;
                }
                int backgroundRepeatPolicy = WepsTableEnum.getEnumFromTag(repeatText);
                setRepeatBackground(backgroundRepeatPolicy);
            } catch (IllegalArgumentException iae) {
            } catch (NullPointerException npe) {
            }

        }
    }

    private void loadForegroundColors(Element node) {
        List<Element> foregroundColors = node.getChildren(WepsTableEnum.XML_foreground);

        //Clear defaults
        setForeground(null);
        setRepeatForegroundColors(null);

        //Set the background color to the first color
        if (foregroundColors.size() == 1) {
            String value = foregroundColors.get(0).getText();
            Color color = Helper.parseColor(value);
            setForeground(color);
            setRepeatForeground(JCTableEnum.REPEAT_NONE);
        } else if (foregroundColors.size() > 1) {
            //Might be a repeat policy
            try {
                //We have a valid policy, set the repeating colors
                setRepeatForegroundColors(Helper.parseColorList(foregroundColors));

                //Convert policy to JC Constant Ennum
                String repeatText = node.getChildTextTrim(WepsTableEnum.XML_repeatforeground);
                if (repeatText == null || repeatText.length() == 0) {
                    return;
                }
                int foregroundRepeatPolicy = WepsTableEnum.getEnumFromTag(repeatText);
                setRepeatForeground(foregroundRepeatPolicy);

            } catch (IllegalArgumentException iae) {
            } catch (NullPointerException npe) {
            }

        }
    }

    private void loadBorder(Element node) {
        //clear defaults
        setCellBorder(null);
        setCellBorderColor(null);

        String borderText = node.getChildTextTrim(WepsTableEnum.XML_border);
        if (borderText == null || borderText.length() == 0) {
            return;
        }

        int borderPolicy = WepsTableEnum.getEnumFromTag(borderText);
        setCellBorder(new JCCellBorder(borderPolicy));

        Element borderNode = node.getChild(WepsTableEnum.XML_border);
        String colorText = borderNode.getAttributeValue(WepsTableEnum.XML_color);
        Color color = Helper.parseColor(colorText);
        if (color != null) {
            setCellBorderColor(color);
            setCellBorderColorMode(JCTableEnum.USE_CELL_BORDER_COLOR);
        }
        boolean left = Helper.isTrue(borderNode.getAttributeValue(WepsTableEnum.XML_left));
        boolean right = Helper.isTrue(borderNode.getAttributeValue(WepsTableEnum.XML_right));
        boolean top = Helper.isTrue(borderNode.getAttributeValue(WepsTableEnum.XML_top));
        boolean bottom = Helper.isTrue(borderNode.getAttributeValue(WepsTableEnum.XML_bottom));
        boolean all = Helper.isTrue(borderNode.getAttributeValue(WepsTableEnum.XML_all));
        boolean none = Helper.isTrue(borderNode.getAttributeValue(WepsTableEnum.XML_none));

        int borderSides = JCTableEnum.BORDERSIDE_ALL;

        if (left || right || top || bottom || all || none) {
            borderSides = 0;
            if (left) {
                borderSides |= JCTableEnum.BORDERSIDE_LEFT;
            }
            if (right) {
                borderSides |= JCTableEnum.BORDERSIDE_RIGHT;
            }
            if (top) {
                borderSides |= JCTableEnum.BORDERSIDE_TOP;
            }
            if (bottom) {
                borderSides |= JCTableEnum.BORDERSIDE_BOTTOM;
            }
            if (all) {
                borderSides |= JCTableEnum.BORDERSIDE_ALL;
            }
        }
        setCellBorderSides(borderSides);
    }

    private void loadClipHints(Element node) {
        //Clear defaults
        setClipHints(JCTableEnum.SHOW_HORIZONTAL);
        String clipText = node.getChildText(WepsTableEnum.XML_cliphints);
        int clipPolicy = WepsTableEnum.getEnumFromTag(clipText);
        if (clipPolicy != WepsTableEnum.NOT_FOUND) {
            setClipHints(clipPolicy);
        }
    }

    private void loadAlignments(Element node) {
        String horizontalText = node.getChildTextTrim(WepsTableEnum.XML_horizontalalignment);
        int horizontalPolicy = WepsTableEnum.getEnumFromTag(horizontalText);
        if (horizontalPolicy != WepsTableEnum.NOT_FOUND) {
            setHorizontalAlignment(horizontalPolicy);
        }

        String verticalText = node.getChildTextTrim(WepsTableEnum.XML_verticalalignment);
        int verticalPolicy = WepsTableEnum.getEnumFromTag(verticalText);
        if (verticalPolicy != WepsTableEnum.NOT_FOUND) {
            setVerticalAlignment(verticalPolicy);
        }
    }

    private void loadFont(Element node) {
        //Clear defaults
        setFont(null);

        Element fontNode = node.getChild(WepsTableEnum.XML_font);
        if (fontNode != null) {
            Font font = XmlFont.fromXml(fontNode);
            setFont(font);
        }
    }

    private void loadRenderer(Element node) {
        String rendererClassName = node.getChildTextTrim(WepsTableEnum.XML_renderer);
        if (rendererClassName != null && rendererClassName.length() > 0) {
            Class<?> renderer;
            try {
                renderer = Class.forName(rendererClassName);
                setCellRenderer((JCCellRenderer) renderer.getDeclaredConstructor().newInstance());
            } catch (ClassNotFoundException cnfe) {
                //TODO: have a better failing method.
                //System.err.println("Unable to load JCCellRenderer : " + rendererClassName);
            } catch (InstantiationException ie) {
                //System.err.println("Unable to load JCCellRenderer : " + rendererClassName);
            } catch (IllegalAccessException iae) {
                //System.err.println("Unable to load JCCellRenderer : " + rendererClassName);
            }
            catch(NoSuchMethodException ncd)
            {
                
            }
            catch(InvocationTargetException ite)
            {
                
            }
        }
    }

    private void loadEditor(Element node) {
        String editorClassName = node.getChildTextTrim(WepsTableEnum.XML_editor);
        if (editorClassName != null && editorClassName.length() > 0) {
            Class<?> editor;
            try {
                editor = Class.forName(editorClassName);
                setCellEditor((JCCellEditor) editor.getDeclaredConstructor().newInstance());
            } catch (ClassNotFoundException cnfe) {
                //TODO: have a better failing method.
                //System.err.println("Unable to load JCCellEditor : " + editorClassName);
            } catch (InstantiationException ie) {
                //System.err.println("Unable to load JCCellEditor : " + editorClassName);
            } catch (IllegalAccessException iae) {
                //System.err.println("Unable to load JCCellEditor : " + editorClassName);
            }
            catch(NoSuchMethodException ncd)
            {
                
            }
            catch(InvocationTargetException ite)
            {
                
            }
        }
    }

    private void loadConditionalStyles(Element node) {
        List<Element> stylesList = node.getChildren(WepsTableEnum.XML_conditional);
        for (Element styleNode : stylesList) {
            CellStyle style = new CellStyle(c_meta, this);
            style.fromXml(styleNode);
            if (style.isConditional()) {
                if (c_conditionalStyles == null) {
                    c_conditionalStyles = new Vector<CellStyle>();
                }
                c_conditionalStyles.add(style);
            }
        }
    }

    /**
     * Returns the conditional expression associated with this CellStyle.
     * @return The conditional expression or null if this is not a conditional CellStyle.
     */
    public String getExpression() {
        return c_expression;
    }

    /**
     * Return the conditional CellStyles associated with this CellStyle.  Will check the parent if there are no conditional CellStyles associated with this CellStyle.
     * @return A List containing all available conditional CellStyles from this CellStyle or its parent.
     */
    public List<CellStyle> getConditionalStyles() {
        if (c_conditionalStyles == null && c_parent != null) {
            return c_parent.getConditionalStyles();
        } else {
            return c_conditionalStyles;
        }
    }

    /**
     * Return whether or not there are any conditional CellStyles in this CellStyle.
     * @return <b>true</b> if this CellStyle has any conditional CellStyles, <b>false</b> otherwise.
     */
    public boolean hasConditionalStyles() {
        List<CellStyle> list = getConditionalStyles();
        if (list == null || list.size() == 0) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Return the Font associated with this CellStyle, it's parent's Font if there is no Font in this CellStyle, or <code>new Font("SansSerif",Font.PLAIN, 11);</code> if there is no parent and no Font.
     * @return The Font to be used by this CellStyle.
     */
    @Override
    public Font getFont() {
        if (font == null && c_parent != null) {
            return c_parent.getFont();
        } else if (font == null) {
            return new Font("SansSerif", Font.PLAIN, 11);
        } else {
            return font;
        }
    }

    /**
     * Overrides Object.toString().  Returns this CellStyles ID.
     * @return The CellStyles ID.
     */
    @Override
    public String toString() {
        return getId();
    }

    /**
     * Set the WepsTableMeta that owns this CellStyle.
     * @param meta The WepsTableMeta that owns this CellStyle.
     */
    public void setMeta(WepsTableMeta meta) {
        c_meta = meta;
        refreshParentStyle();
    }

    /**
     * Set the parent CellStyle for this CellStyle.
     * @param style The parent CellStyle.
     */
    public void setParentStyle(CellStyle style) {
        c_parent = style;
        super.setParentStyle(style);
    }
}
