// SplineFrame.java
//           demonstrate equations to draw a cubic Spline curve
//           and integrate

import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.io.*;
import javax.swing.*;


public class SplineFrame extends JFrame
{
  double x[] = new double[100];
  double y[] = new double[100];
  int n; // number of points so far
  boolean plot;
  int height=450; // 350;
  int width=450; // 300;
  int hw=400; // base height and width of point area
  int hwTemp=hw;
  final double xymax=100.0; // maximum value of x or y point
  double sca=(double)hw/xymax;
  double scaTemp=sca;
  int f1xOff=50;
  int f1yOff=50;
  int orderValues[] = {2, 3, -1, -2}; // -1 is code for N/2 -2 is code for N     
  int orderItemValue; // set to order code
  int optionItemValues[];
  JRadioButtonMenuItem orderItems[], options[];
  ButtonGroup optionGroup, orderGroup;
  MyJPanel panel;
  private JDesktopPane theDesktop;
  
  public SplineFrame()
  {
    n=0;
    plot=false;
    setTitle("SplineFrame demonstration");
    setSize(520,600); // 300,370
    setBackground(Color.white);
    setForeground(Color.black);

    // create menu bar and attach it to AwtMenuTest window
    JMenuBar bar = new JMenuBar();  
    setJMenuBar(bar);  

    // set up Algorithm menu
    JMenu algorithmMenu = new JMenu("Algorithm");
    bar.add(algorithmMenu);  // add algorithm menu to menu bar
    // set up Algorithm items
    JMenuItem splineItem = new JMenuItem("Spline fit");
    algorithmMenu.add(splineItem);
    JMenuItem evaluateItem = new JMenuItem("Evaluate");
    algorithmMenu.add(evaluateItem);
    JMenuItem integrateItem = new JMenuItem("Integrate");
    algorithmMenu.add(integrateItem);

    // set up Code menu
    JMenu codeMenu = new JMenu("Code");
    bar.add(codeMenu);       // add code menu to menu bar 
    // set up Code items
    JMenuItem splineClassItem = new JMenuItem("Spline class");
    codeMenu.add(splineClassItem);
    JMenuItem demonstrationItem = new JMenuItem("Demonstration");
    codeMenu.add(demonstrationItem);

    // set up Parameter menu
    JMenu parameterMenu = new JMenu("Parameters");  
    bar.add( parameterMenu );  // add parameter menu to menu bar 
    // create Order submenu
    String orders[] = {"2", "3", "N/2", "N" };
    JMenu orderMenu = new JMenu( "Order" );
    orderItems = new JRadioButtonMenuItem[ orders.length ];
    orderGroup = new ButtonGroup();
    ItemHandler itemHandler = new ItemHandler();
    // create Order radio button menu items
    for(int i=0; i<orders.length; i++)
    {
       orderItems[i] = new JRadioButtonMenuItem(orders[i]);
       orderMenu.add(orderItems[i]);
       orderGroup.add(orderItems[i]);
       orderItems[i].addActionListener(itemHandler);
    }
    // select first Order menu item
    orderItems[0].setSelected(true);  
    // add parameter menu to menu bar
    parameterMenu.add( orderMenu );
    parameterMenu.addSeparator();

    // create Option submenu
    String optionNames[] = {"Normal", "Other"};
    JMenu optionMenu = new JMenu("Option");
    options = new JRadioButtonMenuItem[ optionNames.length ];
    optionItemValues = new int[options.length];
    optionGroup = new ButtonGroup();
    // create Option radio button menu items
    for(int i=0; i<options.length; i++ )
    {
      options[i] = new JRadioButtonMenuItem(optionNames[i]);
      optionMenu.add(options[i]);
      optionGroup.add(options[i]);
      options[i].addActionListener(itemHandler);
    }
    // select first Option menu item
    options[0].setSelected(true);
    // put Option menu in Parameters menu
    parameterMenu.add(optionMenu);

    // set up Help menu
    JMenu helpMenu = new JMenu("Help");  
    bar.add(helpMenu);  // add help menu to menu bar 
    // set up Help items
    JMenuItem helpItem = new JMenuItem("Running demonstration");
    helpMenu.add(helpItem);
    JMenuItem aboutItem = new JMenuItem("About");
    helpMenu.add(aboutItem);

    // set up plot area
    theDesktop = new JDesktopPane();
    getContentPane().add(theDesktop);
    // Create internal frame
    JInternalFrame frame = new JInternalFrame(
        "Plot area", true, true, true, true);
    // attach panel to internal frame
    Container container = frame.getContentPane();
    panel = new MyJPanel();
    container.add(panel, BorderLayout.CENTER);
    // set size of internal frame to size of its contents
    frame.pack(); // uses class Dimension
    theDesktop.add(frame);
    panel.addMouseListener (new mousePressHandler());
    frame.setVisible(true);

    splineItem.addActionListener(new ActionListener()
          {public void actionPerformed(ActionEvent event)
          { displayHelp("SplineAlgorithm.txt");
          }});
    evaluateItem.addActionListener(new ActionListener()
          {public void actionPerformed(ActionEvent event)
          { displayHelp("SplineEvaluate.txt");
          }});
    integrateItem.addActionListener(new ActionListener()
          {public void actionPerformed(ActionEvent event)
          { displayHelp("SplineIntegrate.txt");
          }});
    splineClassItem.addActionListener(new ActionListener()
          {public void actionPerformed(ActionEvent event)
          { displayHelp("Spline.java");
          }});
    demonstrationItem.addActionListener(new ActionListener()
          {public void actionPerformed(ActionEvent event)
          { displayHelp("SplineFrame.java");
          }});
    helpItem.addActionListener(new ActionListener()
          {public void actionPerformed(ActionEvent event)
          { displayHelp("SplineHelp.txt");
          }});
    aboutItem.addActionListener(new ActionListener()
          {public void actionPerformed(ActionEvent event)
          { displayHelp("SplineAbout.txt");
          }});
    addWindowListener(new WindowAdapter()
    {
      public void windowClosing(WindowEvent e)
      {
        System.exit(0);
      }
    });
 
    setVisible(true);
  } // end constructor SplineFrame

  class mousePressHandler extends MouseAdapter
  {
    public void mousePressed (MouseEvent e)
    {
      int xp, yp, b;

      // width = getSize().width;
      // height = getSize().height;
      // System.out.println("width="+width+",  height="+height);
      // if(width<400) width=400; // do not shrink more than initial
      // if(height<450) height=450;
      // hwTemp=Math.min(width-100, height-150);
      scaTemp=(double)hwTemp/xymax;
      
      if(plot)
      {
        plot=false; // ready for next set of points
        n=0;
      }
      xp = e.getX();
      yp = e.getY();
      b = e.getButton();
      if((b==3 && n>3) || n>98) // right button and 4 points or too many
      {
        plot=true;
        requestFocus();
        System.out.println("plot"); // debug print
        panel.repaint();
        return;
      }
      if(b!=1) return;
      x[n]=(double)(xp-f1xOff)/sca;
      if(x[n]<0.0)  x[n]=0.0;
      if(x[n]>xymax) x[n]=xymax;
      y[n]=(double)(-yp+f1yOff+hw)/sca;
      if(y[n]<0.0)  y[n]=0.0;
      if(y[n]>xymax) y[n]=xymax;
      // check no infinite slope
      for(int i=0; i<n; i++) if(Math.abs(x[n]-x[i])<0.01) { n--; break; }
      n++;
      requestFocus();
      System.out.println("at x="+xp+"   y="+yp+"   b="+b); // debug print
      panel.repaint();
    }
  } // end mousePressHandler

  class MyJPanel extends JPanel
  {
    MyJPanel(){} // default, just to have "paint" dependent

    public void paintComponent(Graphics g)
    {
      hw = hwTemp; // may have changed, only detected on mouse action
      sca = scaTemp;
 
      String pmax=new String("100");   
      int f1xC=f1xOff+hw/2;
      int f1yC=f1yOff+hw/2;
    
      g.clearRect(0, 0, 1000, 1000); // cover non-erase bug
      g.setColor(Color.blue);
      g.drawRect(f1xOff, f1yOff, hw, hw);
      g.drawString(pmax, f1xOff-26,   f1yOff+4);
      g.drawString("  0", f1xOff-20,   f1yOff+hw+4);
      g.drawString("  0", f1xOff-4,    f1yOff+hw+15);
      g.drawString(pmax, f1xOff+hw-10, f1yOff+hw+15);
      g.drawString("  y",  f1xOff-20,  f1yC);
      g.drawString("  x",  f1xC-10,    f1yOff+hw+15);
  
      g.setColor(Color.red);
      Font cur18 = new Font("courier", Font.BOLD, 18); 
      g.setFont(cur18);
      g.drawString("SplineFrame demonstration", 10, 15);
      Font cur14 = new Font("courier", 0, 14); 
      g.setFont(cur14);
  
      // draw points
      g.setColor(Color.red);
      for(int i=0; i<n; i++)
        g.fillOval((int)(f1xOff+x[i]*sca-3), (int)(f1yOff-y[i]*sca+hw-3), 6, 6);
   
      if(!plot) return;
      
      // compute Spline coefficients
      double xx[] = new double[n];
      double ff[] = new double[n];
      double xmin=x[0];
      double xmax=x[0];
      int ixmin=0;
      for(int i=0; i<n; i++)
      {
        xx[i]=x[i];
        ff[i]=y[i];
        if(x[i]<xmin) { xmin=x[i]; ixmin=i; }
        if(x[i]>xmax) xmax=x[i];
      }
      Spline s1 = new Spline(xx, ff);
      
      // plot curve
      double dx = (xmax-xmin)/100.009;
      double xp, yp, xt, yt;
      g.setColor(Color.black);
      xp=x[ixmin];
      yp=y[ixmin];
      for(int i=1; i<=100; i++)
      {
        xt = xmin+(double)i * dx;
        yt = s1.spline_value(xt);
        if(xt>xymax)xt=xymax; // crop the plot 
        if(xt<0.0)xt=0.0;
        if(yt>xymax)yt=xymax;
        if(yt<0.0)yt=0.0;
        // plot segment
        g.drawLine((int)(f1xOff+xp*sca), (int)(f1yOff-yp*sca+hw),
                   (int)(f1xOff+xt*sca), (int)(f1yOff-yt*sca+hw));
        
        xp=xt;
        yp=yt;
      }
      xt = s1.integrate();
      g.setColor(Color.blue);
      g.drawString("integral="+xt, 50, 30);
    } // end paint

    // return dimensions for sizing
    public Dimension getPreferredSize()
    {
      return new Dimension(500, 500); // 300,350
    }
  } // end MyJPanel

  void displayHelp(String file_name)
  {
    Frame f = new Frame();
    f.setTitle("Menu Information");
    f.setSize(600, 400);
    f.setLocation(400, 100);
    TextArea t = new TextArea("", 24, 80, TextArea.SCROLLBARS_BOTH);
    Font cur12 = new Font("courier", 0, 12); 
    t.setFont(cur12);
    try
    {
      BufferedReader in = new BufferedReader(new FileReader(file_name));
      String input_line;
      input_line = in.readLine();
      while(input_line != null)
      {
        t.append(input_line+"\n");
        input_line = in.readLine();
      }
    }
    catch(IOException exception)
    {
      System.out.println(exception);
    }
    f.add(t);
    f.addWindowListener(new WindowAdapter()
                            { public void windowClosing(WindowEvent e)
                            { e.getWindow().dispose(); }});
    f.setVisible(true);              
  } // end displayHelp  

  // class to handle action events from order and option items
  private class ItemHandler implements ActionListener
  {
    // process order and option selections
    public void actionPerformed( ActionEvent event )
    {
      // process order selection
      for(int i=0; i<orderItems.length; i++)
      {
        if(orderItems[i].isSelected())
        {
          orderItemValue = orderValues[i];
          break;
        }
      }
      // process option selection
      for(int i=0; i<options.length; i++)
      {
     optionItemValues[i]=0;
        if(event.getSource() == options[i])
        {
          optionItemValues[i]=1;
        }
      }
    }
  }  // end class ItemHandler

  public static void main (String[] args) // no args expected
  {
    new SplineFrame();
  } // end main
} // end class SplineFrame

