/*
 * @(#)SimpleFunctionPane.java  2.5.0  1 September 2006
 *
 * Copyright 2006
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import edu.neu.ccs.*;
import edu.neu.ccs.parser.*;

import java.awt.*;
import java.awt.font.*;
import javax.swing.*;
import java.util.*;

/**
 * <p>Class <code>SimpleFunctionPane</code>
 * provides a pane that permits
 * the interactive definition of simple functions,
 * that is, objects of class
 * <code>SimpleFunction</code>.
 * As part of the definition of a simple function,
 * the function is installed into the default parser.</p>
 * 
 * <p>Here is a description of the GUI.</p>
 * 
 * <p>A dropdown list provides the names of all
 * currently installed simple functions and
 * clicking on a name will bring up its current
 * definition.</p>
 * 
 * <p>A simple function definition may be viewed,
 * edited, or created from scratch using three
 * text fields for:</p>
 * 
 * <ul>
 *   <li>The function name as an identifier</li>
 *   <li>The function formal parameter list as a
 *       comma separated list of identifiers</li>
 *   <li>The function body that is an expression
 *       that uses the formal parameters and any
 *       other defined functions, constants, and
 *       variables.</li>
 * </ul>
 * 
 * <p>There are three actions attached to buttons:</p>
 * 
 * <ul>
 *   <li>Define Function: Defines a new simple function
 *       or replaces an existing simple function.</li>
 *   <li>Remove Function: Removes an existing simple
 *       function.</li>
 *   <li>Clear Fields: Clears the three text fields so
 *       work can start fresh.</li>
 * </ul>
 * 
 * <p>The GUI prevents the user from replacing or
 * removing an installed function whose class is not
 * equal to or derived from <code>SimpleFunction</code>.</p>
 * 
 * @author Richard Rasala
 * @version 2.5.0
 */
public class SimpleFunctionPane
    extends BasePane
{
    /** Reference to the default JPT parser. */
    protected BaseParser parser =
        ParserUtilities.getDefaultParser();
    
    
    /** The label for the functions dropdown list. */
    protected Annotation fcnsLabel =
        new Annotation("Existing Simple Functions", labelFont);
    
    /** The functions dropdown list. */
    protected Dropdown fcnsList =
        new Dropdown(fieldFont, smallFieldWidth);
    
    /**
     * <p>The action to select a function from the
     * functions dropdown list and show its definition.</p>
     * 
     * <p>Utilizes the method <code>selectFunction()</code>.</p>
     */
    protected SimpleAction selectFcn =
        new SimpleAction("Select Function") {
            public void perform() { selectFunction(); }
        };
    
        
    // Populate the functions dropdown list
    // Add the select functions action as a behavior
    //     for the functions dropdown list
    {
        populateFcnsList();
        fcnsList.addActionListener(selectFcn);
    }
    
    
    /** The label for the function name text field. */
    protected Annotation nameLabel =
        new Annotation("Function Name", labelFont);
    
    /** The function name text field. */
    protected TextFieldView nameTFV =
        new TextFieldView(fieldFont, smallFieldWidth);
        
    /** The label for the function parameters text field. */
    protected Annotation paramsLabel =
        new Annotation("Function Parameters", labelFont);
    
    /** The function parameters text field. */
    protected TextFieldView paramsTFV =
        new TextFieldView(fieldFont, smallFieldWidth);
        
    /** The label for the function body text field. */
    protected Annotation bodyLabel =
        new Annotation("Function Body", labelFont);
    
    /** The function body text field. */
    protected TextFieldView bodyTFV =
        new TextFieldView(fieldFont, largeFieldWidth);
    
    /**
     * The array with the 4 labels and correspond GUI
     * objects: the dropdown and the 3 text fields.
     */
    protected Object[] defFields =
        { fcnsLabel,   fcnsList,
          nameLabel,   nameTFV,
          paramsLabel, paramsTFV,
          bodyLabel,   bodyTFV    };
    
    /**
     * The table with the 4 labels and correspond GUI
     * objects: the dropdown and the 3 text fields.
     */
    protected VTable defFieldsTable =
        new VTable(defFields, gap, gap, WEST);
    
    
    /**
     * <p>The define function action.</p>
     * 
     * <p>Utilizes the method <code>defineFunction()</code>.</p>
     */
    protected SimpleAction defFun =
        new SimpleAction("Define Function") {
        public void perform() { defineFunction(); }
    };
    
    /**
     * <p>The remove function action.</p>
     * 
     * <p>Utilizes the method <code>removeFunction()</code>.</p>
     */
    protected SimpleAction remFun =
        new SimpleAction("Remove Function") {
        public void perform() { removeFunction(); }
    };
    
    /**
     * <p>The clear fields action.</p>
     * 
     * <p>Utilizes the method <code>clearFields()</code>.</p>
     */
    protected SimpleAction clear =
        new SimpleAction("Clear Fields") {
        public void perform() { clearFields(); }
    };
    
    
    /** The define function button. */
    protected JButton defFunButton = new JButton(defFun);
    
    /** The remove function button. */
    protected JButton remFunButton = new JButton(remFun);
    
    /** The clear fields button. */
    protected JButton clearButton = new JButton(clear);
    
    
    // Set the font for the buttons
    {
        defFunButton.setFont(buttonFont);
        remFunButton.setFont(buttonFont);
        clearButton.setFont(buttonFont);
    }
    
    
    /** The array with the 3 buttons. */
    protected Object[] defButtons =
        { defFunButton, remFunButton, clearButton };
    
    /** The table with the 3 buttons. */
    protected HTable defButtonsTable =
        new HTable(defButtons, gap, gap, CENTER);
    
    
    /** The array with the fields and the buttons. */
    protected Object[] mainStuff =
        { defFieldsTable, defButtonsTable };
    
    /** The table with the fields and the buttons. */
    protected VTable mainTable =
        new VTable(mainStuff, gap, gap, CENTER);
    
    
    // Apply borders to the main table
    //   1. Empty border
    //   2. Title border
    {
        mainTable.emptyBorder(gap);
        
        mainTable.titleBorder(
            " Simple Function Definition Pane ",
            labelFont,
            Color.black);
    }
    
    
    /**
     * The constructor for a simple function definition pane.
     */
    public SimpleFunctionPane() {
        add(mainTable);
    }
    
    
    /**
     * <p>Populate the functions dropdown list with the
     * names of all simple functions currently
     * installed in the default parser.</p>
     * 
     * <p>The names are sorted alphabetically and an
     * empty string is added to the front of the list
     * so that after the selection of a name the list
     * can revert to showing no name.</p>
     */
    protected void populateFcnsList() {
        String[] fcns = parser.simpleFunctionNames();
        
        int length = fcns.length;
        
        String[] list = new String[length + 1];
        
        list[0] = "";
        
        for (int i = 0; i < length; i++)
            list[i+1] = fcns[i];
        
        fcnsList.setItems(list);
    }
    
    
    /**
     * <p>The method to select a function from the
     * functions dropdown list and show its definition.</p>
     * 
     * <p>After the function definition is shown, reverts
     * the function list to showing the empty string.</p>
     */
    protected void selectFunction() {
        String name = (String) fcnsList.getSelectedItem();
        
        if (parser.isSimpleFunctionName(name)) {
            SimpleFunction function =
                (SimpleFunction) parser.getFunction(name);
            
            nameTFV.setViewState(function.name());
            paramsTFV.setViewState(function.parametersAsString());
            bodyTFV.setViewState(function.body());
            
            fcnsList.setSelectedIndex(0);
        }
    }
    
    
    /**
     * <p>Defines a function using the data in the 3 text fields.</p>
     * 
     * <p>Checks for as many errors as possible and provides an
     * informative dialog box when errors occur.</p>
     * 
     * <p>Cannot replace an existing function that is not a simple
     * function.</p>
     */
    protected void defineFunction() {
        String name = nameTFV.getViewState().trim();
        String body = bodyTFV.getViewState().trim();

        String[] parameters =
            Strings.splitCommaList(paramsTFV.getViewState());

        // check for errors
        String message = SimpleFunction.testFunctionName(name);
        
        if (message == null)
            message = SimpleFunction.testParameterArrayNames
                (parameters, name);
        
        if (message == null)
            message = SimpleFunction.testFunctionBody(body);
        
        if (message != null) {
            GeneralDialog.showOKDialog
                (message, "Simple Function Definition Error");
            
            return;
        }
        
        new SimpleFunction(name, parameters, body);
        
        populateFcnsList();
        clearFields();
    }
    
    
    /**
     * <p>Removes a function using the name in the function name
     * text field.  Ignores the other two text fields.</p>
     * 
     * <p>Cannot remove an existing function that is not a simple
     * function.</p>
     */
    protected void removeFunction() {
        String name = nameTFV.getViewState().trim();
        
        String message = null;
        
        if (parser.isSimpleFunctionName(name)) {
            parser.removeFunction(name);
            
            populateFcnsList();
            clearFields();
            
            return;
        }
        
        if (parser.isOrdinaryFunctionName(name)) {
             message = "You may not remove"
                + " an existing built-in function ";
        }
        else {
            message = "Function " + name + " is not installed";
        }
        
        GeneralDialog.showOKDialog
            (message, "Simple Function Removal Error");
    }
    
    
    /** Clears the 3 text fields. */
    protected void clearFields() {
        nameTFV.  setViewState("");
        paramsTFV.setViewState("");
        bodyTFV.  setViewState("");
    }
    
    
    /**
     * <p>The main method launches
     * a simple function definition pane
     * in its own GUI frame.</p>
     * 
     * <p>Use the call:</p>
     * 
     * <pre>  SimpleFunctionPane.main(null)</pre>
     * 
     * @param args ignored and may be <code>null</code>
     */
    public static void main(String[] args) {
        new SimpleFunctionPane().frame("Simple Function Pane");
    }
   
}

