/*
 * File:     ExportTabDialog2.java
 * Project:  MPI Linguistic Application
 * Date:     03 April 2006
 *
 * Copyright (C) 2001-2006  Max Planck Institute for Psycholinguistics
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package mpi.eudico.client.annotator.export;

import mpi.eudico.client.annotator.ElanLocale;
import mpi.eudico.client.annotator.Preferences;
import mpi.eudico.client.annotator.Selection;

import mpi.eudico.client.annotator.gui.TextExportFileChooser;

import mpi.eudico.client.annotator.util.ElanFileFilter;
import mpi.eudico.client.annotator.util.FileExtension;

import mpi.eudico.client.util.CheckBoxTableCellRenderer;
import mpi.eudico.client.util.Transcription2TabDelimitedText;

import mpi.eudico.server.corpora.clom.Transcription;

import mpi.eudico.server.corpora.clomimpl.abstr.TierImpl;

import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.io.File;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Vector;

import javax.swing.ButtonGroup;
import javax.swing.DefaultCellEditor;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;


/**
 * A dialog for exporting a set of tiers to a tab delimited text file. Provides
 * ui elements to customize the output.
 *
 * @author Han Sloetjes
 */
public class ExportTabDialog2 extends AbstractTierExportDialog
    implements ActionListener, ListSelectionListener, ChangeListener {
    private Transcription transcription;
    private Selection selection;
    private JButton upButton;
    private JButton downButton;
    private JCheckBox restrictCheckBox;
    private JLabel timeCodesLabel;
    private JLabel timeFormatLabel;
    private JCheckBox btCheckBox;
    private JCheckBox etCheckBox;
    private JCheckBox durCheckBox;
    private JCheckBox hhmmssmsCheckBox;
    private JCheckBox ssmsCheckBox;
    private JCheckBox msCheckBox;
    private JCheckBox timecodeCB;
    private JRadioButton palTimecodeRB;
    private JRadioButton ntscTimecodeRB;

    /**
     * Creates a new ExportTabDialog2 instance
     *
     * @param parent the parent frame
     * @param modal the modal property
     * @param transcription the transcription to export
     * @param selection the selection object
     */
    public ExportTabDialog2(Frame parent, boolean modal,
        Transcription transcription, Selection selection) {
        super(parent, modal);
        this.transcription = transcription;
        this.selection = selection;
        this.initComponents();
        extractTiers();
        postInit();
    }

    /**
     * Initializes UI elements.
     */
    protected void initComponents() {
        super.initComponents();

        // add more 
        upButton = new JButton();
        downButton = new JButton();
        restrictCheckBox = new JCheckBox();

        timeCodesLabel = new JLabel();
        timeFormatLabel = new JLabel();
        btCheckBox = new JCheckBox();
        etCheckBox = new JCheckBox();
        durCheckBox = new JCheckBox();
        hhmmssmsCheckBox = new JCheckBox();
        ssmsCheckBox = new JCheckBox();
        msCheckBox = new JCheckBox();
        timecodeCB = new JCheckBox();
        palTimecodeRB = new JRadioButton();
        ntscTimecodeRB = new JRadioButton();

        ButtonGroup group = new ButtonGroup();

        try {
            ImageIcon upIcon = new ImageIcon(this.getClass().getResource("/toolbarButtonGraphics/navigation/Up16.gif"));
            ImageIcon downIcon = new ImageIcon(this.getClass().getResource("/toolbarButtonGraphics/navigation/Down16.gif"));
            upButton.setIcon(upIcon);
            downButton.setIcon(downIcon);
        } catch (Exception ex) {
            upButton.setText("Up");
            downButton.setText("Down");
        }

        model.setColumnIdentifiers(new String[] { EXPORT_COLUMN, TIER_NAME_COLUMN });
        tierTable.getColumn(EXPORT_COLUMN).setCellEditor(new DefaultCellEditor(
                new JCheckBox()));
        tierTable.getColumn(EXPORT_COLUMN).setCellRenderer(new CheckBoxTableCellRenderer());
        tierTable.getColumn(EXPORT_COLUMN).setMaxWidth(30);
        tierTable.setShowVerticalLines(false);
        tierTable.setTableHeader(null);
        tierTable.getSelectionModel().addListSelectionListener(this);

        upButton.addActionListener(this);
        downButton.addActionListener(this);

        GridBagConstraints gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        tierSelectionPanel.add(upButton, gridBagConstraints);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        tierSelectionPanel.add(downButton, gridBagConstraints);

        // options
        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(restrictCheckBox, gridBagConstraints);

        JPanel fill = new JPanel();
        Dimension fillDim = new Dimension(30, 10);
        fill.setPreferredSize(fillDim);
        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(fill, gridBagConstraints);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.gridwidth = 1;
        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(timeCodesLabel, gridBagConstraints);

        JPanel filler = new JPanel();
        filler.setPreferredSize(fillDim);
        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(filler, gridBagConstraints);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(timeFormatLabel, gridBagConstraints);

        btCheckBox.setSelected(true);
        etCheckBox.setSelected(true);
        durCheckBox.setSelected(true);
        hhmmssmsCheckBox.setSelected(true);
        ssmsCheckBox.setSelected(true);
        msCheckBox.setSelected(false);
        timecodeCB.setSelected(false);
        group.add(palTimecodeRB);
        group.add(ntscTimecodeRB);
        palTimecodeRB.setSelected(true);
        palTimecodeRB.setEnabled(false);
        ntscTimecodeRB.setEnabled(false);
        timecodeCB.addChangeListener(this);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.gridwidth = 1;
        gridBagConstraints.fill = GridBagConstraints.NONE;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(btCheckBox, gridBagConstraints);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.fill = GridBagConstraints.NONE;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(hhmmssmsCheckBox, gridBagConstraints);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 4;
        gridBagConstraints.gridwidth = 1;
        gridBagConstraints.fill = GridBagConstraints.NONE;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(etCheckBox, gridBagConstraints);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 4;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.fill = GridBagConstraints.NONE;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(ssmsCheckBox, gridBagConstraints);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 5;
        gridBagConstraints.gridwidth = 1;
        gridBagConstraints.fill = GridBagConstraints.NONE;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(durCheckBox, gridBagConstraints);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 5;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.fill = GridBagConstraints.NONE;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(msCheckBox, gridBagConstraints);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 6;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.fill = GridBagConstraints.NONE;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(timecodeCB, gridBagConstraints);

        JPanel smpteFill = new JPanel();
        smpteFill.setPreferredSize(new Dimension(10, 20));
        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 7;
        gridBagConstraints.gridwidth = 1;
        gridBagConstraints.gridheight = 2;
        gridBagConstraints.fill = GridBagConstraints.NONE;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.weightx = 0.0;
        optionsPanel.add(smpteFill, gridBagConstraints);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 7;
        gridBagConstraints.gridwidth = 1;
        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 10.0;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(palTimecodeRB, gridBagConstraints);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 8;
        gridBagConstraints.gridwidth = 1;
        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 10.0;
        gridBagConstraints.anchor = GridBagConstraints.WEST;
        gridBagConstraints.insets = insets;
        optionsPanel.add(ntscTimecodeRB, gridBagConstraints);

        updateLocale();
    }

    /**
     * Extract candidate tiers for export.
     */
    protected void extractTiers() {
        if (model != null) {
            for (int i = model.getRowCount() - 1; i >= 0; i--) {
                model.removeRow(i);
            }

            if (transcription != null) {
                Vector v = transcription.getTiers();
                TierImpl t;

                for (int i = 0; i < v.size(); i++) {
                    t = (TierImpl) v.get(i);

                    // add all
                    if (i == 0) {
                        model.addRow(new Object[] { Boolean.TRUE, t.getName() });
                    } else {
                        model.addRow(new Object[] { Boolean.FALSE, t.getName() });
                    }
                }
            }

            if (model.getRowCount() > 1) {
                upButton.setEnabled(true);
                downButton.setEnabled(true);
            } else {
                upButton.setEnabled(false);
                downButton.setEnabled(false);
            }
        } else {
            upButton.setEnabled(false);
            downButton.setEnabled(false);
        }
    }

    /**
     * Pack, size and set location.
     */
    protected void postInit() {
        pack();

        int w = 550;
        int h = 400;
        setSize((getSize().width < w) ? w : getSize().width,
            (getSize().height < h) ? h : getSize().height);
        setLocationRelativeTo(getParent());

        //setResizable(false);
    }

    /**
     * Set the localized text on ui elements.
     *
     * @see mpi.eudico.client.annotator.export.AbstractTierExportDialog#updateLocale()
     */
    protected void updateLocale() {
        setTitle(ElanLocale.getString("ExportTabDialog.Title"));
        titleLabel.setText(ElanLocale.getString("ExportTabDialog.TitleLabel"));
        tierSelectionPanel.setBorder(new TitledBorder(ElanLocale.getString(
                    "ExportDialog.Label.SelectTiers")));
        optionsPanel.setBorder(new TitledBorder(ElanLocale.getString(
                    "ExportDialog.Label.Options")));
        restrictCheckBox.setText(ElanLocale.getString("ExportDialog.Restrict"));
        timeCodesLabel.setText(ElanLocale.getString(
                "ExportTabDialog.Label.Columns"));
        timeFormatLabel.setText(ElanLocale.getString(
                "ExportTabDialog.Label.Formats"));
        btCheckBox.setText(ElanLocale.getString(
                "Frame.GridFrame.ColumnBeginTime"));
        etCheckBox.setText(ElanLocale.getString("Frame.GridFrame.ColumnEndTime"));
        durCheckBox.setText(ElanLocale.getString(
                "Frame.GridFrame.ColumnDuration"));
        hhmmssmsCheckBox.setText(ElanLocale.getString("TimeCodeFormat.TimeCode"));
        ssmsCheckBox.setText(ElanLocale.getString("TimeCodeFormat.Seconds"));
        msCheckBox.setText(ElanLocale.getString("TimeCodeFormat.MilliSec"));
        timecodeCB.setText(ElanLocale.getString("TimeCodeFormat.TimeCode.SMPTE"));
        ntscTimecodeRB.setText(ElanLocale.getString(
                "TimeCodeFormat.TimeCode.SMPTE.NTSC"));
        palTimecodeRB.setText(ElanLocale.getString(
                "TimeCodeFormat.TimeCode.SMPTE.PAL"));

        startButton.setText(ElanLocale.getString("Button.OK"));
        closeButton.setText(ElanLocale.getString("Button.Close"));
    }

    /**
     * Starts the actual exporting process.
     *
     * @return true if export succeeded
     *
     * @throws IOException can occur when writing to the file
     * @throws NullPointerException DOCUMENT ME!
     */
    protected boolean startExport() throws IOException {
        if ((model == null) || (tierTable == null)) {
            throw new NullPointerException("The tier table is null");
        }

        int includeCol = model.findColumn(EXPORT_COLUMN);
        int nameCol = model.findColumn(TIER_NAME_COLUMN);

        // check if there is anything to export
        boolean atLeastOneTier = false;

        for (int i = 0; i < model.getRowCount(); i++) {
            Boolean include = (Boolean) model.getValueAt(i, includeCol);

            if (include.booleanValue()) {
                atLeastOneTier = true;

                break;
            }
        }

        if (!atLeastOneTier) {
            JOptionPane.showMessageDialog(this,
                ElanLocale.getString("ExportTradTranscript.Message.NoTiers"),
                ElanLocale.getString("Message.Warning"),
                JOptionPane.WARNING_MESSAGE);

            return false;
        }

        // prompt for file name and location
        String fileName = promptForFileName(ElanLocale.getString(
                    "ExportTabDialog.Title"));

        if (fileName == null) {
            throw new IOException("No valid output file specified");
        }

        // export....
        ArrayList exportTiers = new ArrayList();

        // add selected tiers in the right order
        for (int i = 0; i < model.getRowCount(); i++) {
            Boolean include = (Boolean) model.getValueAt(i, includeCol);

            if (include.booleanValue()) {
                exportTiers.add(model.getValueAt(i, nameCol));
            }
        }

        String[] tierNames = (String[]) exportTiers.toArray(new String[] {  });

        long selectionBT = 0L;
        long selectionET = Long.MAX_VALUE;

        if (restrictCheckBox.isSelected()) {
            selectionBT = selection.getBeginTime();
            selectionET = selection.getEndTime();
        }

        Transcription2TabDelimitedText.exportTiers(transcription, tierNames,
            new File(fileName), encoding, selectionBT, selectionET,
            btCheckBox.isSelected(), etCheckBox.isSelected(),
            durCheckBox.isSelected(), hhmmssmsCheckBox.isSelected(),
            ssmsCheckBox.isSelected(), msCheckBox.isSelected(),
            timecodeCB.isSelected(), palTimecodeRB.isSelected());

        return true;
    }

    /**
     * Moves selected tiers up in the list of tiers.
     */
    private void moveUp() {
        if ((tierTable == null) || (model == null) ||
                (model.getRowCount() < 2)) {
            return;
        }

        int[] selected = tierTable.getSelectedRows();

        for (int i = 0; i < selected.length; i++) {
            int row = selected[i];

            if ((row > 0) && !tierTable.isRowSelected(row - 1)) {
                model.moveRow(row, row, row - 1);
                tierTable.changeSelection(row, 0, true, false);
                tierTable.changeSelection(row - 1, 0, true, false);
            }
        }
    }

    /**
     * Moves selected tiers up in the list of tiers.
     */
    private void moveDown() {
        if ((tierTable == null) || (model == null) ||
                (model.getRowCount() < 2)) {
            return;
        }

        int[] selected = tierTable.getSelectedRows();

        for (int i = selected.length - 1; i >= 0; i--) {
            int row = selected[i];

            if ((row < (model.getRowCount() - 1)) &&
                    !tierTable.isRowSelected(row + 1)) {
                model.moveRow(row, row, row + 1);
                tierTable.changeSelection(row, 0, true, false);
                tierTable.changeSelection(row + 1, 0, true, false);
            }
        }
    }

    /**
     * The action performed event handling.
     *
     * @param ae the action event
     */
    public void actionPerformed(ActionEvent ae) {
        Object source = ae.getSource();

        if (source == upButton) {
            moveUp();
        } else if (source == downButton) {
            moveDown();
        } else {
            super.actionPerformed(ae);
        }
    }

    /**
     * Updates the checked state of the export checkboxes.
     *
     * @param lse the list selection event
     */
    public void valueChanged(ListSelectionEvent lse) {
        if ((model != null) && lse.getValueIsAdjusting()) {
            int b = lse.getFirstIndex();
            int e = lse.getLastIndex();
            int col = model.findColumn(EXPORT_COLUMN);

            for (int i = b; i <= e; i++) {
                if (tierTable.isRowSelected(i)) {
                    model.setValueAt(Boolean.TRUE, i, col);
                }
            }
        }
    }

    /**
     * Enables / disables PAL and NTSC radio buttons.
     * @param ce change event
     */
    public void stateChanged(ChangeEvent ce) {
        palTimecodeRB.setEnabled(timecodeCB.isSelected());
        ntscTimecodeRB.setEnabled(timecodeCB.isSelected());
    }
}
