/*
 * NCSA Data To Knowledge (D2K) Project
 * Automated Learning Group
 * National Center for Supercomputing Applications
 * University of Illinois at Urbana-Champaign
 * 605 E. Springfield, Champaign, IL 61820
 *
 * d2k@ncsa.uiuc.edu
 *
 * Copyright 2003, Board of Trustees of the University of Illinois,
 * All Rights Reserved
 *
 * NCSA Data To Knowledge (D2K) software, source code, binary code, and Java-byte 
 * code including D2K modules and D2K itineraries (hereafter, Software) is 
 * copyrighted by the Board of Trustees of the University of Illinois (UI),
 * and ownership remains with the UI.
 *
 * Prior to receiving this Software Source Code, You must have agreed to a
 * non-exclusive, non-transferable, restricted License of Software with UI for
 * limited Research Use and/or Internal Business Use. The terms of that License
 * control the use of this Software Source Code. For the sake of convenience, 
 * certain provisions of that License are stated here. However, in the event
 * of any real or perceived differences between the terms of the License and the
 * statements made herein, the License controls.  
 *
 * You may not distribute the Software including the Binary Code and this Source
 * Code to third parties.
 *
 * You may make Derivative Works. You are encouraged to provide information to 
 * UI regarding Derivative Works and your experience with Software. However, if
 * You make any Derivative Work based on or derived from the Software, then You
 * will: (1) clearly notify users that such Derivative Work is a modified version
 * and uses or is derived from the original NCSA Data To Knowledge (D2K)
 * developed at UI, and include specific language in that notice as provided for
 * in the License, and (2) acknowledge via citation and provide UI with a copy of
 * any report or publication using the Software or Derivative Work.
 *
 * If You wish to make Commercial Use of the Software or Derivative Works, then
 * You should contact the UI, c/o NCSA, to negotiate an appropriate license for
 * such Commercial Use. Commercial Use includes sale, lease, license,
 * distribution or otherwise making the Software or Derivative Works available to
 * third parties, which includes, but is not limited to, integration of all or
 * part of the Software or Derivative Work into a product for sale or license by
 * or on behalf of You to third parties.
 *
 * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY
 * PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. THE UI
 * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE USERS OF THIS SOFTWARE.
 *
 * By using or copying this Software, You agree to abide by the copyright law
 * and all other applicable laws of the U.S. including, but not limited to,
 * export control laws, and the terms of the License. UI shall have the right
 * to terminate its license with You immediately upon Your breach of, or
 * non-compliance with, any of its terms. You may be held legally responsible
 * for any copyright infringement that is caused or encouraged by Your failure
 * to abide by the terms of the License.
 */

package ncsa.d2k.modules.core.transform.attribute;

import ncsa.d2k.core.modules.*;
import ncsa.d2k.userviews.swing.*;
import ncsa.d2k.core.modules.UserView;
import ncsa.gui.Constrain;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
import ncsa.d2k.modules.core.datatype.table.*;

/**
 ChooseAttributes.java  (previously ChooseFields)

 Allows the user to choose which columns of a table are inputs and outputs.
 Then assigns them in an ExampleTable.

 @author Peter Groves, c/o David Clutter
 */
public class ChooseAttributes extends UIModule  {

  /**
   Return a description of the function of this module.
   @return A description of this module.
   */
  public String getModuleInfo() {
    String info = "<p>Overview: ";
    info += "This module allows the user to choose which columns of a table are inputs and outputs.";
    info += "</p><p>Detailed Description: ";
    info += "This module outputs an <i>Example Table</i> with the input and output features assigned. ";
    info += "Inputs and outputs do not have to be selected, nor do they have to be mutually exclusive. ";
    info += "</p><p>Data Handling: ";
    info += "This module does not modify the data in the table. It only sets the input and output features.";
    return info;
  }

  /**
   Return the name of this module.
   @return The name of this module.
   */
  public String getModuleName() {
    return "Choose Attributes";
  }

  /**
   Return a String array containing the datatypes the inputs to this
   module.
   @return The datatypes of the inputs.
   */
  public String[] getInputTypes() {
    String[] types = {"ncsa.d2k.modules.core.datatype.table.Table"};
    return types;
  }

  /**
   Return a String array containing the datatypes of the outputs of this
   module.
   @return The datatypes of the outputs.
   */
  public String[] getOutputTypes() {
    String[] types = {"ncsa.d2k.modules.core.datatype.table.ExampleTable"};
    return types;
  }

  /**
   Return a description of a specific input.
   @param i The index of the input
   @return The description of the input
   */
  public String getInputInfo(int i) {
    switch (i) {
      case 0: return "The Table to choose inputs and outputs from.";
      default: return "No such input";
    }
  }

  /**
   Return the name of a specific input.
   @param i The index of the input.
   @return The name of the input
   */
  public String getInputName(int i) {
    switch(i) {
      case 0:
        return "Table";
      default: return "No such input";
    }
  }

  /**
   Return the description of a specific output.
   @param i The index of the output.
   @return The description of the output.
   */
  public String getOutputInfo(int i) {
    switch (i) {
      case 0: return "The Example Table with input and output features assigned.";
      default: return "No such output";
    }
  }

  /**
   Return the name of a specific output.
   @param i The index of the output.
   @return The name of the output
   */
  public String getOutputName(int i) {
    switch(i) {
      case 0:
        return "Example Table";
      default: return "No such output";
    }
  }


    public PropertyDescription[] getPropertiesDescriptions() {
   return new PropertyDescription[0]; // so that "windowName" property
   // is invisible
    }


  /**
   Return the UserView
   @returns the UserView
   */
  protected UserView createUserView() {
    return new AttributeView();
  }

  /**
  Not used
  */
  protected String[] getFieldNameMapping() {
    return null;
  }

  /**
   Pushes the outputs. Called when the view has finished.
   */
  /*public void finish(ExampleTable et) {
   this.pushOutput(et, 0);
   executionManager.moduleDone(this);
   }*/

  /**
   The user view class
   */
  class AttributeView extends JUserPane implements ActionListener {
    private Table table; //Old data
    private ExampleTable et; //Updated table

    private ChooseAttributes module;
    private JButton abort;
    private JButton done;

    private JList inputList;
    private JList outputList;

    private JLabel inputLabel;
    private JLabel outputLabel;

    boolean labels;
    private HashMap inputToIndexMap;
    private HashMap outputToIndexMap;

    private JCheckBoxMenuItem miColumnOrder;
    private JCheckBoxMenuItem miAlphaOrder;

    private JMenuBar menuBar;

    /**
     Initialize
     */
    public void initView(ViewModule v) {
      module = (ChooseAttributes)v;
      abort = new JButton("Abort");
      done = new JButton("Done");
      abort.addActionListener(this);
      done.addActionListener(this);
      menuBar = new JMenuBar();
      JMenu m1 = new JMenu("File");
      miColumnOrder = new JCheckBoxMenuItem("Column Order");
      miColumnOrder.addActionListener(this);
      miColumnOrder.setState(true);
      miAlphaOrder = new JCheckBoxMenuItem("Alphabetical Order");
      miAlphaOrder.addActionListener(this);
      miAlphaOrder.setState(false);
      m1.add(miColumnOrder);
      m1.add(miAlphaOrder);
      menuBar.add(m1);
    }

    public Object getMenu() {
      return menuBar;
    }

    /**
     Called when inputs arrive
     */
    public void setInput(Object o, int id) {
      if(id == 0) {
        table = (Table)o;
        this.removeAll();
        addComponents();
      }
    }

    /**
     Make me at least this big
     */
    public Dimension getPreferredSize() {
      return new Dimension(400, 300);
    }

    /**
     Add all the components
     */
    private void addComponents() {
      JPanel back = new JPanel();

      int numColumns = table.getNumColumns();

      /*String[] labels=new String[numColumns];
      for(int i = 0; i < numColumns; i++)
      labels[i] = table.getColumnLabel(i);
      */
      String[] labels = orderedLabels();
      if (table.getColumnLabel(0).equals("")) {
        miColumnOrder.setEnabled(false);
        miAlphaOrder.setEnabled(false);
      } else {
        miColumnOrder.setEnabled(true);
        miAlphaOrder.setEnabled(true);
      }

      inputList=new JList(/*labels*/);
      DefaultListModel dlm = new DefaultListModel();
      for(int i = 0; i < labels.length; i++)
        dlm.addElement(labels[i]);
      inputList.setModel(dlm);
      if(table instanceof ExampleTable) {
        //inputList.setSelectedIndices(((ExampleTable)table).getInputFeatures());
        int[] ins = ((ExampleTable)table).getInputFeatures();
        if(ins != null) {
          int[] sel = new int[ins.length];
          for(int i = 0; i < ins.length; i++) {
            String s = table.getColumnLabel(ins[i]);
            Integer ii = (Integer)inputToIndexMap.get(s);
            sel[i] = ii.intValue();
          }
          inputList.setSelectedIndices(sel);
        }
      }
      outputList=new JList(/*labels*/);
      dlm = new DefaultListModel();
      for(int i = 0; i < labels.length; i++)
        dlm.addElement(labels[i]);
      outputList.setModel(dlm);
      if(table instanceof ExampleTable) {
        //outputList.setSelectedIndices(((ExampleTable)table).getOutputFeatures());
        int[] ins = ((ExampleTable)table).getOutputFeatures();
        if(ins != null) {
          int[] sel = new int[ins.length];
          for(int i = 0; i < ins.length; i++) {
            String s = table.getColumnLabel(ins[i]);
            Integer ii = (Integer)outputToIndexMap.get(s);
            sel[i] = ii.intValue();
          }
          outputList.setSelectedIndices(sel);
        }
      }
      JScrollPane leftScrollPane=new JScrollPane(inputList);
      JScrollPane rightScrollPane=new JScrollPane(outputList);

      inputLabel=new JLabel("Input Attributes");
      inputLabel.setHorizontalAlignment(SwingConstants.CENTER);

      outputLabel=new JLabel("Output Attributes");
      outputLabel.setHorizontalAlignment(SwingConstants.CENTER);

      back.setLayout(new GridBagLayout());

      Constrain.setConstraints(back, inputLabel, 0, 0, 1, 1,
                               GridBagConstraints.BOTH, GridBagConstraints.CENTER, 0, 0);
      Constrain.setConstraints(back, outputLabel, 1, 0, 1, 1,
                               GridBagConstraints.BOTH, GridBagConstraints.CENTER, 0, 0);
      Constrain.setConstraints(back, leftScrollPane, 0, 1, 1, 1,
                               GridBagConstraints.BOTH, GridBagConstraints.CENTER, 1, 1);
      Constrain.setConstraints(back, rightScrollPane, 1, 1, 1, 1,
                               GridBagConstraints.BOTH, GridBagConstraints.CENTER, 1, 1);

      JPanel buttons = new JPanel();
      buttons.add(abort);
      buttons.add(done);

      this.add(back, BorderLayout.CENTER);
      this.add(buttons, BorderLayout.SOUTH);
    }

    /**
     Listen for ActionEvents
     */
    public void actionPerformed(ActionEvent e) {
      Object src = e.getSource();
      if(src == abort)
        module.viewCancel();
      else if(src == done) {
        //if(checkChoices()) {
        setFieldsInTable();
        //module.finish(et);
        pushOutput(et, 0);
        viewDone("Done");
        et = null;
        this.removeAll();
        //}
      }
      else if(src == miColumnOrder) {
        String [] labels = orderedLabels();
        miAlphaOrder.setState(false);
        DefaultListModel dlm = (DefaultListModel)inputList.getModel();
        dlm.removeAllElements();
        for(int i = 0; i < labels.length; i++) {
          dlm.addElement(labels[i]);
        }
        dlm = (DefaultListModel)outputList.getModel();
        dlm.removeAllElements();
        for(int i = 0; i < labels.length; i++) {
          dlm.addElement(labels[i]);
        }
      }
      else if(src == miAlphaOrder) {
        String [] labels = alphabetizeLabels();
        miColumnOrder.setState(false);
        DefaultListModel dlm = (DefaultListModel)inputList.getModel();
        dlm.removeAllElements();
        for(int i = 0; i < labels.length; i++) {
          dlm.addElement(labels[i]);
        }
        dlm = (DefaultListModel)outputList.getModel();
        dlm.removeAllElements();
        for(int i = 0; i < labels.length; i++) {
          dlm.addElement(labels[i]);
        }
      }
    }

    private final String[] orderedLabels() {
      String[] labels=new String[table.getNumColumns()];

      inputToIndexMap = new HashMap(labels.length);
      outputToIndexMap = new HashMap(labels.length);
      for(int i = 0; i < labels.length; i++) {
        String label = table.getColumnLabel(i);
        if (label.equals(""))
          label = new String("Column " + Integer.toString(i));
        labels[i] = label;
        inputToIndexMap.put(labels[i], new Integer(i));
        outputToIndexMap.put(labels[i], new Integer(i));
      }
      return labels;
    }

    private final String[] alphabetizeLabels() {
      String [] labels = new String[table.getNumColumns()];
      inputToIndexMap = new HashMap(labels.length);
      outputToIndexMap = new HashMap(labels.length);
      for(int i = 0; i < labels.length; i++) {
        labels[i] = table.getColumnLabel(i);
        inputToIndexMap.put(labels[i], new Integer(i));
        outputToIndexMap.put(labels[i], new Integer(i));
      }
      Arrays.sort(labels, new StringComp());
      return labels;
    }

    private final class StringComp implements Comparator {
      public int compare(Object o1, Object o2) {
        String s1 = (String)o1;
        String s2 = (String)o2;
        return s1.toLowerCase().compareTo(s2.toLowerCase());
      }
      public boolean equals(Object o) {
        return super.equals(o);
      }
    }

    private void setFieldsInTable(){
      et= table.toExampleTable();
      //et.setInputFeatures(inputList.getSelectedIndices());
      //et.setOutputFeatures(outputList.getSelectedIndices());
      Object[] selected = inputList.getSelectedValues();
      int [] inputFeatures = new int[selected.length];
      for(int i = 0; i < selected.length; i++) {
        String s = (String)selected[i];
        Integer ii = (Integer)inputToIndexMap.get(s);
        inputFeatures[i] = ii.intValue();
      }
      selected = outputList.getSelectedValues();
      int [] outputFeatures = new int[selected.length];
      for(int i = 0; i < selected.length; i++) {
        String s = (String)selected[i];
        Integer ii = (Integer)outputToIndexMap.get(s);
        outputFeatures[i] = ii.intValue();
      }
      et.setInputFeatures(inputFeatures);
      et.setOutputFeatures(outputFeatures);
      table = null;
    }

    /**
     Not used
     Make sure all choices are valid.
     */
    protected boolean checkChoices() {
      if(outputList.getSelectedIndex() == -1){
        JOptionPane.showMessageDialog(this,
                                      "You must select at least one output",
                                      "Error", JOptionPane.ERROR_MESSAGE);
        return false;
      }
      if(inputList.getSelectedIndex() == -1){
        JOptionPane.showMessageDialog(this,
                                      "You must select at least one input",
                                      "Error", JOptionPane.ERROR_MESSAGE);
        return false;
      }
      return true;
    }
  }
}

//QA Comments Anca - added getPropertyDescription
//QA Comments Ruth - used Example Table in what user reads (w/ space)