/***************************************************************\
 * Program: DispConWeb                                         *
 * Author: Steve Palmer               Date: June 9, 2007        *
 *         (remlaps@ccil.org)                                  *
 * Purpose:                                                    *
 *   This program provides a graphical front end for Integer   *
 * base conversion.                                            *
 *                                                             *
 * Modeled after "Swing: A Beginner's Guide", Schildt, P. 37   *
 *                                                             *
 * Revision History:                                           *
 *   April 29, 2007 - Initial Development                      *
 *   May 6, 2007 - Version 1.1 checked into new RCS library.   *
 *   June 9, 2007 - Forget about RCS.  Playing with netbeans   *
 *                  now.  That requires a new directory        *
 *                  structure.                                 *
 *                - Added error checking for:                  *
 *                  o Negative integers                        *
 *                  o illegal digits                           *
 *                  o Empty input numeral.                     *
 *                - Default to inbase=16, outbase=10.          *
 *                                                             *
 * Files:                                                      *
 *   DispCon.java-                                             *
 *      This file.                                             *
 *   Normalize.java-                                           *
 *      Digit normalization                                    *
 *      base B to base (B*n)                                   *
 *      base B to base (B/n)                                   *
 *   PascaltT.java-                                            *
 *      Matrix multiplication                                  *
 *      Pascal's Triangle and its powers                       *
 *      base B to base (B+n)                                   *
 *      base B to base (B-n)                                   *
 *                                                             *
 * Wish List, Possible Improvements:                           *
 * - Move legal digit check to the same pass with building     *
 *   the coefficient list.                                     *
 * - Convert base to base using LCM and two passes of the      *
 *   multiple routines instead of Pascal's triangle.           *
 * - Add option to show steps.                                 *
 *                                                             *
 * Known Bugs:                                                 *
 *    - Unreported memory overflow for too many digits.        *
 *                                                             *
 * \***************************************************************/
import java.awt.*;          // For swing
import java.awt.event.*;    // For swing
import javax.swing.*;       // For swing
import java.math.*;         // Needed for BigInteger
import java.util.*;         // Needed for Vector and/or ArrayList

public class DispConWeb extends JApplet implements ActionListener {
    JTextField jtfInString;      // java text field - input numeral.
    JTextField jtfOutString;     // java text field - output numeral.
    JTextField jtfInBase;        // java text field - input base.
    JTextField jtfOutBase;       // java text field - output base.
    
    /**
     *
     * @return
     */
    public DispConWeb() {
        // JApplet j_app= new JApplet();
        
        // Specify FlowLayout for the layout manager.
        getContentPane().setLayout(new FlowLayout());
        
        // Give the frame an initial size.
        setSize(500,210);
       
        // Create Labels
        JLabel j_labInString = new JLabel("  Numeral to convert");
        JLabel j_labOutString = new JLabel("  Converted numeral");
        JLabel j_labInBase =
                new JLabel(" Input base (base 10 representation)");
        JLabel j_labOutBase =
                new JLabel(" Output base (base 10 representation)");
        JLabel j_labINSTR = new JLabel
           ("Enter a numeral and bases from -94 through 94, excluding -1,0 and 1");
        
        // Create four text field instances
        jtfInString = new JTextField(30);
        jtfOutString = new JTextField(30);
        jtfOutString.setEditable(false);
        jtfInBase = new JTextField(8);
        jtfOutBase = new JTextField(8);
        
        // Set the action commands for the text fields.
        jtfInString.addActionListener(this);
        jtfOutString.addActionListener(this);
        jtfInBase.addActionListener(this);
        jtfOutBase.addActionListener(this);
        
        // Add the text fields and labels to the content pane.
        getContentPane().add(j_labINSTR);
        getContentPane().add(j_labInString);
        getContentPane().add(jtfInString);
        getContentPane().add(j_labOutString);
        getContentPane().add(jtfOutString);
        getContentPane().add(j_labInBase);
        getContentPane().add(jtfInBase);
        getContentPane().add(j_labOutBase);
        getContentPane().add(jtfOutBase);
        
        // Create push button instances.
        JButton j_butonConvert = new JButton("Convert");
        JButton j_butonReset = new JButton("Reset");
        
        // Add action listeners for the buttons.
        j_butonConvert.addActionListener(this);
        j_butonReset.addActionListener(this);
        
        // Add the buttons to the content pane.
        getContentPane().add(j_butonConvert);
        getContentPane().add(j_butonReset);
        
        // Initialize fields to reset() values.
        pageReset();
        
        // Display the frame.
        // setVisible(true);
    }
    
    // Handle action events.
    public void actionPerformed(ActionEvent ae) {
        if (ae.getActionCommand().equals("Reset")) {
            pageReset();
        } else {
            // Convert the string.                        
            Vector <BigInteger> CoeffList = new Vector<BigInteger>();
            Integer inBase, outBase;
            
            // Obtain the plain text and put it into Integers.
            try {
                   String tempStr = jtfInBase.getText();
                   inBase = Integer.parseInt(tempStr);
                }
            catch (NumberFormatException NFE)
               {
                   jtfOutString.setText("### NOTINT Input Base Error ###");
                   return;
               }
         
            try {
                   String tempStr = jtfOutBase.getText();
                   outBase = Integer.parseInt(jtfOutBase.getText());
                }
            catch(NumberFormatException NFE)
               {
                   jtfOutString.setText("### NOTINT Output Base Error ###");
                   return;
                }

            // Convert numeral string into coefficient list.
            CoeffList = Normalize.strToList(jtfInString.getText());
            
            // Store number of digits/coefficients.
            int numParams[] = new int[1];
            numParams[0] = jtfInString.getText().length();
            // Normalize.showList(CoeffList,numParams);
            /*
             * Look for bases with abs(base) > 94 or abs(base) = {0,1}
             */
            if ( isKnownBase (outBase, inBase))
            {
                jtfOutString.setText("### Invalid Base Error ###");
                return;
            }
            
           /*
            * Look for negative integers.
            */
            if (((CoeffList.size() % 2) == 0) && (inBase < 0))
            {
                jtfOutString.setText("### Negative Integer Error ###");
                return;
            }
            
           /*
            * Look for empty input numeral.
            */
            if (CoeffList.size() == 0) {
                jtfOutString.setText("### No inNUM Error ###");
                return;
            }
            
            /*
             * Make sure digits are valid.  This would be better to do
             * when building CoeffList.
             */
             for (int lcv=0; lcv<CoeffList.size(); lcv++) {
                if (  CoeffList.elementAt(lcv).compareTo(
                       BigInteger.valueOf(Math.abs(inBase))
                   ) >= 0)
             {
                 jtfOutString.setText("### Illegal Digit Error ###");
                 return;
             }
         }
            
            /*
             * Perform base conversion.
             */
            if ((( inBase / outBase ) * outBase) == inBase ) {
                  /*
                   * Convert numeral where inBase is a multiple of outBase.
                   */
                 Normalize.bReduce(CoeffList, inBase/outBase, outBase,
                        numParams);
            } else if ((( outBase / inBase ) * inBase) == outBase ) {
                /*
                 * Convert numeral where outBase is a multiple of inBase.
                 */
                Normalize.bGrow(CoeffList, outBase/inBase, outBase,
                        numParams);
            } else {
               /*
                * Convert numeral using Pascal's triangle and an offset.
                */
                int Multiple;
                if ( inBase < outBase ) {
                    Multiple = outBase - inBase;
                } else {
                    Multiple = inBase - outBase;
                }
                int size = numParams[0];
                BigInteger[][] PasMat = PascalT.initMat(numParams[0]);
                BigInteger[][] ProdMat;
                if ( Multiple > 1 ) {
                   /*
                    * Initialize Pascal's triangle matrix and raise it
                    * to the power given by offset.
                    */
                    ProdMat = PascalT.multMat             // Initialize
                            (PasMat, PasMat, size, size, size, size);
                    for (int lcv=2; lcv<Multiple; lcv++) {  // Raise to a power
                        ProdMat = PascalT.multMat
                             (PasMat, ProdMat, size, size, size, size);
                    }
                } else {    // Offset is 1.
                    /*
                     * Initialize Pascal's triangle matrix for number of digits
                     * from numParams[0].
                     */
                    ProdMat = PascalT.initMat(numParams[0]);
                }
                
               /*
                * Put coefficient List into a matrix.
                */
                // stuffMat should not be in Normalize.
                BigInteger DigitMat[][] = Normalize.stuffMat
                        (CoeffList, numParams);
                if ( outBase > inBase ) {
                   /*
                    * If inter-base offset is positive, negate Pascal's triangle.
                    */
                    ProdMat = PascalT.negPascal(ProdMat,size);
                }
                
                /*
                 * Matrix Multiply coefficients by Pascal's triangle.
                 */
                 BigInteger ResultMat[][] = PascalT.multMat
                        (DigitMat,ProdMat, 1, size, size, size );
                
                /*
                 * Retrieve ceofficient list from matrix.
                 */
                //dumpMat should not be in Normalize.
                CoeffList = Normalize.dumpMat(ResultMat, numParams);
            }
            
            /*
             * Normalize coefficients into legal digits.
             */
            Normalize.normalDigs(CoeffList, outBase, numParams);
            jtfOutString.setText
                    (Normalize.listToNum
                       (CoeffList, numParams));
            
            // End Convert button action.
        }
    }
    
    public static void init(String args[]) {
        // Create the frame on the event dispatching thread.
        try {
               SwingUtilities.invokeLater(new Runnable() {
                  public void run() {
                     new DispConWeb();
                  }
               });
        } catch (Exception exc) {
            System.err.println("Can't create because of " + exc);
            return;
        }
    }

    private void pageReset() {
        /*
         * Reset chosen.  Initialize GUI.
         */
          jtfInString.setText("");
          jtfOutString.setText("");
          jtfInBase.setText("16");
          jtfOutBase.setText("10");    
        // End Reset button action.
    }

    private boolean isKnownBase(Integer outBase, Integer inBase) {
       return (
                 ( outBase == 0 || inBase == 0) ||     // Base == 0 error
                 (inBase > -2 && inBase < 2) ||     // Input base in (-1,1) error
                 (outBase > -2 && outBase < 2 ) ||  // Output base in (-1,1) error
                 (Math.abs(inBase) > 94) ||         // |Input base| > 94 error
                 (Math.abs(outBase) > 94)           // |Output base| > 94 error
              );
    }
}
