/*
 *This library is free software; you can redistribute it and/or
 *modify it under the terms of the GNU Lesser General Public
 *License as published by the Free Software Foundation; either
 *version 2.1 of the License, or (at your option) any later version.
 *
 *This library 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
 *Lesser General Public License for more details.
 *
 *You should have received a copy of the GNU Lesser General Public
 *License along with this library; if not, write to the Free Software
 *Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package uk.ac.man.bioinf.gui.util.forms;
import javax.swing.*; 
import javax.swing.text.*; 
import javax.swing.event.*;
import java.awt.Toolkit;
import java.text.NumberFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.*;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;


public class ValidatedForm extends JPanel {
   private HashMap labels;
   private HashMap fields;
   private HashMap modifiers;
   private HashMap changes;
   private GridBagConstraints c;
   private GridBagLayout gridbag;
   private int currentRow;
   private MyDocumentListener myDocumentListener;

   public static final int LESS_THAN    = 0;
   public static final int GREATER_THAN = 1;
   public static final int EQUALS       = 2;
   public static final int LIKE         = 3;

   public ValidatedForm() {
      super();
      labels    = new HashMap();
      fields    = new HashMap();
      modifiers = new HashMap();
      changes   = new HashMap();
      //text fields on right
      myDocumentListener = new MyDocumentListener();

      this.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));


      c = new GridBagConstraints();
      gridbag = new GridBagLayout();
      this.setLayout(gridbag);

      currentRow = 0;
      c.anchor = GridBagConstraints.EAST;
   }

   public void addIntegerField(String key, String label, int init, boolean modifier, int columns) {
      JLabel l = new JLabel(label);
      labels.put(key,l);
      IntegerField i = new IntegerField(init,columns);
      fields.put(key,i);
      i.getDocument().addDocumentListener(myDocumentListener);
      i.getDocument().putProperty("name", key);
      ModifierCombo m = new ModifierCombo(false);
      if(modifier) modifiers.put(key,m);

      c.gridwidth = 1;
      c.gridx     = 0;
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.NONE;      //reset to default
      c.weightx = 0.0;                       //reset to default
      gridbag.setConstraints(l, c);
      this.add(l);

      c.gridwidth = 1;
      c.gridx     = 1; 
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.HORIZONTAL;      //reset to default
      c.weightx = 0.0;                       //reset to default
      gridbag.setConstraints(m, c);
      this.add(m);
      
      c.gridwidth = 1;
      c.gridx     = 2;
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.HORIZONTAL;
      c.weightx = 1.0;
      gridbag.setConstraints(i, c);
      this.add(i);

      currentRow++;

      this.revalidate();
   }

   public void addIntegerField(String key, String label, boolean modifier, int columns) {
      JLabel l = new JLabel(label);
      labels.put(key,l);
      IntegerField i = new IntegerField(columns);
      fields.put(key,i);
      i.getDocument().addDocumentListener(myDocumentListener);
      i.getDocument().putProperty("name", key);

      ModifierCombo m = new ModifierCombo(false);
      if(modifier) modifiers.put(key,m);

      c.gridwidth = 1;
      c.gridx     = 0;
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.NONE;      //reset to default
      c.weightx = 0.0;                       //reset to default
      gridbag.setConstraints(l, c);
      this.add(l);

      c.gridwidth = 1;
      c.gridx     = 1; 
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.HORIZONTAL;      //reset to default
      c.weightx = 0.0;                       //reset to default
      gridbag.setConstraints(m, c);
      this.add(m);
      
      c.gridwidth = 1;
      c.gridx     = 2;
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.HORIZONTAL;
      c.weightx = 1.0;
      gridbag.setConstraints(i, c);
      this.add(i);

      currentRow++;

      this.revalidate();
   }

   public void addDecimalField(String key, String label, double init, boolean modifier, int columns, boolean isPercent) {
      JLabel l = new JLabel(label);
      labels.put(key,l);
      DecimalField i;
      if(isPercent) {
         NumberFormat percentFormat = NumberFormat.getNumberInstance();
         percentFormat.setMinimumFractionDigits(3);
         //XXXX: Workaround. With an empty positive suffix
         //the format allows letters in the number.
         ((DecimalFormat)percentFormat).setPositiveSuffix(" ");
         i = new DecimalField(init,columns,percentFormat);
      }
      else {
         NumberFormat format = NumberFormat.getNumberInstance();

         i = new DecimalField(init,columns,format);
      }
      fields.put(key,i);
      i.getDocument().addDocumentListener(myDocumentListener);
      i.getDocument().putProperty("name", key);

      ModifierCombo m = new ModifierCombo(false);
      if(modifier) modifiers.put(key,m);

      c.gridwidth = 1;
      c.gridx     = 0;
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.NONE;      //reset to default
      c.weightx = 0.0;                       //reset to default
      gridbag.setConstraints(l, c);
      this.add(l);

      c.gridwidth = 1;
      c.gridx     = 1; 
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.HORIZONTAL;      //reset to default
      c.weightx = 0.0;                       //reset to default
      gridbag.setConstraints(m, c);
      this.add(m);
      
      c.gridwidth = 1;
      c.gridx     = 2;
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.HORIZONTAL;
      c.weightx = 1.0;
      gridbag.setConstraints(i, c);
      this.add(i);

      currentRow++;

      this.revalidate();
   }

   public void addDecimalField(String key, String label, boolean modifier, int columns, boolean isPercent) {
      JLabel l = new JLabel(label);
      labels.put(key,l);
      DecimalField i;
      if(isPercent) {
         NumberFormat percentFormat = NumberFormat.getNumberInstance();
         percentFormat.setMinimumFractionDigits(3);
         //XXXX: Workaround. With an empty positive suffix
         //the format allows letters in the number.
         ((DecimalFormat)percentFormat).setPositiveSuffix(" ");
         i = new DecimalField(columns,percentFormat);
      }
      else {
         NumberFormat format = NumberFormat.getNumberInstance();

         i = new DecimalField(columns,format);
      }
      fields.put(key,i);
      i.getDocument().addDocumentListener(myDocumentListener);
      i.getDocument().putProperty("name", key);

      ModifierCombo m = new ModifierCombo(false);
      if(modifier) modifiers.put(key,m);

      c.gridwidth = 1;
      c.gridx     = 0;
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.NONE;      //reset to default
      c.weightx = 0.0;                       //reset to default
      gridbag.setConstraints(l, c);
      this.add(l);

      c.gridwidth = 1;
      c.gridx     = 1; 
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.HORIZONTAL;      //reset to default
      c.weightx = 0.0;                       //reset to default
      gridbag.setConstraints(m, c);
      this.add(m);
      
      c.gridwidth = 1;
      c.gridx     = 2;
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.HORIZONTAL;
      c.weightx = 1.0;
      gridbag.setConstraints(i, c);
      this.add(i);

      currentRow++;

      this.revalidate();
   }

   public void addTextField(String key, String label, String init, boolean modifier, int columns) {
      JLabel l = new JLabel(label);
      labels.put(key,l);
      JTextField i = new JTextField(init,columns);
      fields.put(key,i);
      i.getDocument().addDocumentListener(myDocumentListener);
      i.getDocument().putProperty("name", key);

      ModifierCombo m = new ModifierCombo(true);
      if(modifier) modifiers.put(key,m);

      c.gridwidth = 1;
      c.gridx     = 0;
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.NONE;      //reset to default
      c.weightx = 0.0;                       //reset to default
      gridbag.setConstraints(l, c);
      this.add(l);

      c.gridwidth = 1;
      c.gridx     = 1; 
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.HORIZONTAL;      //reset to default
      c.weightx = 0.0;                       //reset to default
      gridbag.setConstraints(m, c);
      this.add(m);
      
      c.gridwidth = 1;
      c.gridx     = 2;
      c.gridy     = currentRow;
      c.fill = GridBagConstraints.HORIZONTAL;
      c.weightx = 1.0;
      gridbag.setConstraints(i, c);
      this.add(i);

      currentRow++;

      this.revalidate();
   }

   class MyDocumentListener implements DocumentListener {
            public void insertUpdate(DocumentEvent e) {
               recordChange(e);
            }
            public void removeUpdate(DocumentEvent e) {
               recordChange(e);
            }
            public void changedUpdate(DocumentEvent e) {
                // we won't ever get this with a PlainDocument
            }
            private void recordChange(DocumentEvent e) {
            }
        }    


   public int getIntegerFieldValue(String key) {
      IntegerField i = (IntegerField) fields.get(key);
      return i.getValue();
   }

   public double getDecimalFieldValue(String key) {
      DecimalField d = (DecimalField) fields.get(key);
      return d.getValue();
   }

   public String getTextFieldValue(String key) {
      JTextField t = (JTextField) fields.get(key);
      return t.getText();
   }

  public boolean isIntegerFieldValid(String key) {
      IntegerField i = (IntegerField) fields.get(key);
      return i.isValid();
   }

  public boolean isDecimalFieldValid(String key) {
      DecimalField d = (DecimalField) fields.get(key);
      return d.isValid();
   }

  public boolean isTextFieldValid(String key) {
      JTextField t = (JTextField) fields.get(key);
      return (!t.getText().equals(""));
   }

   public void setIntegerFieldValue(String key, int v) {
      IntegerField i = (IntegerField) fields.get(key);
      i.setValue(v);
   }

   public void setDecimalFieldValue(String key, double v) {
      DecimalField d = (DecimalField) fields.get(key);
      d.setValue(v);
   }

   public void setTextFieldValue(String key, String v) {
      JTextField t = (JTextField) fields.get(key);
      t.setText(v);
   }

   public int getModifierType(String key) {
      ModifierCombo mc = (ModifierCombo) modifiers.get(key);
      String text = (String)mc.getSelectedItem();
      if(text.equals("<")) {
         return ValidatedForm.LESS_THAN;
      }
      else if(text.equals(">")) {
         return ValidatedForm.GREATER_THAN;
      }
      else if(text.equals("=")) {
         return ValidatedForm. EQUALS;
      }
      else if(text.equals("equals")) {
         return ValidatedForm.EQUALS;
      }
      else {
         return ValidatedForm.LIKE;
      }
   }

   public void setModifierType(String key, int t) {
      ModifierCombo mc = (ModifierCombo) modifiers.get(key);
      switch(t) {
      case ValidatedForm.LESS_THAN:
         mc.setSelectedIndex(0);
         break;
      case ValidatedForm.GREATER_THAN:
         mc.setSelectedIndex(1);
         break;
      case ValidatedForm.EQUALS:
         mc.setSelectedIndex(2);
         break;
      case ValidatedForm.LIKE:
         mc.setSelectedIndex(3);
         break;
      default:
         throw new IllegalArgumentException("Modifier type out of range!");
      }

   }

   private class ModifierCombo extends JComboBox {

      public ModifierCombo(boolean text) {
         super();
         if(text) initText();
         else     initNumber();
      }

      private void initText() {
         this.addItem("equals");
         this.addItem("like");
      }


      private void initNumber() {
         this.addItem("=");
         this.addItem("<");
         this.addItem(">");
      }

   }

}
