/***********************************************************************

	This file is part of KEEL-software, the Data Mining tool for regression, 
	classification, clustering, pattern mining and so on.

	Copyright (C) 2004-2010
	
	F. Herrera (herrera@decsai.ugr.es)
    L. Sánchez (luciano@uniovi.es)
    J. Alcalá-Fdez (jalcala@decsai.ugr.es)
    S. García (sglopez@ujaen.es)
    A. Fernández (alberto.fernandez@ujaen.es)
    J. Luengo (julianlm@decsai.ugr.es)

	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/
  
**********************************************************************/

/**
 * <p>
 * @author Written by Pedro Antonio GutiÃ©rrez and Juan Carlos FernÃ¡ndez (University of CÃ³rdoba) 23/10/2008
 * @version 1.0
 * @since JDK1.5
 * </p>
 */
package keel.GraphInterKeel.datacf.partitionData;

import java.io.IOException;
import keel.GraphInterKeel.datacf.util.FileBrowserPanel;
import keel.GraphInterKeel.datacf.util.KeelFileFilter;
import keel.GraphInterKeel.util.Path;
import java.awt.Cursor;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Iterator;
import java.util.List;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

public class PartitionPanel extends javax.swing.JPanel {

    /**
     * <p>
     * Panel for performing partitions of datasets
     * </p>
     */

    /**
     * <p>
     * Constructor that initializes the panel
     * </p>
     */
    public PartitionPanel() {
        initComponents();
    }


    /**
     * <p>
     * This method is called from within the constructor to
     * initialize the form.
     * </p>
     * <p>
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     * </p>
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        fileBrowserPanel = new keel.GraphInterKeel.datacf.util.FileBrowserPanel();
        KeelFileFilter fileFilter = new KeelFileFilter();
        fileFilter.addExtension("dat");
        fileFilter.setFilterName("KEEL Files (.dat)");
        fileBrowserPanel.getFileChooser().setFileFilter(fileFilter);
        //chooser.showOpenDialog(this);
        typeofPartitionjPanel = new javax.swing.JPanel();
        typejLabel = new javax.swing.JLabel();
        typejComboBox = new javax.swing.JComboBox();
        seedjLabel = new javax.swing.JLabel();
        seedjTextField = new javax.swing.JTextField();
        optionsjButton = new javax.swing.JButton();
        dividejButton = new javax.swing.JButton();

        setName("Form"); // NOI18N

        fileBrowserPanel.setName("fileBrowserPanel"); // NOI18N

        typeofPartitionjPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Type of Partition"));
        typeofPartitionjPanel.setName("typeofPartitionjPanel"); // NOI18N

        typejLabel.setText("Type of Partition");
        typejLabel.setName("typejLabel"); // NOI18N

        typejComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "K-Fold Cross Validation", "5x2 Cross Validation", "Hold-Out" }));
        typejComboBox.setToolTipText("Type of Partitioning Approach to Apply");
        typejComboBox.setName("typejComboBox"); // NOI18N
        typejComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                typejComboBoxActionPerformed(evt);
            }
        });

        seedjLabel.setText("Seed");
        seedjLabel.setName("seedjLabel"); // NOI18N

        seedjTextField.setText("12345678");
        seedjTextField.setToolTipText("Random Generator Seed for Performing the Partitions");
        seedjTextField.setName("seedjTextField"); // NOI18N

        optionsjButton.setText("Options");
        optionsjButton.setToolTipText("Options of the partitioning algorithm");
        optionsjButton.setName("optionsjButton"); // NOI18N
        optionsjButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                optionsjButtonActionPerformed(evt);
            }
        });

        dividejButton.setText("Divide");
        dividejButton.setToolTipText("Make partitions of your selected dataset");
        dividejButton.setName("dividejButton"); // NOI18N
        dividejButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                dividejButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout typeofPartitionjPanelLayout = new javax.swing.GroupLayout(typeofPartitionjPanel);
        typeofPartitionjPanel.setLayout(typeofPartitionjPanelLayout);
        typeofPartitionjPanelLayout.setHorizontalGroup(
            typeofPartitionjPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, typeofPartitionjPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(typeofPartitionjPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(seedjLabel)
                    .addComponent(typejLabel))
                .addGap(34, 34, 34)
                .addGroup(typeofPartitionjPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(typejComboBox, 0, 171, Short.MAX_VALUE)
                    .addComponent(seedjTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 171, Short.MAX_VALUE))
                .addGap(90, 90, 90)
                .addGroup(typeofPartitionjPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(dividejButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(optionsjButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addGap(28, 28, 28))
        );
        typeofPartitionjPanelLayout.setVerticalGroup(
            typeofPartitionjPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(typeofPartitionjPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(typeofPartitionjPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(typejLabel)
                    .addComponent(optionsjButton)
                    .addComponent(typejComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(18, 18, 18)
                .addGroup(typeofPartitionjPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(seedjLabel)
                    .addComponent(seedjTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(dividejButton))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(fileBrowserPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 494, Short.MAX_VALUE)
            .addComponent(typeofPartitionjPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(fileBrowserPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 395, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(typeofPartitionjPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        );
    }// </editor-fold>//GEN-END:initComponents

private void optionsjButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_optionsjButtonActionPerformed
    //If K-Fold Cross Validation is selected
    if (this.typejComboBox.getSelectedItem() == this.typejComboBox.getItemAt(0)) {
        kfoldOptions.setVisible(true);
        //Hold-out
    } else if (this.typejComboBox.getSelectedItem() == this.typejComboBox.getItemAt(2)) {
        holdOutOptions.setVisible(true);
    }
}//GEN-LAST:event_optionsjButtonActionPerformed

private void typejComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_typejComboBoxActionPerformed
    if (this.typejComboBox.getSelectedItem() == this.typejComboBox.getItemAt(1)) {
        this.optionsjButton.setEnabled(false);
    } else {
        this.optionsjButton.setEnabled(true);
        //If K-Fold Cross Validation is selected
    }
    if (this.typejComboBox.getSelectedItem() == this.typejComboBox.getItemAt(0)) {
        kfoldOptions = new KFoldOptionsJDialog(parent, true);
        if (holdOutOptions != null) {
            holdOutOptions = null;
        }
    } //Hold-out
    else if (this.typejComboBox.getSelectedItem() == this.typejComboBox.getItemAt(2)) {
        holdOutOptions = new HoldOutOptionsJDialog(parent, true);
        if (kfoldOptions != null) {
            kfoldOptions = null;
        }
    }

}//GEN-LAST:event_typejComboBoxActionPerformed

private void dividejButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dividejButtonActionPerformed
    File originalFile = null;

    originalFile = fileBrowserPanel.getFileChooser().getSelectedFile();
    if (originalFile == null) {
        JOptionPane.showMessageDialog(this, "You have to select a file", "Error", JOptionPane.ERROR_MESSAGE);
        return;
    }


    Path.setFilePath(fileBrowserPanel.getFileChooser().getCurrentDirectory());

    int type = -1, nOfPartitions = 0, totalFractions = 0;
    if (((String) typejComboBox.getSelectedItem()).equals("K-Fold Cross Validation")) {
        type = 1;
        nOfPartitions = kfoldOptions.getK();
    } else if (((String) typejComboBox.getSelectedItem()).equals("5x2 Cross Validation")) {
        type = 3;
    } else if (((String) typejComboBox.getSelectedItem()).equals("Hold-Out")) {
        type = 2;
        nOfPartitions = holdOutOptions.getNOfPartitions();
        totalFractions = holdOutOptions.getTotalFractions();
    } else {
        return;
    }
    long seed;

    try {
        seed = Long.parseLong((String) seedjTextField.getText());
    } catch (Exception ex) {
        JOptionPane.showMessageDialog(this, "Insert a correct seed, please",
                "Error", 2);
        return;
    }

    File posibleExperimentFile =
            new File("." + File.separator + "data" + File.separator + originalFile.getName().replaceAll(".dat", "") + File.separator + originalFile.getName());

    boolean experimentDataset = false;
    try {
        if (posibleExperimentFile != null && posibleExperimentFile.exists() && originalFile.getCanonicalPath().equals(posibleExperimentFile.getCanonicalPath())) {
            experimentDataset = true;
        }
    } catch (IOException ex) {
    }
    String outputPath = null;
    if (!experimentDataset) {
        JFileChooser chooser = new JFileChooser();
        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        chooser.setDialogTitle("Select a folder to save the files");
        chooser.setCurrentDirectory(Path.getFilePath());

        String name = originalFile.getName();
        int index = name.indexOf("-");
        if (index == -1) {
            index = name.lastIndexOf(".");
        }
        if (index == -1) {
            index = name.length();
        }
        name = name.substring(0, index);
        String finalName = name.concat("-" + ((type == 1) ? (nOfPartitions + "Fold") : ((type == 2) ? "HoldOut" : "5x2")));
        chooser.setSelectedFile(new File(Path.getFilePath() + File.separator + finalName));
        int returnVal = chooser.showSaveDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            Path.setFilePath(chooser.getCurrentDirectory());

            File directory = new File(chooser.getSelectedFile().getPath());
            if (!directory.exists()) {
                if (!directory.mkdirs()) {
                    JOptionPane.showMessageDialog(this, "Problem Creating Directory", "Error", JOptionPane.ERROR_MESSAGE);
                    return;
                }
            }
            outputPath = directory.getPath();
        }
    } else {
        JOptionPane.showMessageDialog(this, "This dataset is included in the experiment section\n The partitions will be generated in the corresponding folder", "Error", JOptionPane.INFORMATION_MESSAGE);
        outputPath = originalFile.getPath().substring(0, originalFile.getPath().lastIndexOf(File.separator));
    }

    if (outputPath != null) {
        // ---------------------
        // K-fold partition type
        if (type == 1) {
            for (int i = 0; i < nOfPartitions; i++) {
                String nombre_base = originalFile.getName().replaceAll(".dat", "");
                File trainingFile = new File(outputPath + File.separator + nombre_base + "-" +
                        String.valueOf(nOfPartitions) + "-" + String.valueOf(i + 1) +
                        "tra.dat");
                System.out.println(trainingFile.getAbsolutePath());
                File testFile = new File(outputPath + File.separator + nombre_base + "-" +
                        String.valueOf(nOfPartitions) + "-" + String.valueOf(i + 1) +
                        "tst.dat");
                if (!wantToBeOverwritten(trainingFile) || !wantToBeOverwritten(testFile)) {
                    return;
                }
            }
        }
        // ----------------------
        // Holdout partition type
        else if (type == 2) {
            for (int i = 0; i < nOfPartitions; i++) {
                String nombre_base = originalFile.getName().replaceAll(".dat", "");
                File trainingFile = new File(outputPath + File.separator + nombre_base + "-" +
                        String.valueOf(totalFractions) + "-" + String.valueOf(i + 1) +
                        "tra.dat");
                File testFile = new File(outputPath + File.separator + nombre_base + "-" +
                        String.valueOf(totalFractions) + "-" + String.valueOf(i + 1) +
                        "tst.dat");
                if (!wantToBeOverwritten(trainingFile) || !wantToBeOverwritten(testFile)) {
                    return;
                }
            }

        }
        // -----------------------------
        // 5x2 Dietterich partition type
        else {
            for (int i = 0; i < 5; i++) {
                String nombre_base = originalFile.getName().replaceAll(".dat", "");
                File trainingFile = new File(outputPath + File.separator + nombre_base + "-5x2-" +
                        String.valueOf(i + 1) + "tra.dat");
                File testFile = new File(outputPath + File.separator + nombre_base + "-5x2-" +
                        String.valueOf(i + 1) + "tst.dat");
                if (!wantToBeOverwritten(trainingFile) || !wantToBeOverwritten(testFile)) {
                    return;
                }
            }
        }

        try {
            this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            PartitionGenerator partitionGenerator = new PartitionGenerator();
            partitionGenerator.partition(type, originalFile.getPath(), outputPath, seed, nOfPartitions, totalFractions);
            this.setCursor(Cursor.getDefaultCursor());
            JOptionPane.showMessageDialog(this, "Your partitions have been correctly generated!!", "Partitions Generated!!", JOptionPane.INFORMATION_MESSAGE);
        } catch (Exception ex) {
            JOptionPane.showMessageDialog(this, "Problem Creating Partitions", "Error", JOptionPane.ERROR_MESSAGE);
            this.setCursor(Cursor.getDefaultCursor());
            return;
        }


        // If the dataset is the main file of a dataset of the Experiment section
        if (experimentDataset) {
            /*Load the previuos datasets.xml*/
            Document data = new Document();
            try {
                SAXBuilder builder = new SAXBuilder();
                data = builder.build("./data/Datasets.xml");
            } catch (JDOMException ex) {
                JOptionPane.showMessageDialog(this, "Problem Reading ./data/Datasets.xml:\n" + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
                return;
            } catch (Exception ex) {
                JOptionPane.showMessageDialog(this, "Problem Reading ./data/Datasets.xml:\n" + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
                return;
            }

            String rootName = originalFile.getName().replaceAll(".dat", "");
            Iterator it = data.getRootElement().getChildren().iterator();
            while (it.hasNext()) {
                Element element = (Element) it.next();
                if (element.getChild("nameAbr").getValue().equals(rootName)) {
                    // Holdout
                    if (type == 1) {
                        List<Element> list = element.getChild("partitions").getChildren("partition");

                        boolean founded = false;
                        for (Element e : list) {
                            if (e.getText().equals(nOfPartitions + "cfv")) {
                                founded = true;
                            }
                        }
                        if (!founded) {
                            Element partition = new Element("partition");
                            partition.setText(nOfPartitions + "cfv");
                            element.getChild("partitions").addContent(partition);
                        }
                    } else if (type == 2) {
                        List<Element> list = element.getChild("partitions").getChildren("partition");

                        boolean founded = false;
                        for (Element e : list) {
                            if (e.getText().equals(totalFractions + "ho")) {
                                founded = true;
                            }
                        }
                        if (!founded) {
                            Element partition = new Element("partition");
                            partition.setText(totalFractions + "ho");
                            element.getChild("partitions").addContent(partition);
                        }
                    } else {
                        List<Element> list = element.getChild("partitions").getChildren("partition");

                        boolean founded = false;
                        for (Element e : list) {
                            if (e.getText().equals(nOfPartitions + "ho")) {
                                founded = true;
                            }
                        }

                        if (!founded) {
                            Element partition = new Element("partition");
                            partition.setText("5x2cv");
                            element.getChild("partitions").addContent(partition);
                        }
                    }
                }
            }

            try {
                File f = new File("./data/Datasets.xml");
                FileOutputStream file2 = new FileOutputStream(f);
                XMLOutputter fmt = new XMLOutputter();
                fmt.setFormat(Format.getPrettyFormat());
                fmt.output(data, file2);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
}//GEN-LAST:event_dividejButtonActionPerformed
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton dividejButton;
    private keel.GraphInterKeel.datacf.util.FileBrowserPanel fileBrowserPanel;
    private javax.swing.JButton optionsjButton;
    private javax.swing.JLabel seedjLabel;
    private javax.swing.JTextField seedjTextField;
    private javax.swing.JComboBox typejComboBox;
    private javax.swing.JLabel typejLabel;
    private javax.swing.JPanel typeofPartitionjPanel;
    // End of variables declaration//GEN-END:variables

    /** Parent frame */
    protected JFrame parent = null;

    /** An option dialog for obtaining the options of the holdout partition proccess */
    protected HoldOutOptionsJDialog holdOutOptions = null;
    
    /** An option dialog for obtaining the options of the k-fold partition proccess */
    protected KFoldOptionsJDialog kfoldOptions = new KFoldOptionsJDialog(parent, true);

    /**
     * <p>
     * Gets the file browser from this partition panel
     * </p>
     * @return FileBrowserPanel of this partition panel
     */
    public FileBrowserPanel getFileBrowserPanel() {
        return fileBrowserPanel;
    }

    /**
     * <p>
     * Sets the filebrowser of this partition panel
     * </p>
     * @param fileBrowserPanel New FileBrowserPanel for this partition panel
     */
    public void setFileBrowserPanel(FileBrowserPanel fileBrowserPanel) {
        this.fileBrowserPanel = fileBrowserPanel;
    }

    /**
     * <p>
     * Sets the JFrame parent
     * </p>
     * @param parent JFrame parent
     */
    public void setParent(JFrame parent) {
        this.parent = parent;
    }

    /**
     * <p>
     * Shows an Option Dialog for confirming if a file should be overwritten
     * (if the file exists)
     * </p>
     * @param file File object to check if it should be overwritten
     * @return boolean This value indicates if the file should be overwritten
     */
    private boolean wantToBeOverwritten(File file) {
        // Check if the file exists
        if (file.exists()) {
            // Do you want to overwrite the file?
            Object[] options = {"Yes",
                "No"
            };
            int n = JOptionPane.showOptionDialog(parent,
                    "File \"" + file.getName() + "\" exists.\nDo you want to overwrite it?",
                    "File exists",
                    JOptionPane.YES_NO_OPTION,
                    JOptionPane.QUESTION_MESSAGE,
                    null,
                    options,
                    options[1]);
            if (n == 1) {
                return false;
            }
        }
        return true;
    }
}

