package org.gitools.ui.app.fileimport.wizard.text;

/*
 * #%L
 * gitools-ui-app
 * %%
 * Copyright (C) 2013 Biomedical Genomics Lab
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */

import org.gitools.ui.app.fileimport.wizard.text.reader.FlatTextImporter;
import org.gitools.ui.platform.dialog.MessageStatus;
import org.gitools.ui.platform.wizard.AbstractWizardPage;
import org.gitools.utils.readers.FileField;
import org.gitools.utils.readers.FileHeader;
import org.gitools.utils.readers.MatrixReaderProfile;
import org.gitools.utils.readers.profile.ReaderProfileValidationException;

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

public class SelectMatrixColumnsPage extends AbstractWizardPage implements IFileImportStep {

    private FlatTextImporter reader;

    private JPanel mainPanel;
    private JList<FileHeader> valuesHeaderList;
    private JList<FileHeader> ignoredHeaderList;
    private JTextPane previewPane;
    private JSpinner columnIdsSpinner;
    private JSpinner rowIdsSpinner;
    private JButton previewRowIdsButton;
    private JButton previewColumnIdsButton;
    private JFormattedTextField valueNameBox;
    private JLabel previewLabel;
    private List<FileHeader> allheaders;
    private List<List<FileField>> preview;
    private FileHeader lastSelected;
    private static String ID = "id";
    private static String COLUMN = "column";
    private String lastPreview;
    private FileHeader rowIdsHeader;
    private boolean rowIdsHeaderWasIgnored = true;

    public SelectMatrixColumnsPage() {

        rowIdsSpinner.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {

                updateRowIdHeader();

                updateControls();

                if (lastPreview == null) {
                    return;
                }

                if (lastPreview.equals(ID)) {
                    updateRowIdPreview();
                } else {
                    updateColumnPreview(lastSelected);
                }
            }
        });
        columnIdsSpinner.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {

                updateControls();

                if (lastPreview == null) {
                    return;
                }

                if (lastPreview.equals(ID)) {
                    updateColumnIdPreview();
                } else {
                    updateColumnPreview(lastSelected);
                }
            }
        });

        previewColumnIdsButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                updateColumnIdPreview();
            }
        });
        previewRowIdsButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                updateRowIdPreview();
            }
        });
        valueNameBox.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                updateControls();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                updateControls();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                updateControls();
            }
        });
    }

    private boolean correctValueName() {
        return !valueNameBox.getText().equals("") && !valueNameBox.getText().toLowerCase().equals("value")
                && !valueNameBox.getText().toLowerCase().equals("values");
    }

    private void updateRowIdHeader() {
        previewLabel.setText("Row ids preview");

        FileHeader newHeader = allheaders.get((int) rowIdsSpinner.getValue() - 1);
        FileHeader oldHeader = rowIdsHeader;

        if (oldHeader == null)
            return;

        //add old header where it belongs
        DefaultListModel restoreListModel;
        if (rowIdsHeaderWasIgnored) {
            restoreListModel = (DefaultListModel) ignoredHeaderList.getModel();
        } else {
            restoreListModel = (DefaultListModel) valuesHeaderList.getModel();
        }
        restoreListModel.add(0, oldHeader);


        //remove old header
        DefaultListModel removeListModel = (DefaultListModel) valuesHeaderList.getModel();
        if (removeListModel.contains(newHeader)) {
            rowIdsHeaderWasIgnored = false;
        } else {
            removeListModel = (DefaultListModel) ignoredHeaderList.getModel();
            rowIdsHeaderWasIgnored = true;
        }
        removeListModel.removeElement(newHeader);
        rowIdsHeader = newHeader;


    }

    private void initJList(JList<FileHeader> list, ListItemTransferHandler transferHandler, DefaultListModel<FileHeader> dataModel, boolean bold, boolean smallText) {

        list.setModel(dataModel);
        list.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        list.setDropMode(DropMode.INSERT);
        list.setDragEnabled(true);
        list.setTransferHandler(transferHandler);
        list.setCellRenderer(new HeaderCellRenderer(bold, smallText));

        // listener
        list.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if (!e.getValueIsAdjusting()) {
                    JList theList = (JList) e.getSource();

                    updateControls();

                    if (theList.getSelectedValue() == null)
                        return;

                    int i = theList.getLeadSelectionIndex();
                    updateColumnPreview((FileHeader) theList.getModel().getElementAt(i));
                }
            }
        });


        //Disable row Cut, Copy, Paste
        ActionMap map = list.getActionMap();
        AbstractAction dummy = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) { /* Dummy action */ }
        };
        map.put(TransferHandler.getCutAction().getValue(Action.NAME), dummy);
        map.put(TransferHandler.getCopyAction().getValue(Action.NAME), dummy);
        map.put(TransferHandler.getPasteAction().getValue(Action.NAME), dummy);
    }

    private void updateRowIdPreview() {


        int rowPos = (int) rowIdsSpinner.getModel().getValue() - 1;
        int colPos = (int) columnIdsSpinner.getModel().getValue() - 1;
        StringBuilder html = new StringBuilder("");

        html.append("<html>" +
                PreviewHTMLs.CSS +
                "<body><table>");
        html.append("<tr><th>Row Ids</th><tr>");
        for (List<FileField> line : preview) {
            FileField f = line.get(rowPos);
            if (f.getLine() > colPos) {
                html.append("<tr><td>" + f.getLabel() + "</td></tr>");
            }
        }
        html.append("</table></body></html>");

        previewPane.setText(html.toString());
        previewPane.setCaretPosition(0);
        lastPreview = this.ID;

    }

    private void updateColumnIdPreview() {

        previewLabel.setText("Column ids preview");

        int rowPos = (int) rowIdsSpinner.getModel().getValue() - 1;
        StringBuilder html = new StringBuilder("");

        html.append("<html>" +
                PreviewHTMLs.CSS +
                "<body><table>");
        html.append("<tr><th>Column Ids</th><tr>");
        for (int i = 0; i < valuesHeaderList.getModel().getSize(); i++) {
            html.append("<tr><td>" + valuesHeaderList.getModel().getElementAt(i).getLabel() + "</td></tr>");
        }

        html.append("</table></body></html>");

        previewPane.setText(html.toString());
        previewPane.setCaretPosition(0);
        lastPreview = this.ID;


    }

    private void updateColumnPreview(FileHeader selectedValue) {

        previewLabel.setText("Data column preview");


        if (selectedValue == null)
            return;

        StringBuilder html = new StringBuilder("");

        html.append("<html>" +
                PreviewHTMLs.CSS +
                "<body><table>");
        html.append("<tr><th>" + selectedValue.getLabel() + "</th><tr>");
        for (List<FileField> line : preview) {
            FileField f = line.get(selectedValue.getPos());
            if (f.getLine() < (int) rowIdsSpinner.getModel().getValue())
                continue;
            html.append("<tr><td>" + line.get(selectedValue.getPos()).getLabel() + "</td></tr>");
        }
        html.append("</table></body></html>");

        previewPane.setText(html.toString());
        previewPane.setCaretPosition(0);
        lastPreview = this.COLUMN;

    }

    @Override
    public JComponent createControls() {
        return mainPanel;
    }

    @Override
    public void updateControls() {
        setMessage(MessageStatus.INFO, "Drag and Drop the file headers to choose which columns to use for the heatmap");

        if ((int) rowIdsSpinner.getValue() < 1) {
            setMessage(MessageStatus.ERROR, "Heatmap row id must be a number bigger than 0");
            setComplete(false);
            return;
        } else if ((int) columnIdsSpinner.getValue() < 1) {
            setMessage(MessageStatus.ERROR, "Heatmap column id must be a number bigger than 0");
            setComplete(false);
            return;
        } else if (valuesHeaderList.getModel().getSize() == 0) {
            setMessage(MessageStatus.ERROR, "At least one column for heatmap values needed");
            setComplete(false);
            return;
        }

        if (!correctValueName()) {
            setMessage(MessageStatus.ERROR, "Please specify a name describing the data values (e.g. expression)");
            setComplete(false);
            return;
        }

        setComplete(true);
    }

    private void fillComponents() {
        if (allheaders == null) {
            this.preview = reader.getPreview();
            this.allheaders = reader.getFileHeaders();

            rowIdsSpinner.setValue(1);
            columnIdsSpinner.setValue(1);

            DefaultListModel<FileHeader> values = new DefaultListModel<FileHeader>();
            DefaultListModel<FileHeader> ignored = new DefaultListModel<FileHeader>();

            for (FileHeader header : allheaders) {
                if (header.getPos() != (int) rowIdsSpinner.getValue() - 1) {
                    values.addElement(header);
                } else {
                    rowIdsHeader = header;
                    rowIdsHeaderWasIgnored = true;
                }
            }

            ListItemTransferHandler transferHandler = new ListItemTransferHandler();

            initJList(valuesHeaderList, transferHandler, values, false, false);
            initJList(ignoredHeaderList, transferHandler, ignored, false, true);
        }
    }

    @Override
    public void finish() throws ReaderProfileValidationException {

        MatrixReaderProfile profile = (MatrixReaderProfile) reader.getReaderProfile();

        profile.setRowIdsPosition((int) rowIdsSpinner.getValue() - 1);
        profile.setColumnIdsPosition((int) columnIdsSpinner.getValue() - 1);
        profile.setValueColumns(getPositions(valuesHeaderList));
        profile.setIgnoredColumns(getPositions(ignoredHeaderList));
        profile.setDataName(valueNameBox.getText());
        reader.getReaderProfile().validate(allheaders);
    }

    private int[] getPositions(JList<FileHeader> headerList) {
        int[] indices = new int[headerList.getModel().getSize()];
        for (int i = 0; i < indices.length; i++) {
            indices[i] = headerList.getModel().getElementAt(i).getPos();
        }
        return indices;
    }

    @Override
    public FlatTextImporter getReader() {
        return reader;
    }

    public void setReader(FlatTextImporter reader) {
        this.reader = reader;
        fillComponents();
    }


    class HeaderCellRenderer extends DefaultListCellRenderer {


        private boolean bold;
        private boolean smallFont;

        HeaderCellRenderer(boolean bold, boolean smallFont) {
            this.bold = bold;
            this.smallFont = smallFont;
        }

        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {

            FileHeader h = (FileHeader) value;
            String newValue = h.getLabel() + " (column " + (h.getPos() + 1) + ")";

            Component c = super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus);
            if (bold) {
                c.setFont(c.getFont().deriveFont(Font.BOLD));
            }

            if (smallFont) {
                c.setFont(c.getFont().deriveFont(11f));
            } else {
                c.setFont(c.getFont().deriveFont(14f));
            }
            return c;
        }
    }


}
