/*
 * DiffTable.java
 *
 * Created on May 25, 2006, 2:51 PM
 *
 */
package usda.weru.util.diff.table;

import com.klg.jclass.cell.JCCellInfo;
import com.klg.jclass.cell.JCComponentCellRenderer;
import com.klg.jclass.cell.renderers.JCLabelCellRenderer;
import com.klg.jclass.table.JCSelectEvent;
import com.klg.jclass.table.JCSelectListener;
import com.klg.jclass.table.JCTableEnum;
import com.klg.jclass.table.beans.LiveTable;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Font;
import java.util.Hashtable;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeCellRenderer;
import usda.weru.mcrew.DataObject;
import usda.weru.mcrew.ManageData;
import usda.weru.mcrew.RowInfo;
import usda.weru.resources.Resources;
import usda.weru.util.diff.DiffEngine;
import usda.weru.util.diff.Differ;

/**
 *
 * @author Joseph Levin
 */
public class DiffTable extends LiveTable implements JCSelectListener {

    private static final long serialVersionUID = 1L;

    private DiffTabelModel dataSource;
    private Hashtable<Integer, Boolean> c_expandedMap;
    private int c_filter;

    /** Creates a new instance of DiffTable */
    public DiffTable() {
        super();
        init();
        initCellRenderers();

    }

    private void init() {
        c_expandedMap = new Hashtable<Integer, Boolean>();

        //Default data source
        setDataSource(new DiffTabelModel());

        //Selection events
        addSelectListener(this);

        //Default display
        setColumnHidden(-1, true);
        setAutoscrolls(false);
        setAutoScroll(JCTableEnum.AUTO_SCROLL_NONE);
        setHorizSBPosition(JCTableEnum.POSITION_AT_SIDE);
        setVertSBPosition(JCTableEnum.POSITION_AT_SIDE);
        setSelectionPolicy(JCTableEnum.SELECT_RANGE);
        setAllowResizeBy(JCTableEnum.RESIZE_BY_LABELS);
        setAllowCellResize(JCTableEnum.RESIZE_COLUMN);
        setPopupMenuEnabled(false);

        //this.setTrackCursor(false);
        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));

        //Cells
        getDefaultCellStyle().setEditable(false);

        setPixelWidth(0, 160);
        setPixelWidth(1, 100);
        setPixelWidth(2, 1);
        setPixelWidth(3, 1);

        autoResizeColumns();

        //Header
        getDefaultLabelStyle().setBackground(new Color(Integer.parseInt("FFEC8B", 16)));
        getDefaultLabelStyle().setVerticalAlignment(JCTableEnum.CENTER);
        Font headerFont = getDefaultLabelStyle().getFont();
        headerFont = new Font(headerFont.getName(), Font.BOLD, 12);
        getDefaultLabelStyle().setFont(headerFont);
        setPixelHeight(-1, 25);

    }

    private void initCellRenderers() {
        setCellRenderer(TreeWrapper.class, new TreeCellRenderer());
        setCellRenderer(DiffWrapper.class, new DiffCellRenderer());
        setCellRenderer(ManageData.class, WepsObjectsCellRenderer.class);
        setCellRenderer(RowInfo.class, WepsObjectsCellRenderer.class);
        setCellRenderer(DataObject.class, WepsObjectsCellRenderer.class);
    }

    /**
     *
     */
    public void autoResizeColumns() {
        int total = 0;
        int[] widths = new int[4];
        for (int c = 0; c < 4; c++) {
            widths[c] = getPixelWidth(c);
            total += widths[c];
        }

        int valueSpace = getWidth() - widths[0] - widths[1];
        JScrollBar vbar = getVertSB();
        if (vbar.isVisible()) {
            valueSpace -= vbar.getWidth();
        }
        int half = valueSpace / 2;
        setPixelWidth(2, half);
        setPixelWidth(3, half);
    }

    /**
     *
     * @param index
     */
    public void toggleNode(int index) {
        if (isExpanded(index)) {
            collapseNode(index);
        } else {
            expandNode(index);
        }
    }

    /**
     *
     * @param index
     */
    public void expandNode(int index) {
        c_expandedMap.put(index, true);
        int numDecendants = dataSource.getNumDescendants(index);
        for (int i = 0; i < numDecendants; i++) {
            updateVisibility(i + index + 1);
        }
    }

    /**
     *
     * @param index
     */
    public void collapseNode(int index) {
        c_expandedMap.put(index, false);
        int numDecendants = dataSource.getNumDescendants(index);
        for (int i = 0; i < numDecendants; i++) {
            updateVisibility(i + index + 1);
        }

    }

    private void updateVisibility(int index) {
        setRowHidden(index, isHidden(index) || isFilteredOut(index));
    }

    private boolean isLeaf(int index) {
        return dataSource.getNumDescendants(index) == 0;
    }

    private boolean isExpanded(int index) {
        boolean expanded;
        try {
            expanded = c_expandedMap.get(index);
        } catch (NullPointerException npe) {
            expanded = false;
        }
        return expanded;
    }

    private boolean isHidden(int index) {
        int parentIndex = dataSource.getParentIndex(index);
        if (parentIndex < 0) {
            return false;
        }

        while (parentIndex >= 0) {
            if (isExpanded(parentIndex) == false) {
                return true;
            }
            parentIndex = dataSource.getParentIndex(parentIndex);
        }
        return false;
    }

    private boolean isFilteredOut(int index) {
        return !isFilteredIn(index);
    }

    private boolean isFilteredIn(int index) {
        DiffWrapper wrapper = (DiffWrapper) dataSource.getTableDataItem(index, 1);
        int diff = wrapper.getDiff();
        for (int childIndex : dataSource.getDirectChildren(index)) {
            if (isFilteredIn(childIndex)) {
                return true;
            }
        }
        if ((diff & c_filter) > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     *
     * @param filter
     */
    public void setFilter(int filter) {
        c_filter = filter;
        for (int index = 0; index < dataSource.getNumRows(); index++) {
            updateVisibility(index);
        }
        autoResizeColumns();
    }

    //Selection events
    @Override
    public void beforeSelect(JCSelectEvent event) {
        if (event.getStartColumn() == 0 && event.getStartRow() >= 0) {
            toggleNode(event.getStartRow());
        }
    }

    @Override
    public void select(JCSelectEvent event) {

    }

    @Override
    public void afterSelect(JCSelectEvent event) {

    }

    /**
     *
     * @param model
     */
    public void setDataSource(DiffTabelModel model) {
        c_expandedMap = new Hashtable<Integer, Boolean>();
        dataSource = model;
        super.setDataSource(model);
        setFilter(c_filter);
        expandChangedNodes();

    }

    /**
     *
     * @param index
     */
    public void ensureVisible(int index) {
        int parentIndex = dataSource.getParentIndex(index);
        while (parentIndex >= 0) {
            expandNode(parentIndex);
            parentIndex = dataSource.getParentIndex(parentIndex);
        }
    }

    /**
     *
     */
    public void expandChangedNodes() {
        for (int i = 0; i < dataSource.getNumRows(); i++) {
            Object o = dataSource.getTableDataItem(i, 1);
            DiffWrapper wrapper = (DiffWrapper) o;
            int diff = wrapper.getDiff();
            if (DiffEngine.bitFlag(diff, Differ.DIFF_SAME) == false) {
                ensureVisible(i);
            }

        }
    }

    @Override
    public DiffTabelModel getDataSource() {
        return dataSource;
    }

    class TreeCellRenderer implements JCComponentCellRenderer {

        private static final long serialVersionUID = 1L;

        private final DefaultTreeCellRenderer c_renderer;
        private final JTree c_tree;
        private static final int NODE_SPACING = 10;

        /**
         * Creates a new instance of TreeCellRenderer
         */
        public TreeCellRenderer() {
            c_renderer = new DefaultTreeCellRenderer();
            c_tree = new JTree();

            c_renderer.setOpenIcon(Resources.getIcon("openfolder.gif"));
            c_renderer.setClosedIcon(Resources.getIcon("folder.gif"));

        }

        @Override
        public Component getRendererComponent(JCCellInfo cellInfo, Object o, boolean selected) {
            TreeWrapper wrapper = (TreeWrapper) o;
            int index = wrapper.getIndex();
            int depth = wrapper.getDepth();
            String text = wrapper.getText();
            boolean expanded = isExpanded(index);
            boolean leaf = !wrapper.hasChildren();
            boolean hasFocus = false;

            JPanel panel = new JPanel(null);
            Component component = c_renderer.getTreeCellRendererComponent(c_tree, text, selected, expanded,
                    leaf, 1, hasFocus);

            int fillerWidth = (depth - 1) * NODE_SPACING;
            panel.add(component);
            panel.setBackground(cellInfo.getBackground());
            component.setLocation(fillerWidth, -2);
            component.setSize(component.getPreferredSize());
            return panel;
        }

    }

    class DiffCellRenderer extends JCLabelCellRenderer {

        private static final long serialVersionUID = 1L;

        /** Creates a new instance of DiffCellRenderer */
        public DiffCellRenderer() {
            super();
        }

        @Override
        public Component getRendererComponent(JCCellInfo cellInfo, Object o, boolean selected) {
            DiffWrapper wrapper = (DiffWrapper) o;
            int index = wrapper.getIndex();
            int diff;
            boolean expanded = isExpanded(index);
            boolean leaf = isLeaf(index);
            if (expanded) {
                diff = wrapper.getDiff();
            } else {
                diff = wrapper.getDiffRecursive();
            }

            String text = "";

            //Changed values
            if (leaf || expanded) {
                if (DiffEngine.bitFlag(diff, Differ.DIFF_UNKNOWN)) {
                    text = text + bold(font("#CCCCCC", "?"));   //Gray
                }
                if (DiffEngine.bitFlag(diff, Differ.DIFF_SAME) && DiffEngine.bitFlag(c_filter, Differ.DIFF_SAME)) {
                    text += font("BLACK", "Same");
                }
                if (DiffEngine.bitFlag(diff, Differ.DIFF_INCREASED)) {
                    text = text + bold(font("BLUE", "Increased"));
                } else if (DiffEngine.bitFlag(diff, Differ.DIFF_DECREASED)) {
                    text = text + bold(font("BLUE", "Decreased"));
                } else if (DiffEngine.bitFlag(diff, Differ.DIFF_CHANGED)) {
                    text = text + font("BLUE", "Changed");
                }

                if (DiffEngine.bitFlag(diff, Differ.DIFF_ADDED)) {
                    text = text + bold(font("GREEN", "Added"));
                }

                if (DiffEngine.bitFlag(diff, Differ.DIFF_REMOVED)) {
                    text = text + bold(font("RED", "Removed"));
                }

                if (DiffEngine.bitFlag(diff, Differ.DIFF_ERROR)) {
                    text = bold(font("RED", "ERROR"));
                }
            } else {
                if (DiffEngine.bitFlag(diff, Differ.DIFF_CHANGED)) {
                    text = bold(font("BLUE", "Changed"));
                } else {
                    text = font("BLACK", "Same");
                }
            }

            setText(html(text));
            setBackground(selected ? cellInfo.getSelectedBackground() : cellInfo.getBackground());
            setForeground(selected ? cellInfo.getSelectedForeground() : cellInfo.getForeground());
            setHorizontalAlignment(cellInfo.getHorizontalAlignment());
            setVerticalAlignment(cellInfo.getVerticalAlignment());
            return (this);
        }

        private String font(String color, String text) {
            return "<font color=" + color + ">" + text + "</font>";
        }

        private String html(String text) {
            return "<HTML>" + text + "</HTML>";
        }

        private String bold(String text) {
            return "<b>" + text + "</b>";
        }
    }

}
