/*
 * Carnegie Mellon University, Human Computer Interaction Institute
 * Copyright 2009
 * All Rights Reserved
 */
import static edu.cmu.pslc.datashop.util.CollectionUtils.map;

import java.awt.Dimension;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.RowFilter;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

/**
 * A Swing UI for the Demo Client for Datashop Web Services.
 */
public class WebServicesDemoClientUI extends JFrame {
    /** Root text. */
    private static final String ROOT_TEXT = "Dataset ";
    /** Root node. */
    private String rootNodeText;
    /** The table row sorter. */
    private TableRowSorter<MyTableModel> sorter;
    /** The JTable. */
    private JTable table;
    /** The JTree. */
    private JTree tree;

    /**
     * The constructor.
     * @param root the root node
     * @param datasetId the dataset id
     */
    public WebServicesDemoClientUI(String root, String datasetId) {
        //super(new GridLayout(1,0));
        rootNodeText = ROOT_TEXT + " " + datasetId;

        String[] columnNames = {"Problem Hierarchy",
                                "Problem",
                                "Step",
                                "Outcome",
                                "Input",
                                "Observations"};

        WebServicesDemoClient client = WebServicesDemoClient.createErrorReport(root);
        TreeMap<TransactionDataSubset, Integer> map = client.runReport(datasetId);

        Object[][] data = new Object[map.size()][6];

        int idx = 0;
        for (Map.Entry<TransactionDataSubset, Integer> entry : map.entrySet()) {
            TransactionDataSubset t = entry.getKey();
            Integer numObservations = entry.getValue();
            Object[] row = {t.getProblemHierarchy(),
                            t.getProblem(),
                            t.getStep(),
                            t.getOutcome(),
                            t.getInput(),
                            numObservations};
            data[idx] = row;
            idx++;
        }

        //Create a table model, table and sorter.
        MyTableModel model = new MyTableModel(columnNames, data);
        sorter = new TableRowSorter<MyTableModel>(model);
        table = new JTable(model);
        table.setRowSorter(sorter);
        table.setPreferredScrollableViewportSize(new Dimension(500, 70));
        table.setFillsViewportHeight(true);

        //Create the scroll pane and add the table to it.
        JScrollPane scrollPane = new JScrollPane(table);

        //Create hash map of problem hierarchy strings, to a hash map of problem name strings,
        //to a hash map of step names to outcome strings.
        HashMap<String, HashMap> phMap = new HashMap();
        for (Map.Entry<TransactionDataSubset, Integer> entry : map.entrySet()) {
            TransactionDataSubset t = entry.getKey();

            HashMap<String, HashMap> problemMap = phMap.get(t.getProblemHierarchy());
            if (problemMap == null) {
                problemMap = new HashMap();

                HashMap<String, String> stepMap = new HashMap();
                stepMap.put(t.getStep(), t.getOutcome());
                problemMap.put(t.getProblem(), stepMap);

                phMap.put(t.getProblemHierarchy(), problemMap);
            } else {
                HashMap<String, String> stepMap = problemMap.get(t.getProblem());
                if (stepMap == null) {
                    stepMap = new HashMap();
                    stepMap.put(t.getStep(), t.getOutcome());
                    problemMap.put(t.getProblem(), stepMap);
                } else {
                    String outcome = stepMap.get(t.getStep());
                    if (outcome == null) {
                        stepMap.put(t.getStep(), t.getOutcome());
                        problemMap.put(t.getProblem(), stepMap);
                    }
                }
            }
        }

        //Create the tree nodes from the crazy hash map thing created above.
        DefaultMutableTreeNode rootTreeNode =
            new DefaultMutableTreeNode(rootNodeText);
        for (Map.Entry<String, HashMap> phEntry : phMap.entrySet()) {
            DefaultMutableTreeNode phNode = new DefaultMutableTreeNode(phEntry.getKey());

            HashMap<String, HashMap> problemMap = phMap.get(phEntry.getKey());
            for (Map.Entry<String, HashMap> problemEntry : problemMap.entrySet()) {
                DefaultMutableTreeNode problemNode =
                        new DefaultMutableTreeNode(problemEntry.getKey());

                HashMap<String, String> stepMap = problemMap.get(problemEntry.getKey());
                for (Map.Entry<String, String> stepEntry : stepMap.entrySet()) {
                    DefaultMutableTreeNode stepNode =
                            new DefaultMutableTreeNode(stepEntry.getKey());
                    problemNode.add(stepNode);
                }

                phNode.add(problemNode);
            }
            rootTreeNode.add(phNode);
        }

        //Create a tree that allows one selection at a time.
        tree = new JTree(rootTreeNode);
        tree.getSelectionModel().setSelectionMode
                (TreeSelectionModel.SINGLE_TREE_SELECTION);
        //Create a listener on selections in the tree
        tree.addTreeSelectionListener(
                new TreeSelectionListener() {
                    public void valueChanged(TreeSelectionEvent event) {
                        newFilter();
                    }
                });

        //Create the scroll pane and add the tree to it.
        JScrollPane treeView = new JScrollPane(tree);

        //Add the scroll panes to a split pane.
        JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        splitPane.setTopComponent(treeView);
        splitPane.setBottomComponent(scrollPane);

        Dimension minimumSize = new Dimension(100, 50);
        scrollPane.setMinimumSize(minimumSize);
        treeView.setMinimumSize(minimumSize);
        splitPane.setDividerLocation(100);
        splitPane.setPreferredSize(new Dimension(500, 300));

        //Add the split pane to this panel.
        add(splitPane);

        this.setSize(800, 400);
        //pack();
        setVisible(true);
    };

    /**
     * Filter the rows in the table based on the node of the tree selected.
     */
    private void newFilter() {
        TreePath path = tree.getSelectionPath();
        if (path == null) {
            return;
        }
        String filterText = path.toString();
        filterText = filterText.substring(rootNodeText.length());
        if (filterText.length() <= 3) {
            sorter.setRowFilter(null);
            return;
        }
        filterText = filterText.substring(3, filterText.length() - 1);

        RowFilter<MyTableModel, Object> rf = null;
        try {
            List<RowFilter<MyTableModel, Object>> filters
                = new ArrayList<RowFilter<MyTableModel, Object>>(3);
            String[] fields = filterText.split(", ");
            for (int i = 0; i < fields.length; i++) {
                rf = RowFilter.regexFilter(fields[i], i);
                filters.add(rf);
            }
            RowFilter<MyTableModel, Object> myAndFilters = RowFilter.andFilter(filters);
            sorter.setRowFilter(myAndFilters);
        } catch (java.util.regex.PatternSyntaxException e) {
            //if current expression doesn't parse, don't update.
            return;
        }
    }

    /** Inner Class. */
    class MyTableModel extends AbstractTableModel {
        /** The array of column names. */
        private String[] columnNames;
        /** The two-dimensional array of data. */
        private Object[][] data;

        /**
         * Constructor.
         * @param columnNames the array of column names
         * @param data the two-dimensional array of data
         */
        public MyTableModel(String[] columnNames, Object[][] data) {
            this.columnNames = columnNames;
            this.data = data;
        }

        /** Return the column count. @return the column count */
        public int getColumnCount() {
            return columnNames.length;
        }

        /** Return the row count. @return the row count */
        public int getRowCount() {
            return data.length;
        }

        /** Gets the column name.
         * @param col the column index
         * @return the column name */
        public String getColumnName(int col) {
            return columnNames[col];
        }

        /** Gets the value.
         * @param row the row index
         * @param col the column index
         * @return the value at at a given row and column */
        public Object getValueAt(int row, int col) {
            return data[row][col];
        }

        /** Gets the class at a given column index
         * @param c the column index
         * @return the class at a given column */
        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }
    }

    /**
     * Main.
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Map<String, String> argsMap = map((Object[])args);
        String datasetId = argsMap.get("dataset");

        if (datasetId == null) { datasetId = System.getProperty("dataset"); }

        if (datasetId == null) {
            System.out.println("No property or parameter found for datasetId");
            System.exit(1);
        }

        new WebServicesDemoClientUI(WebServicesDemoClient.QA_SERVICES_ROOT, datasetId);
    }
}
