import ChaosDemos.*;
import java.awt.*;
import java.util.*;

//***********************************************************************
/** Iterates and plots the quadratic and sine maps, and demonstrates
* functional composition and rescaling.<br>
* Uses the "Java Graph Class Library" by Leigh Brrokshaw
* @author Michael Cross
* @version 3 August, 1997
*/
//**********************************************************************/

public class Scalemap extends dynamicGraph {

/** Number of points in plot of f(x) */
      int np = 200;
/** nit = 2^nf -> functional composition */
      int nit=1;
/** number of curves */
      int ncurve=-1;
/** index of iteration curve */      
      int iterateCurve;
/** iterations to eliminate transients  */
      int trans=0;
/** compositions -> nit */
      int nf=0;
/** scaling factor: f and x are rescaled by (-alpha)^nsc */
      int nsc=0;
/** sleep delay in ms in dynamic iteration */
      int delay=50;
/** function: 0=Quadratic; 1=Sine */
      int nfun=0;
/** map variable */
      double x;               // x
/** iterate of x */      
      double f; 
/** map parameter */
      double a=3.7;
/** string value of a: used because of string truncation */    
      String atext;   
/** initial value of x - scale */
      double xmin=1.0;
/** rescaled value of x- scale  */
      double xscale;
/** starting value of x */
      double x0=0.35;

/** constants */      
      private static double alpha=-2.502907875;
      private static double Pi=3.141592654;

/** GUI items */      
      private textControls variables,acontrol;
      buttonControls buttons;
      private ScalemapControls choices;
      private Panel leftPanel;

/** parent */      
      private startMap outerparent;      
/** animation thread */
      private Thread aThread=null;
/** classes used */            
      private superGraph2D graph;
      private movie theMovie;      

//*********************************************************************
/**
* @param target starting class
* @see startMap
*/
//*********************************************************************
        Scalemap(startMap target) {
        graph = new superGraph2D(this);
        theMovie = new movie(this);        
        xscale=xmin;
        x=(x0-0.5)*xscale; 
        this.outerparent = target;   

        setLayout( new GridLayout(1,2) );
        
        graph.setXAxisTitle("x");     
        graph.setYAxisTitle("f(x)");  
        leftPanel = new Panel();
        leftPanel.setLayout(new BorderLayout());
        leftPanel.add("Center",graph);
        leftPanel.add("South",theMovie);
        theMovie.scrollStart = 4;
        theMovie.setScroll();
              
        add(leftPanel);           

        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
 
        Panel rightPanel = new Panel();
        rightPanel.setLayout(gridbag);
        add(rightPanel);

//      c.fill = GridBagConstraints.BOTH;
        c.gridheight = 2;
        c.gridwidth=1;
        c.weightx=1.;
        c.weighty = 1.0;
        String[] buttonLabels={"   Reset    ","   Clear   ",
                   "    Step   ","    Start   ","    Stop   "};
        buttons = new buttonControls((dynamicGraph) this, 
                        buttonLabels,buttonLabels.length);
        buttons.b_init[0] = true;
        buttons.b_stopped[4] = false;
        buttons.b_started[1] = true;
        buttons.b_started[4] = true;
        buttons.setup();
        gridbag.setConstraints(buttons, c);
        rightPanel.add(buttons);

        String[] textboxes = {String.valueOf(nf),String.valueOf(nsc),
                    String.valueOf(x0),String.valueOf(xmin),
                    String.valueOf(trans)};
        String[] labels = {"    nf","   nsc","    x0",
                             "scale"," trans" };
        variables = new 
              textControls((dynamicGraph) this,textboxes,labels,textboxes.length);        
        c.gridwidth = GridBagConstraints.REMAINDER;
        gridbag.setConstraints(variables, c);
        rightPanel.add(variables);
                           
        String[] abox={String.valueOf(a)};
        String[] alabel={" a = "};
        acontrol = new textControls((dynamicGraph) this,abox,alabel,1);
        c.gridheight = 1;
        c.gridwidth=GridBagConstraints.REMAINDER;        
        gridbag.setConstraints(acontrol, c);
        rightPanel.add(acontrol);

        choices = new ScalemapControls(this,4);
        c.weightx = 0.0;
        c.weighty=0.0;
        c.gridheight=1; 
        gridbag.setConstraints(choices, c);
        rightPanel.add(choices);
        graph.clearAll=true;
        repaint();
        if(target.inputParameters) {
            choices.setChoices(target.function, target.sign,
                        target.point,target.index);
            choices.setFromChoices();
            if(target.atext.length()>0) {
                 nfun=target.function;
                 acontrol.setText(0,target.atext);
                 atext="";
            }                 
            for(int i=0;i<target.nVariables;i++)
                  if(target.variableBoxes[i].length() != 0)
                        variables.setText(i,target.variableBoxes[i]);                                     
        }         
        buttons.enableGo();
        updateParameters();        
      }

//**********************************************************************      
/**
* Respond to buttonControls
* @see      buttonControls
* @param    buttonIndex index of button pushed
*/          
//********************************************************************** 
      public void respondToButtons(int buttonIndex) {
            boolean dummy;
            if(buttonIndex==0) {
                 buttons.enableGo();
                 updateParameters();  
            }
            if(buttonIndex==2) {
                dummy=iterate();
                graph.clearAll=false;
                graph.repaint();
            }
            else if(buttonIndex==1) {
                graph.clearAll=false; //6/27/96
                if(aThread != null) {
                  movieStop();
                  restart();
                  movieStart();
                }
                else restart();                
            }
//            else if(buttonIndex==2) {
//                 graph.clearAll=true;
//                 graph.repaint();
//                 }
            else if(buttonIndex==3) {
                buttons.disableGo();
                disableAll();
                movieStart();
            }
            else if(buttonIndex==4) {
                buttons.enableGo();
                movieStop();
                enableAll();
            }
      }

//**********************************************************************
/**
* Responds to textControls
* @see textControls
*/      
//**********************************************************************      
      public void respondToText() {
          updateParameters();
          buttons.enableGo();
      }      

//*********************************************************************
/**
* Stop movie thread 
*/  
//*********************************************************************
      public void movieStop() {
                if(aThread!=null) {
                    theMovie.stopIterate();
//                  aThread.stop();
                    aThread=null;
                } 
      }
//*********************************************************************
/**
* Start movie thread 
*/  
//*********************************************************************

      public void movieStart() {
                if(aThread==null) {
                    aThread = new Thread(theMovie);
//                    aThread.setPriority(Thread.MIN_PRIORITY);
                    aThread.start();                    
                }
      }

//************************************************************************
/**
* Event handler:
* Stops iteration on minimising and handles close window event<br>
* (May fail under Windows95)
*/
//************************************************************************     
        public boolean handleEvent(Event evt) {
            switch (evt.id) {
                case Event.WINDOW_DESTROY:
                    movieStop();
                    outerparent.hideWindow();
                    return super.handleEvent(evt);
                case Event.WINDOW_ICONIFY:
                    movieStop();
                    buttons.enableGo();
                    enableAll();
//                  System.out.println("Window minimized");
                    return super.handleEvent(evt);              
                default:
                    return super.handleEvent(evt);
            }
       }
       
//*********************************************************************
/**
* Disables text input and choice contorls
*/ 
//*********************************************************************
      
      public void disableAll() {
          for(int i=0;i<variables.ncontrols();i++)
                                     variables.disableText(i);
          for(int i=0;i<choices.ncontrols();i++) {
            choices.t[i].disable();
            choices.c[i].disable();
          }
          acontrol.disableText(0);
      }

//*********************************************************************
/**
* Enables text input and choice contorls
*/ 
//*********************************************************************

      public void enableAll() {
          for(int i=0;i<variables.ncontrols();i++)
                                    variables.enableText(i);
          for(int i=0;i<choices.ncontrols();i++) {
            choices.t[i].enable();
            choices.c[i].enable();
          }
          acontrol.enableText(0);
      }

//*********************************************************************
/**
* Resets the parameter a and function number. Stores atext 
* for later comparison (because string truncates accuracy of double)
*/
//*********************************************************************
      
      public void seta(double ain, int n) {
           nfun=n;
           a=ain;
           atext=String.valueOf(a);
           acontrol.setText(0,atext);
           atext=acontrol.getText(0);
      }

//*********************************************************************
/**
* Returns map function of class variable x 
* @param n number of iterations
*/ 
//*********************************************************************

      public double function(int n) {
            double xit = x;
            if(nfun==0) {
                for(int i=1;i<=n;i++) {
                    xit=a*(0.25-xit*xit)-0.5;
                }
            }
            else if (nfun==1) {
                for(int i=1;i<=n;i++) {
                    xit=0.25*a*Math.cos(Pi*xit)-0.5;
                }
            }
            return xit;
      }

//*********************************************************************
/**
* Updates parameters from the text controls 
*/ 
//*********************************************************************
      
      public void updateParameters() {
            int i;
            
            if(nfun==0) graph.setTitle("Quadratic Map");
            else if(nfun==1) graph.setTitle("Sine Map");           
//          Update a value only if entered directly into text box            
            if(! atext.equals(acontrol.getText(0))) {
                a=acontrol.parseTextField(0,a, 0., 4.);
                atext=acontrol.getText(0);
            }

            xmin=variables.parseTextField(3, xmin);
            x0=variables.parseTextField(2, x0, 0., 1.);
//            x0=x0-Math.rint(x0);
//            if(x0<0) x0++;
            variables.setText(2,String.valueOf(x0));
            trans=variables.parseTextField(4,trans,true);
            nf=variables.parseTextField(0,nf,true);
            for(i=1,nit=1;i<=nf;i++) {nit=nit*2;}
            nsc=variables.parseTextField(1,nsc,true);
            for(i=1,xscale=xmin;i<=nsc;i++) { 
                xscale=xscale/alpha;
            }
            if(xscale!=1.0) {
                graph.setXAxisTitle     //MCC 3/19/97
                       ("x: Full scale = " + String.valueOf((float)xscale));
            }
            else graph.setXAxisTitle("x");   //MCC 3/19/97
            if(nit==1) graph.setYAxisTitle("f(x)");
            else graph.setYAxisTitle("f{^ "+nit+"}(x)");
            x=(x0-0.5)*xscale;
            x=function(nit*trans);
            restart();
      }      

                
//*********************************************************************
/**
* Resets plot by deleting all curves and restarting
*/ 
//*********************************************************************
      
      public void restart() {
            int i,j,ip;
            double data[] = new double[2*np];
            double xb;      
            
            graph.clearAll=true;
            xb=x;
            if(ncurve>=0) ncurve = graph.deleteAllCurves();

            data[0]=-0.5+0.5;
            data[1]=-0.5+0.5;
            data[2]=0.5+0.5;
            data[3]=0.5+0.5;

            ncurve = graph.addCurve(data,2,Color.black);
            ip=0;
            for(i=j=0; i<np; i++) {
                x =xscale*(-0.5+((double) i)/((double)(np-1)));
                double xdum=x/xscale;
                f = function(nit);
                double fdum=f/xscale;
                if((xdum>=-0.5) && (xdum<=0.5) && (fdum>=-0.5) && (fdum<=0.5)){
                      data[j] = xdum+0.5;
                      data[j+1] =fdum+0.5;
                      j+=2;
                      ip++;
//                    System.out.println("ip= "+ip+" xdum= "+xdum+" fdum= "+fdum);
                }
/*                if(xdum>=-0.5) {
                  if (xdum<=0.5){
                     if(fdum>=-0.5){
                       if(fdum<=0.5){
                              data[j] = xdum+0.5;
                              data[j+1] =fdum+0.5;
                              j+=2;
                              ip++;
                              System.out.println("i[= "+ip+" xdum= "+xdum+" fdum= "+fdum);
                       }
                     }
                  }
                }    */
                x=f;
            }
            if(ip > 2) {
                  ncurve = graph.addCurve(data,ip,Color.red);
            }
            x=xb;            
            data[0]=x/xscale+0.5;
            data[1]=x/xscale+0.5;
            f = function(nit);
            data[2]=x/xscale+0.5;
            data[3]=f/xscale+0.5;
        
            ncurve = graph.addCurve(data,2,Color.blue);
            iterateCurve = ncurve;
//            graph.clearAll= false ;
            graph.paintAll=true; 
            graph.repaint();
      }

//*********************************************************************
/**
* Iterates class variables via map functions x->f f->f(x)  
*/ 
//*********************************************************************
      
      public boolean iterate() {
            
            double[] moredata = new double[6];
            moredata[0]=x/xscale+0.5;
            moredata[1]=f/xscale+0.5;
            moredata[2]=f/xscale+0.5;
            moredata[3]=f/xscale+0.5;
            moredata[4]=f/xscale+0.5;
            x=f;
            f = function(nit);
            moredata[5]=f/xscale+0.5;
            // Don't paint while updating data
            graph.paintAll=false;
//            To keep number of points in plot down            
//            if(graph.nPoints(iterateCurve)>200) {
//                  graph.deleteFromCurve(100,iterateCurve);
//            }       
            graph.appendToCurve(moredata,3,iterateCurve);
            graph.paintAll=true;
            graph.clearAll=false;
            graph.repaint();
            return true;
      }

//*********************************************************************
/**
* Stop thread
*/ 
//*********************************************************************
      
      public void stop() {
            movieStop();
            enableAll();
      }                            

//*********************************************************************
/**
* Restarts iteration on mouse click
* @param xcoord x-coordinate of event
* @param xcoordValid true if event within x-range set by axes
* @param ycoord y-coordinate of event
* @param ycoordValid true if event within y-range set by axes
*/  
//*********************************************************************
    public void respondToMouse(double xcoord, boolean xcoordValid,
                 double ycoord, boolean ycoordValid) {
         if(xcoordValid) {
             variables.setText(2,String.valueOf(xcoord));
             if(aThread != null) {
                  movieStop();
             }
             updateParameters();
             buttons.disableGo();
             disableAll();
             movieStart();
         }             
    }

}   

