/*
 * Created on Sep 3, 2005
 * Copyright 2005 Program of Computer Grpahics, Cornell University
 */
package resample.function;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EtchedBorder;

import resample.MainFrame;


/**
 * All filters for this assignment are built from one dimensional functions.  This
 * class is the parent of all such functions.  You will have to implement the 
 * getSupport() and evaluate(double x) methods for all filter functions.
 * 
 * @author arbree
 * Sep 3, 2005
 * FilterFunction.java
 * Copyright 2005 Program of Computer Graphics, Cornell University
 */
public abstract class FilterFunction implements ActionListener {

  /**
   * Returns the canonical support radius of this function.
   * 
   * @return the support of the function
   */
  public double getSupport() {
    
    return 1.0;
    
  }
  
  /**
   * Returns the value of the function at x.
   * 
   * @param x the requested location
   * @return the value of the function
   */
  public double evaluate(double x) {
    
    return 0.0;
    
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////////
  // Methods needed to create and manage options panels.  DO NOT MODIFY.
  ////////////////////////////////////////////////////////////////////////////////////////////////////

  //String for update button
  private static final String UPDATE_CMD = "Update";
  
  /**
   * Text fields in the options panel
   */
  protected JTextField[] textFields;
  
  /**
   * The options panel for this function.
   */
  protected JComponent optionsPanel = null;
  
  /**
   * @return a list of the field names to set as options, default is null.
   */
  protected String[] getOptionsFields() {
    
    return null;
    
  }
  
  /**
   * Returns an empty options panel by default
   * @return the options panel
   */
  public final JComponent getOptionsPanel() {

    //Get the fields to set in the options panel
    String[] optionsFields = getOptionsFields();
    
    //If there are no fields return a null panel
    if(optionsFields == null)
      return null;
    
    //Otherwise build a panel
    optionsPanel = new JPanel(new BorderLayout());
    optionsPanel.setBorder(new CompoundBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(2,5,2,2)));
    
    //Build a panel to hold the field text inputs
    JPanel textPanel = new JPanel(new GridLayout(optionsFields.length, 1));
    
    //Add the fields
    Class thisClass = this.getClass();
    Field currField = null;
    textFields = new JTextField[optionsFields.length];
    for(int i = 0; i < optionsFields.length; i++) {

      //Try and get the field
      try {
        currField = thisClass.getDeclaredField(optionsFields[i]);
        textFields[i] = new JTextField(currField.get(this).toString());
        textFields[i].addActionListener(this);
        textFields[i].setActionCommand(optionsFields[i]);
        textPanel.add(MainFrame.addLabelLeft(textFields[i], optionsFields[i]+":"));
      }
      catch (SecurityException e) {
        throw new Error("Cannot access field, "+optionsFields[i]+", in class, "+thisClass.getName());
      }
      catch (NoSuchFieldException e) {
        throw new Error("Field, "+optionsFields[i]+", does not exist in class, "+thisClass.getName());
      }
      catch (Exception e) {
        throw new Error("Unrecoverable error while setting Field, "+optionsFields[i]+", in class, "+thisClass.getName());
      }
    }
    
    //Setup the final panel
    optionsPanel.add(textPanel, BorderLayout.CENTER);
    JButton button = new JButton("Update");
    button.setActionCommand(UPDATE_CMD);
    button.addActionListener(this);
    optionsPanel.add(button, BorderLayout.EAST);
    
    return optionsPanel;
    
  }

  /**
   * If the input class is a primitive class, this method returns its wrapper.
   * @param inClass the input class
   * @return the wrapper for inClass, or inClass if its not a primitive type
   */
  private Class resolvePrimitives(Class inClass) {
    
    if(!inClass.isPrimitive())
      return inClass;
    
    if(inClass == Void.TYPE)
      throw new Error("Class types of fields cannot be void.");
    
    if(inClass == Byte.TYPE)
      return Byte.class;
    if(inClass == Short.TYPE)
      return Short.class;
    if(inClass == Integer.TYPE)
      return Integer.class;
    if(inClass == Long.TYPE)
      return Long.class;
    if(inClass == Float.TYPE)
      return Float.class;
    if(inClass == Double.TYPE)
      return Double.class;
    
    return Boolean.class;
    
  }
  
  /**
   * Handle events from the options panels
   * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
   */
  public final void actionPerformed(ActionEvent e) {

    //Get the name of the field issuing the command
    String cmd = e.getActionCommand();
    
    //If the update button is pressed
    if(cmd == UPDATE_CMD) {
      
      //Fire updates for all fields in the options panel
      for(int i = 0; i < textFields.length; i++)
        textFields[i].postActionEvent();
      return;
      
    }
      

    //Try and set the field to the value in the text box
    try {
      
      Field fld = this.getClass().getDeclaredField(cmd);
      JTextField txtFld = (JTextField) e.getSource();  //The only sources should be text fields
      
      //Construct an instance of the field class (resolve primitives to their wrappers)
      Class fldCls = resolvePrimitives(fld.getType());
      
      //Find a constructor that takes a string (should work for all primitives and Strings)
      Constructor fldConst = fldCls.getConstructor(new Class[] {String.class});
      
      //Create an instance of the field class with the string from the text field
      Object fldData = fldConst.newInstance(new Object[] {txtFld.getText()});
      
      //Set the field
      fld.set(this, fldData);
      
    }
    catch (InvocationTargetException e2) {
      
      //If the only problem with invocation is number format do nothing
      if(!(e2.getCause() instanceof NumberFormatException))
        throw new Error("Error when processing action command: "+cmd);
      
    }
    catch (Exception e1) {
      throw new Error("Error when processing action command: "+cmd);
    }
    
  }
}
