/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/11/08, 13:45
!  AUTHOR(S): KOGA, Junichiro
!  File : DataPanel.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.graph;

import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JToggleButton;
import javax.swing.border.TitledBorder;
import javax.swing.table.DefaultTableModel;

import org.apache.log4j.Logger;

import ciss.phase_viewer.common.ChaseFileChooser;
import ciss.phase_viewer.common.Utils;
import ciss.phase_viewer.graph.dataset.DataSet;
import ciss.phase_viewer.graph.dataset.DataSetCollection;
import ciss.phase_viewer.graph.dataset.DataSetElement;
import ciss.phase_viewer.graph.dataset.DataSetManipulator;
import ciss.phase_viewer.graph.dataset.PlotProperties;
import ciss.phase_viewer.graph.dataset.SubPlotProperties;
import ciss.phase_viewer.outputinterface.OutputData;

/**
 * GraphPanel用のデータを表示したりするパネル.
 * 
 * @author
 */
public class DataPanel extends JPanel {
    private static Logger logger = Logger.getLogger(DataPanel.class.getName());

    private OutputData[] outputData;

    private JTabbedPane tabbedPane;

    private JPanel panel;

    public static final int TABBED = 0;

    public static final int SINGLE = 1;

    private int mode = TABBED;

    /**
     * Creates a new instance of MainPanel
     * 
     * @param outputData
     *            表示したいデータの情報を格納したOutputDataの配列. 配列の各要素が一つのタブに相当する.
     */
    public DataPanel(OutputData[] outputData) {
        this.outputData = outputData;
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        init();
    }

    /**
     * Creates a new instance of MainPanel
     * 
     * @param outputData
     *            表示したいデータの情報を格納したOutputDataの配列. 配列の各要素が一つのタブに相当する.
     * @param mode
     *            DataPanelにタブをつける場合TABBEDを, 表示したいデータが一つしかなく,
     *            タブはいらない場合SINGLEを渡す.
     */
    public DataPanel(OutputData[] outputData, int mode) {
        this.outputData = outputData;
        this.mode = mode;
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        init();
    }

    /**
     * @param outputData
     *            表示したいデータの情報を格納したOutputData. 配列ではないので, 自動的にタブでないモードを となる.
     */
    public DataPanel(OutputData outputDat) {
        this.outputData = new OutputData[1];
        this.outputData[0] = outputDat;
        this.mode = SINGLE;
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        init();
    }

    private void init() {
        if (outputData == null || outputData.length == 0) {
            return;
        }
        create();
        if (mode == TABBED) {
            add(tabbedPane);
        } else if (mode == SINGLE) {
            add(panel);
        }
        add(createButtonPanel());
    }

    public void setOutputData(OutputData[] outputData) {
        this.outputData = outputData;
        create();
    }

    public void setOutputData(OutputData outData) {
        if (this.outputData == null) {
            this.outputData = new OutputData[1];
        }
        this.outputData[0] = outData;
        create();
    }

    private void create() {
        if (outputData == null || outputData.length == 0) {
            return;
        }
        if (mode == TABBED) {
            if (tabbedPane == null) {
                tabbedPane = new JTabbedPane();
                tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
            }
            tabbedPane.removeAll();
            for (int i = 0; i < outputData.length; i++) {
                DataTab tab = new DataTab(outputData[i]);
                tabbedPane.addTab(outputData[i].getName(), tab);
                tabbedPane.setToolTipTextAt(i, outputData[i].getDescription());
            }
        } else if (mode == SINGLE) {
            if (panel == null) {
                panel = new JPanel();
                panel.setLayout(new GridLayout());
            }
            panel.removeAll();
            DataTab tab = new DataTab(outputData[0]);
            panel.add(tab);
            panel.revalidate();
        }
    }

    private JComboBox comboplottypes;

    private JToggleButton btntoggle;

    private JPanel createButtonPanel() {
        String[] plottypes = { "xy", "pie" };
        JPanel p = new JPanel();
        // p.setLayout( new BoxLayout(p,BoxLayout.X_AXIS) );
        JButton btnquickplot = new JButton("quick plot");
        JButton btnplot = new JButton("plot");

        comboplottypes = new JComboBox(plottypes);
        JButton btnload = new JButton("load data");
        JButton btntoclip = new JButton("to clipboard");
        JButton btnexport = new JButton("export");
        btntoggle = new JToggleButton("toggle selection mode");
        JButton btndel = new JButton("delete data");
        JButton btnsavedata = new JButton("save data");
        JButton btnclose = new JButton("close");

        JPanel pbtn = new JPanel();
        pbtn.setLayout(new BoxLayout(pbtn, BoxLayout.Y_AXIS));

        JPanel pplot = new JPanel();
        pplot.setLayout(new BoxLayout(pplot, BoxLayout.X_AXIS));
        pplot.add(btntoclip);
        pplot.add(btnexport);
        pplot.add(btntoggle);
        pplot.add(btnquickplot);
        pplot.add(comboplottypes);
        pplot.add(btnplot);
        pbtn.add(pplot);

        p.add(pbtn);

        btnquickplot.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (!doQuickPlot()) {
                    logger.error("plot failed.");
                }
            }
        });

        btnplot.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                bootDataSetManipulator();
            }
        });

        btntoggle.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                DataTab tab = getDataTab();
                if (tab == null) {
                    logger.error("invalid data tab.");
                    return;
                }

                JTable table = tab.getTable();
                if (table == null) {
                    logger.error("'null' table");
                    return;
                }
                table.setRowSelectionAllowed(btntoggle.isSelected());
            }
        });

        btnexport.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doExport();
            }
        });

        btntoclip.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                toClipboard();
            }
        });

        return p;
    }

    private void toClipboard() {
        DataTab tab = getDataTab();
        if (tab == null) {
            logger.error("invalid data tab.");
            return;
        }

        JTable table = tab.getTable();
        if (table == null) {
            logger.error("'null' table");
            return;
        }
        StringBuffer str = new StringBuffer();
        // String str = "#";
        // for ( int i=0 ; i<table.getColumnCount() ; i++ ) {
        // str += table.getColumnName(i)+" ";
        // }
        // str += System.getProperty("line.separator");

        int numcols = table.getColumnCount();
        int numrows = table.getRowCount();
        for (int i = 0; i < numrows; i++) {
            for (int j = 0; j < numcols; j++) {
                if (j == 0)
                    str.append(table.getValueAt(i, j));
                else
                    str.append("\t" + table.getValueAt(i, j));
            }
            str.append("\n");
        }
        Utils.toClipboard(str.toString());
    }

    private void doExport() {
        DataTab tab = getDataTab();
        if (tab == null) {
            logger.error("invalid data tab.");
            return;
        }

        JTable table = tab.getTable();
        if (table == null) {
            logger.error("'null' table");
            return;
        }
        ChaseFileChooser chooser = new ChaseFileChooser(ChaseFileChooser.base,
                true);

        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        if (chooser.showDialog(this, "do export") != JFileChooser.APPROVE_OPTION) {
            logger.info("canceled");
            return;
        }
        File exportTo = chooser.getSelectedFile();
        int numcols = table.getColumnCount();
        int numrows = table.getRowCount();
        logger.info("writing identifiers...");
        String idents = "#  ";
        for (int i = 0; i < numcols; i++) {
            idents += table.getColumnName(i) + "  ";
        }
        PrintWriter writer = null;
        try {
            writer = new PrintWriter(new BufferedWriter(
                    new FileWriter(exportTo)));
            writer.println(idents);
            logger.info("writing data...");
            for (int i = 0; i < numrows; i++) {
                String rowdata = "";
                for (int j = 0; j < numcols; j++) {
                    rowdata += table.getModel().getValueAt(i, j) + "  ";
                }
                writer.println(rowdata);
            }
            logger.info("... export to " + exportTo.getAbsolutePath()
                    + " finished.");
        } catch (Exception exc) {
            logger.error("failed export.");
            exc.printStackTrace();
        } finally {
            try {
                writer.close();
            } catch (Exception exc) {
                exc.printStackTrace();
                ;
            }
        }
    }

    private void bootDataSetManipulator() {
        DataSet[] dsets = createDataSet();
        DataSetManipulator manip = new DataSetManipulator(dsets);
    }

    private DataSet[] createDataSet() {
        DataSet[] ret = null;
        if (mode == TABBED) {
            ret = new DataSet[tabbedPane.getTabCount()];
        } else {
            ret = new DataSet[1];
        }
        JTable[] tables = getTables();
        String[] names = getNames();
        if (tables == null || names == null) {
            return null;
        }

        for (int i = 0; i < tables.length; i++) {
            ret[i] = new DataSet(names[i]);
            ret[i].setFileName(outputData[i].getFileName());
            int nrow = tables[i].getRowCount();
            int ncol = tables[i].getColumnCount();
            String[] idents = new String[ncol];
            for (int j = 0; j < ncol; j++) {
                idents[j] = tables[i].getColumnName(j);
            }
            for (int j = 0; j < ncol; j++) {
                String[] data = new String[nrow];
                for (int k = 0; k < nrow; k++) {
                    data[k] = (String) tables[i].getValueAt(k, j);
                }
                ret[i].addData(data, idents[j]);
            }
        }
        return ret;
    }

    private JTable[] getTables() {
        JTable[] tables;
        if (mode == TABBED) {
            tables = new JTable[tabbedPane.getTabCount()];
            for (int i = 0; i < tabbedPane.getTabCount(); i++) {
                tables[i] = ((DataTab) tabbedPane.getComponentAt(i)).getTable();
            }
            return tables;
        } else if (mode == SINGLE) {
            tables = new JTable[1];
            for (int ii = 0; ii < panel.getComponentCount(); ii++) {
                Component component = panel.getComponent(ii);
                if (component instanceof DataTab) {
                    tables[0] = ((DataTab) component).getTable();
                    return tables;
                }
            }
        }
        return null;
    }

    private DataTab getDataTab() {
        DataTab ret = null;
        if (mode == TABBED) {
            ret = (DataTab) tabbedPane.getSelectedComponent();
        } else if (mode == SINGLE) {
            for (int ii = 0; ii < panel.getComponentCount(); ii++) {
                Component component = panel.getComponent(ii);
                if (component instanceof DataTab) {
                    ret = (DataTab) component;
                    break;
                }
            }
        }
        return ret;
    }

    private String[] getNames() {
        String[] names = null;
        if (mode == TABBED) {
            names = new String[tabbedPane.getTabCount()];
            for (int i = 0; i < tabbedPane.getTabCount(); i++) {
                names[i] = ((DataTab) tabbedPane.getComponentAt(i))
                        .getDataName();
            }
            return names;
        } else if (mode == SINGLE) {
            names = new String[1];
            for (int ii = 0; ii < panel.getComponentCount(); ii++) {
                Component component = panel.getComponent(ii);
                if (component instanceof DataTab) {
                    names[0] = ((DataTab) component).getDataName();
                    return names;
                }
            }
        }
        return names;
    }

    private boolean doQuickPlot() {
        JTable table = null;
        int tabnum = 0;
        if (mode == TABBED) {
            tabnum = tabbedPane.getSelectedIndex();
            if (!(tabnum >= 0)) {
                return false;
            }
            table = ((DataTab) tabbedPane.getComponentAt(tabnum)).getTable();
        } else if (mode == SINGLE) {
            for (int ii = 0; ii < panel.getComponentCount(); ii++) {
                Component component = panel.getComponent(ii);
                if (component instanceof DataTab) {
                    table = ((DataTab) component).getTable();
                    break;
                }
            }
        }

        int numCols = table.getColumnCount();
        int numRows = table.getRowCount();
        int numIdent = numCols;

        int[] index_selected = table.getSelectedColumns();
        int numSelected = index_selected.length;

        int[] index_selected_rows;
        int numSelected_rows;

        if (table.getRowSelectionAllowed()) {
            index_selected_rows = table.getSelectedRows();
            numSelected_rows = index_selected_rows.length;
        } else {
            index_selected_rows = new int[numRows];
            for (int i = 0; i < numRows; i++) {
                index_selected_rows[i] = i;
            }
            numSelected_rows = numRows;
        }

        if (numSelected <= 1) {
            logger.error("select at least two columns!");
            return false;
        }

        if (numSelected_rows <= 0) {
            logger.error("select at least one row!");
            return false;
        }

        DataSet dset = new DataSet(outputData[tabnum].getName());
        String[] idents = outputData[tabnum].getIdentifier();

        if (idents == null || idents.length != numCols) {
            logger.warn("invalid identifiers. defaults are used.");
            idents[0] = "x";
            for (int i = 1; i < numCols; i++) {
                idents[i] = "y" + String.valueOf(i);
            }
        }

        String[] selectedIdents = new String[numSelected];
        for (int i = 0; i < numSelected; i++) {
            selectedIdents[i] = idents[index_selected[i]];
        }

        for (int i = 0; i < numSelected; i++) {
            String[] data = new String[numSelected_rows];
            for (int j = 0; j < numSelected_rows; j++) {
                data[j] = (String) table.getModel().getValueAt(
                        index_selected_rows[j], index_selected[i]);
            }
            dset.addData(data, selectedIdents[i]);
        }

        DataSetElement[] dse = dset.getData();
        DataSetElement[] dsey = new DataSetElement[dse.length - 1];
        for (int i = 1; i < dse.length; i++) {
            dsey[i - 1] = dse[i];
        }

        String axisx = selectedIdents[0];
        String axisy = selectedIdents[0];
        for (int i = 1; i < selectedIdents.length; i++) {
            axisy += ", " + selectedIdents[i];
        }

        int type = comboplottypes.getSelectedIndex();
        PlotProperties props = new PlotProperties();
        if (type == 0) {
            props.setPlotType(PlotProperties.XYPLOT);
        } else if (type == 1) {
            props.setPlotType(PlotProperties.PIEPLOT);
        }

        props.setTitle(outputData[tabnum].getName());
        DataSetCollection coll = new DataSetCollection(
                outputData[tabnum].getName());
        coll.initData();
        coll.setRangeAxisName(0, axisy);
        coll.setDomainAxisName(0, axisx);
        coll.setDomain(0, dse[0]);
        for (int i = 0; i < dsey.length; i++) {
            coll.addRange(0, dsey[i]);
        }
        SubPlotProperties spp = new SubPlotProperties(coll);
        props.addSubPlotProperties(spp);
        String title = outputData[tabnum].getName().replaceAll("\\s", "_");
        String dir = new File(outputData[tabnum].getFileName()).getParent();
        GraphPanel gpanel = new GraphPanel(title, props, dir);
        return true;
    }

}

/**
 * DataPanelの, 各タブのクラス.
 */
class DataTab extends JPanel {
    private OutputData data;

    private Logger logger = Logger.getLogger(DataTab.class.getName());

    private JTable table;

    /**
     * @param data
     *            このオブジェクトに格納された情報を元にタブを構築する.
     */
    protected DataTab(OutputData data) {
        this.data = data;
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        init();
    }

    private void init() {
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        if (data.getHeader() != null && data.getHeader().length() != 0) {
            panel.add(createHeaderPanel());
        }
        panel.add(createTable());
        add(panel);
    }

    private JPanel createHeaderPanel() {
        JScrollPane scr = new JScrollPane();
        JPanel p = new JPanel();
        p.setBorder(new TitledBorder("info"));
        p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));

        JTextArea textArea = new JTextArea();
        textArea.setEditable(false);
        textArea.setText(data.getHeader());
        scr.getViewport().setView(textArea);
        // textArea.setPreferredSize(new Dimension(500,400));
        p.add(scr);
        return p;
    }

    private JPanel createTable() {
        JPanel p = new JPanel();
        p.setBorder(new TitledBorder("data"));
        p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));

        JScrollPane scr = new JScrollPane();
        // scr.setSize(100,100);

        String[] ident = data.getIdentifier();
        String[][] data2d = data.getData2D();
        if (data2d == null || data2d.length == 0) {
            logger.debug("no data!");
            return p;
        }

        if (ident == null || ident.length == 0) {
            ident = new String[data2d[0].length];
            for (int i = 0; i < ident.length; i++) {
                ident[i] = "-";
            }
        }

        DefaultTableModel tableModel = new DefaultTableModel(data2d, ident);
        table = new JTable(tableModel);
        table.setRowSelectionAllowed(false);
        table.setColumnSelectionAllowed(true);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        table.setRowSelectionInterval(0, table.getRowCount() - 1);
        scr.getViewport().setView(table);
        p.add(scr);
        return p;
    }

    protected JTable getTable() {
        return table;
    }

    protected String getDataName() {
        return data.getName();
    }
}
