/**
 *
 * Copyright INRA-URGI 2009-2010
 * 
 * This software is governed by the CeCILL license under French law and
 * abiding by the rules of distribution of free software. You can use,
 * modify and/ or redistribute the software under the terms of the CeCILL
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 * 
 * As a counterpart to the access to the source code and rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty and the software's author, the holder of the
 * economic rights, and the successive licensors have only limited
 * liability.
 * 
 * In this respect, the user's attention is drawn to the risks associated
 * with loading, using, modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean that it is complicated to manipulate, and that also
 * therefore means that it is reserved for developers and experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and, more generally, to use and operate it in the
 * same conditions as regards security.
 * 
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL license and that you accept its terms.
 *
 */
import java.util.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import javax.swing.*;
import javax.swing.filechooser.*;
import javax.swing.border.*;
import javax.swing.SwingUtilities;


public class Smart extends JPanel implements ActionListener {

  String version = "1.0.2";

  JFrame mainFrame;
  JButton openButton;
  JButton comparisonGoButton;

  JComboBox formatTypes;
  JComboBox fileFormats;
  String[]  emptyFormats = {"Choose a type first..."};

  JFrame  askFrame;
  JButton pythonButton;
  JButton mySqlButton;
  JButton rButton;

  HashMap <JButton, Program> callingProgram;

  // comparison
  JList comparisonFile1List;
  JList comparisonFile2List;
  JTextField comparisonOutputTextField;
  JTextField comparisonFiveQueryExtensionTextField;
  JCheckBox comparisonColinearCheckBox;
  JCheckBox comparisonAntisenseCheckBox;
  JCheckBox comparisonInvertCheckBox;

  JList        fileList;
  JTextArea    logArea;

  // progress bar
  JLabel       messageField;
  JProgressBar progressBar;
  JLabel       etaField;

  // process
  Program           currentProgram;
  Process           process;
  javax.swing.Timer processTimer;


  int previousStatus;

  public Smart() {
    super(new BorderLayout());

    callingProgram = new HashMap <JButton, Program> ();

    previousStatus = -1;

    processTimer = new javax.swing.Timer(1000, this);
    processTimer.stop();

    // Ask frame buttons
    pythonButton = new JButton("find...");
    mySqlButton  = new JButton("find...");
    rButton      = new JButton("find...");

    // Get available formats
    FormatsReader formatReader = new FormatsReader(Global.smartFormatsFileName);
    if (! formatReader.read()) {
      System.out.println("Something was wrong while reading file format...");
    }

    // Get screen size
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

    // Log
    logArea = new JTextArea(512, Global.logAreaSize);
    logArea.setPreferredSize(new Dimension(screenSize.width, (int) (screenSize.height * 0.22)));
    logArea.setFont(new Font("Monospaced", logArea.getFont().getStyle(), logArea.getFont().getSize()));
    JScrollPane logScroll  = new JScrollPane(logArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    TitledBorder logBorder = BorderFactory.createTitledBorder("Log");
    logScroll.setBorder(logBorder);
    logArea.append("Using S-MART " + version + "\n");

    GridLayout horizontalLayout = new GridLayout(1, 0);

    // check configuration
    this.readConfigurationFile();
    this.checkConfiguration();

    // Tabs
    JTabbedPane tabbedPane = new JTabbedPane();
    tabbedPane.setPreferredSize(new Dimension(screenSize.width, (int) (screenSize.height * 0.75)));

    // File panel
    JPanel filePanel = new JPanel(false);
    filePanel.setLayout(new FlowLayout());
    tabbedPane.add("Files", filePanel);

    // Format sub-panel
    JPanel         formatComboPanel  = new JPanel(false);
    JPanel         formatPanel       = new JPanel(false);
    Vector<String> formatTypesString = Global.formats.getFormatTypes();
    formatPanel.setLayout(horizontalLayout);
    formatTypesString.insertElementAt("Choose the format type", 0);
    JLabel formatLabel = new JLabel("Format");
    formatTypes        = new JComboBox(formatTypesString);
    fileFormats        = new JComboBox(emptyFormats);
    formatLabel.setLabelFor(fileFormats);
    formatTypes.addActionListener(this);
    formatComboPanel.add(formatTypes);
    formatComboPanel.add(fileFormats);

    formatPanel.add(formatLabel);
    formatPanel.add(formatComboPanel);

    // File chooser sub-panel
    JPanel fileChooserPanel = new JPanel(false);
    fileChooserPanel.setLayout(horizontalLayout);
    JLabel fileLabel = new JLabel("File");
    openButton = new JButton("Open a File...");
    openButton.addActionListener(this);

    fileChooserPanel.add(fileLabel);
    fileChooserPanel.add(openButton);

    // File list sub-panel
    JPanel existingFilesPanel = new JPanel(false);
    existingFilesPanel.setLayout(horizontalLayout);
    existingFilesPanel.setMinimumSize(new Dimension(10000, 10000));
    JLabel existingFilesLabel = new JLabel("Existing files");
    Box fileListBox = Box.createHorizontalBox();
    fileListBox.add(Box.createRigidArea(new Dimension(0, 100)));
    JList fileList = new JList(Global.fileNames);
    fileList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
    fileList.setVisibleRowCount(4);
    JScrollPane listScroller = new JScrollPane(fileList);
    fileListBox.add(listScroller);

    existingFilesPanel.add(existingFilesLabel);
    existingFilesPanel.add(fileListBox);

    // File panel layout
    Box box = Box.createVerticalBox();
    box.add(formatPanel);
    box.add(fileChooserPanel);
    box.add(existingFilesPanel);
    filePanel.add(box);

    // Program panels
    TreeMap < String, JTabbedPane > panels = new TreeMap < String, JTabbedPane >();
    PythonProgramFinder programFinder = new PythonProgramFinder("Python");
    String comments = programFinder.findPrograms();
    if (comments != null) {
      logArea.append(comments);
    }
    for (int i = 0; i < programFinder.getPrograms().size(); i++) {
      Program program         = programFinder.getPrograms().get(i);
      JPanel programPanel     = program.getPanel();
      String section          = program.getSection();
      JTabbedPane sectionPane = null;
      if (panels.containsKey(section)) {
        sectionPane = panels.get(section);
      }
      else {
        sectionPane = new JTabbedPane();
        tabbedPane.addTab(section, sectionPane);
        panels.put(section, sectionPane);
      }

      JScrollPane programScroll  = new JScrollPane(programPanel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
      sectionPane.addTab(program.getName(), programScroll);

      JButton button  = program.getButton();
      button.addActionListener(this);
      callingProgram.put(button, program);
    }

    // Progress bar
    JPanel progressPanel = new JPanel(new GridLayout(1, 0), false);
    progressPanel.setPreferredSize(new Dimension(screenSize.width, (int) (screenSize.height * 0.02)));
    messageField = new JLabel();
    progressBar  = new JProgressBar(0, 100);
    etaField     = new JLabel();
    messageField.setHorizontalAlignment(JLabel.LEFT);
    progressBar.setValue(0);
    etaField.setHorizontalAlignment(JLabel.RIGHT);
    progressBar.setStringPainted(true);
    progressPanel.add(messageField);
    progressPanel.add(progressBar);
    progressPanel.add(etaField);
      
    add(tabbedPane, BorderLayout.PAGE_START);
    add(logScroll, BorderLayout.CENTER);
    add(progressPanel, BorderLayout.PAGE_END);
  }


  public boolean checkConfiguration() {
    int status = this.testConfiguration();
  
    if (status == previousStatus) {
      logArea.append("S-MART does not seem to work properly... Tried to manage it by myself, unsuccessfully... Check documentation for further explanation...\n");
      return false;
    }

    switch (status) {
      case 0:
        return true;
      case 1:
        logArea.append("S-MART does not seem to work properly... Check documentation for further explanation...\n");
        break;
      case 3:
        this.askWhereIsProgram("python");
        break;
      case 4:
        break;
      case 5:
        this.askWhereIsProgram("mySQL");
        break;
      case 6:
        this.askWhereIsProgram("R");
        break;
      case 7:
        logArea.append("Please install 'ColorBrewer' R package...\n");
        break;
      default:
        logArea.append("Weird configuration test status: " + status + "...\n");
    }
    previousStatus = status;
    return true;
  }


  public int testConfiguration() {
    String[]        command  = {Global.pythonCommand, "Python" + java.io.File.separator + "testInstall.py"};
    ProgramLauncher launcher = new ProgramLauncher(command, logArea, messageField, progressBar, etaField);
    String          line;
    launcher.execute();
    (new Exception("hello")).printStackTrace();
    return launcher.getExitValue();
  }


  public void readConfigurationFile() {
    java.io.File   file      = new java.io.File(Global.smartConfFileName);
    String         line      = null;

    if (! file.exists()) {
      return;
    }

    try {
      BufferedReader reader = new BufferedReader(new FileReader(file));

      while ((line = reader.readLine()) != null) {
        line = line.trim();
        if      (line.startsWith("python:")) Global.pythonCommand = line.substring("python:".length()).trim();
        else if (line.startsWith("mysql:"))  Global.mysqlCommand  = line.substring("mysql:".length()).trim();
        else if (line.startsWith("r:"))      Global.rCommand      = line.substring("r:".length()).trim();
      }
      reader.close();
    }
    catch (FileNotFoundException e) {
      logArea.append("Configuration file is empty: " + e.getMessage() + "!\n");
      return;
    }
    catch (IOException e) {
      logArea.append("Weird with configuration file: " + e.getMessage() + "!\n");
      return;
    }
  }


  public void askWhereIsProgram(String program) {
    askFrame = new JFrame("Where is " + program + "?");
    askFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JLabel label = new JLabel("Where is your " + program + " (or " + program + ".exe) file?");
    JButton button = null;
    if ("python".equals(program)) {
      button = pythonButton;
    }
    else if ("mySQL".equals(program)) {
      button = mySqlButton;
    }
    else if ("R".equals(program)) {
      button = rButton;
    }
    else {
      logArea.append("Problem with the button!\n");
    }
    askFrame.getContentPane().add(label, BorderLayout.WEST);
    askFrame.getContentPane().add(button, BorderLayout.EAST);
    button.addActionListener(this);
    askFrame.pack();
    askFrame.setVisible(true);
    askFrame.setAlwaysOnTop(true);
  }


  public void actionPerformed(ActionEvent e) {

    // Python command chooser
    if (e.getSource() == pythonButton) {
      JFileChooser chooser = new JFileChooser();
      if (chooser.showOpenDialog(mainFrame) == JFileChooser.APPROVE_OPTION) {
        Global.pythonCommand = chooser.getSelectedFile().getPath();
        askFrame.setVisible(false);
        askFrame.dispose();
        try {
          BufferedWriter out = new BufferedWriter(new FileWriter(Global.smartConfFileName, true));
          out.write("python: " + Global.pythonCommand + "\n");
          out.close();
        }
        catch (IOException exception) {
          logArea.append("Cannot write configuration file!\n");
        }
      }
      this.checkConfiguration();
    }
    // MySQL command chooser
    else if (e.getSource() == mySqlButton) {
      JFileChooser chooser = new JFileChooser();
      if (chooser.showOpenDialog(mainFrame) == JFileChooser.APPROVE_OPTION) {
        Global.mysqlCommand = chooser.getSelectedFile().getPath();
        askFrame.setVisible(false);
        askFrame.dispose();
        try {
          BufferedWriter out = new BufferedWriter(new FileWriter(Global.smartConfFileName, true));
          out.write("mysql: " + Global.mysqlCommand + "\n");
          out.close();
        }
        catch (IOException exception) {
          logArea.append("Cannot write configuration file!\n");
        }
      }
      this.checkConfiguration();
    }
    // R command chooser
    else if (e.getSource() == rButton) {
      JFileChooser chooser = new JFileChooser();
      if (chooser.showOpenDialog(mainFrame) == JFileChooser.APPROVE_OPTION) {
        Global.rCommand = chooser.getSelectedFile().getPath();
        askFrame.setVisible(false);
        askFrame.dispose();
        try {
          BufferedWriter out = new BufferedWriter(new FileWriter(Global.smartConfFileName, true));
          out.write("r: " + Global.rCommand + "\n");
          out.close();
        }
        catch (IOException exception) {
          logArea.append("Cannot write configuration file!\n");
        }
      }
      this.checkConfiguration();
    }
    // Format type
    else if (e.getSource() == formatTypes) {
      fileFormats.removeAllItems();
      Vector < String > selectedFormats = Global.formats.getFormats((String) formatTypes.getSelectedItem()).getFormats();
      for (int i = 0; i < selectedFormats.size(); i++) {
        fileFormats.addItem(selectedFormats.get(i));
      }
    }
    // Main file chooser
    else if (e.getSource() == openButton) {
      JFileChooser chooser = new JFileChooser();
      if (chooser.showOpenDialog(mainFrame) == JFileChooser.APPROVE_OPTION) {
        String fileName = chooser.getSelectedFile().getPath();
        Global.fileNames.addElement(fileName);
        Global.files.addFile(fileName, (String) formatTypes.getSelectedItem(), (String) fileFormats.getSelectedItem());
      }
    }
    // Other file choosers
    else if (Global.otherFilesChooser.containsKey(e.getSource())) {
      JTextField textField = Global.otherFilesChooser.get(e.getSource());
      JFileChooser chooser = new JFileChooser();
      if (chooser.showOpenDialog(mainFrame) == JFileChooser.APPROVE_OPTION) {
        textField.setText(chooser.getSelectedFile().getPath());
      }
    }
    // Other directories choosers
    else if (Global.otherDirectoriesChooser.containsKey(e.getSource())) {
      JTextField textField = Global.otherDirectoriesChooser.get(e.getSource());
      JFileChooser chooser = new JFileChooser();
      chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
      if (chooser.showOpenDialog(mainFrame) == JFileChooser.APPROVE_OPTION) {
        textField.setText(chooser.getSelectedFile().getPath());
      }
    }
    else if (Global.otherFileConcatenationChooser.containsKey(e.getSource())) {
      JTextField textField = Global.otherDirectoriesChooser.get(e.getSource());
      JFileChooser chooser = new JFileChooser();
      chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
      if (chooser.showOpenDialog(mainFrame) == JFileChooser.APPROVE_OPTION) {
        String text = textField.getText();
        if ((text == null) || ("".equals(text))) {
          textField.setText(chooser.getSelectedFile().getPath());
        }
        else {
          textField.setText(text + "," + chooser.getSelectedFile().getPath());
        }
      }
    }
    // Programs
    else {
      currentProgram = callingProgram.get(e.getSource());
      String comment = currentProgram.checkValues();
      if (comment != null) {
        logArea.append(comment);
        return;
      }
      LinkedList <String> command = currentProgram.getCommand();
      ProgramLauncher launcher = new ProgramLauncher(command, logArea, messageField, progressBar, etaField);
      launcher.execute();
      Vector < File > outputFiles = currentProgram.getOutputFiles();
      for (int i = 0; i < outputFiles.size(); i++) {
        File file = outputFiles.get(i);
        if (file.getFormatType().compareToIgnoreCase("other") != 0) {
          Global.fileNames.addElement(file.getName());
          Global.files.addFile(file);
        }
      }
      currentProgram = null;
    }
  }


  private static void createAndShowGUI() {
    // Create and set up the window.
    JFrame mainFrame = new JFrame("S-Mart");
    mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //Create and set up the content pane.
    JComponent newContentPane = new Smart();
    newContentPane.setOpaque(true);
    mainFrame.setContentPane(newContentPane);

    // Display the window.
    mainFrame.pack();
    mainFrame.setVisible(true);
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    mainFrame.setBounds(0, 0, screenSize.width, screenSize.height);
  }


  public static void main(String[] args) {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        createAndShowGUI();
      }
    });
  }
}
