import ChaosDemos.*;
import java.awt.*;
import java.util.*;
import java.net.URL;
import graph.*;
/**                    
* Iterates and plots one dimensional chaotic maps.<br>
* <UL><LI>Quadratic Map
* <LI>Sine Map
* <LI>Tent Map
* <LI>"Power Law" Map: map with (b+1) power law cusp at x=0.5
* <LI>Circle Map</UL><BR>
* <LI>Shift Map
* Shows the map iteration, and calculates power spectrum and histogram.<BR> 
* Uses the "Java Graph Class Library" by Leigh Brookshaw
* @version 10 October 1997
* @author Michael Cross
*/

public class Map1D extends dynamicGraph {


/** for location of marker.txt */      
      private URL markerURL;
/** location of marker.txt */      
      private URL documentBase;
      
/** functions */
      private static final int QUADRATIC=0;
      private static final int SINE=1;
      private static final int TENT=2;
      private static final int POWER=3;
      private static final int CIRCLE=4;    
      private static final int SHIFT=5;
      private static final int CUBIC=6;
      private static final int MYFUNCTION=7;      

/** plot types */      
      private static final int TIMESERIES=0; 
      private static final int FOURIER=1;
      private static final int BIN=2;
      private static final int BIFURCATION=3;
      private static final int LYAPUNOV=4;
      
/* classes used */
      private startMap1D outerparent;
      public superGraph2D graph;
      private movie theMovie;
      private Thread aThread=null;      
      private Map1DDiagnostics diagnostics;
      public Map1DFunction mapFunction;            

/* GUI classes */
      public textControls variables;
      public  buttonControls buttons;
      public  choiceControls choices;      
      private Panel topRightPanel;      
      public  Choice plotChoice;
      public  Choice functionChoice;
      public  Label status;                                        
      
/* flags */
/**  true if user has chosen range with mouse */
      private boolean setAxesRange=false;   
/** true if iteration number to be shown. Set by choices */
      public boolean showTime;                  
/** true after one mouse click */
      boolean clicked=false;              
/** xcoord of first mouse click */
      private double xmouse;
/** ycoord of first mouse click */
      private double ymouse; 
      private boolean showa;             

/** delay in graph update         */
      private int delay=100;  
      
/** plot number selected */
      private int plot=0;
/** function number selected */
      private int function=0;

/** parameter of map equations */
      private double a=3.5;
/** Maximum value allowed for a */
      private double aMaxLimit;
/** Minimum value allowed for a */      
      private double aMinLimit;    
/** parameter of map equations */                          
      private double b=0.;
/** array containing the map parameters a,b */
      public double[] parameters;
/** Number of parameters for the map function */      
      public int nParameters;     
                  
/** iteration variable */
      private double x=0.;

/** Range of plot */
      private double xmin=0,xmax=1,ymin=0,ymax=1;      

/** x-axis label for each plot type */                              
      private String[] xTitleArray={"X_n","Frequency","X","a","a"} ;
/** y-axis label for each plot type */           
      private String[] yTitleArray={"X_n+1","log(Power)","Number","X","Exponent"};

/**
* @param target starting class
* @see startMap1D
*/ 
        Map1D(startMap1D target,  URL in_documentBase) {
        
        documentBase=in_documentBase;
        graph = new superGraph2D(this);
        graph.borderRight=35;                       

        try {
           markerURL = new URL(documentBase,"marker.txt");
           graph.setMarkers(new Markers(markerURL));
        } catch(Exception e) {
           System.out.println("Failed to create Marker URL!");
        }
        
        theMovie = new movie(this);
        this.outerparent = target; 

        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints constraints = new GridBagConstraints();
        
        setLayout(gridbag);
        constraints.fill=GridBagConstraints.BOTH;
        constraints.gridheight = 1;
        constraints.gridwidth=1;
        constraints.weighty = 1.0; 
        constraints.weightx=0.75;       
        constraints.insets = new Insets(0,0,0,10);
        
        Panel leftPanel = new Panel();
        leftPanel.setLayout(new BorderLayout());
        leftPanel.add("Center",graph);  
                
        Panel bottomLeftPanel = new Panel();
        bottomLeftPanel.setLayout( new GridLayout(1,1));
        bottomLeftPanel.add(theMovie);
         
/*      String[] buttonLabels={"Reset","Clear",
                    "Start"," Step"," Stop"};
        buttons = new buttonControls((dynamicGraph) this, 
                        buttonLabels,buttonLabels.length,true);
        buttons.b_init[0] = true;
        buttons.b_stopped[4] = false;
        buttons.b_started[1] = true;
        buttons.b_started[4] = true;
        buttons.setup();
        bottomLeftPanel.add(buttons);
*/                       
        leftPanel.add("South",bottomLeftPanel);
         
        constraints.insets = new Insets(0,0,10,0);
        gridbag.setConstraints(leftPanel, constraints);              
        add(leftPanel);    

        Panel rightPanel = new Panel();
        rightPanel.setLayout(new BorderLayout());
        constraints.weightx=0.25;
        constraints.insets = new Insets(0,0,15,10);
        gridbag.setConstraints(rightPanel, constraints);
        add(rightPanel);                
 
        topRightPanel = new Panel();
        topRightPanel.setLayout(gridbag);

        constraints.gridheight = 3;
        constraints.gridwidth=1;
        constraints.weightx=1.;
        constraints.weighty = 0.75;
        constraints.insets = new Insets(30,0,0,10);
        constraints.fill=GridBagConstraints.NONE;             

        String[] buttonLabels={"Reset","Clear",
                    "Start"," Step"," Stop"};
        buttons = new buttonControls((dynamicGraph) this, 
                        buttonLabels,buttonLabels.length,false);
        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, constraints); 
        topRightPanel.add(buttons);
                       

        String[] textboxes1 = {"3.5","0.","1","0","0.","0.2","0"};
        String[] labels1 = {"   a   ","   b   ","Compose",
                                  "Transient","Delta-x","Start x","Window"};
        variables = new 
              textControls((dynamicGraph) this,textboxes1,labels1,textboxes1.length,5);        
        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.insets = new Insets(30,0,0,10);
        gridbag.setConstraints(variables, constraints);
        topRightPanel.add(variables);
        
        functionChoice = new Choice();
        functionChoice.addItem("Quadratic");
        functionChoice.addItem("Sine");
        functionChoice.addItem("Tent");
        functionChoice.addItem("Power law");
        functionChoice.addItem("Circle");
        functionChoice.addItem("Shift");
        functionChoice.addItem("Cubic");
        functionChoice.addItem("My Function");
        constraints.gridwidth=1;
        constraints.weighty = 0.2;       
        constraints.gridheight=1;          
        constraints.gridwidth=GridBagConstraints.REMAINDER;        
        constraints.insets = new Insets(0,0,0,0);
        gridbag.setConstraints(functionChoice, constraints);  
        topRightPanel.add(functionChoice);        

        plotChoice = new Choice();
        plotChoice.addItem("Time Series");
        plotChoice.addItem("Spectrum");
        plotChoice.addItem("Histogram");
        plotChoice.addItem("Bifurcation");
        plotChoice.addItem("Lyapunov");
        constraints.gridwidth=1;
        constraints.weighty = 0.2;       
        constraints.gridheight=1;          
        constraints.gridwidth=GridBagConstraints.REMAINDER;        
        constraints.insets = new Insets(0,0,0,0);
        gridbag.setConstraints(plotChoice, constraints);  
        topRightPanel.add(plotChoice);
        
        String[] choiceLabels={"Show Time:"};
        choices = new choiceControls((dynamicGraph) this, 
                        choiceLabels);
        constraints.weighty = 0.25;
        constraints.gridheight = 1;
        gridbag.setConstraints(choices, constraints);
        topRightPanel.add(choices);
        choices.setState(0,true);            
        showTime=true;             
        theMovie.toSleep=true;                     

        rightPanel.add("Center",topRightPanel);
        status=new Label("                    ");
        rightPanel.add("South",status);
        
        graph.clearAll=true;
        repaint();

        /* Setup run */
        buttons.enableGo();
        if(target.inputParameters) {
            function=target.function;
            functionChoice.select(function);
            plot=target.plot ;
            plotChoice.select(plot);
            setFunctionDefaults();
            setPlotDefaults();   
            for(int i=0;i<target.nVariables;i++)
                  if(target.variableBoxes[i].length() != 0)
                        variables.setText(i,target.variableBoxes[i]);                                     
        } 
        else {
              setFunctionDefaults();
              setPlotDefaults();          
        }
 
                         
        updateParameters();
        restart();

      }

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

//**********************************************************************
/**
* Responds to choiceControls
* @see choiceControls
*/      
//**********************************************************************      
      public void respondToChoices() {
            if(choices.getState(0))
                  showTime=true;
            else
                  showTime=false; 
                  status.setText("");                 
      }
                  
            
      
//**********************************************************************      
/**
* Respond to buttonControls
* @see      buttonControls
* @param    buttonIndex index of button pushed
*/          
//**********************************************************************            
      public void respondToButtons(int buttonIndex) {

            if(buttonIndex==0) {
                 buttons.enableGo();
                 resetRange();
                 updateParameters();
                 restart();
                 graph.paintAll=true;
                 graph.repaint();
            }
            else if(buttonIndex==1) {
                graph.clearAll=true;
                if(aThread != null) {
                  movieStop();
                  restart();
                  movieStart();
                }
                else restart();
            }
            else if(buttonIndex==2) {
                  buttons.disableGo();
                  disableAll();
                  status.setText("");
                  clicked=false;
                  graph.allowDrag=false;
                  movieStart();    
            }
            else if(buttonIndex==3) {
                iterate();
                graph.allowDrag=true;
                clicked=false;                     
            }
            else if(buttonIndex==4) {
                buttons.enableGo();
                movieStop();
                enableAll(); 
                graph.allowDrag=true;                             
            }
      }
      
//      public Insets insets() {
//            return new Insets(0,0,20,0);
//      }

//************************************************************************
/**
* 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.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();
                    return super.handleEvent(evt);                
                case Event.ACTION_EVENT:
                    if(evt.target == functionChoice || evt.target == plotChoice) {
          
                        function = functionChoice.getSelectedIndex();            
                        if(evt.target == functionChoice) {                           
                              setFunctionDefaults();
                              setPlotDefaults();
                        }
                        plot = plotChoice.getSelectedIndex();
                        if(evt.target == plotChoice) {
                            setPlotDefaults();                                    
                        }   
                        topRightPanel.validate();                             
                        updateParameters();
                        resetRange();
                        restart();
                    }
                return super.handleEvent(evt);                      
                default:                     
                    return super.handleEvent(evt);
            }
       }
       
//************************************************************************
/**
* Disables text input and choice controls
*/
//************************************************************************
      
      public void disableAll() {
          int i;
          for(i=0;i<variables.ncontrols();i++)
                 variables.disableText(i);
          plotChoice.disable();
          functionChoice.disable();                         
      }

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

      public void enableAll() {
          for(int i=0;i<variables.ncontrols();i++)
                variables.enableText(i);
          plotChoice.enable();
          functionChoice.enable();      
      }

//********************************************************************
/**
* Updates parameters from the text controls 
*/ 
//********************************************************************      
      public void updateParameters() {
            int i;
            mapFunction.winding=0;
            mapFunction.total=0;              
            clicked=false;            
            graph.setTitle(mapFunction.title);
            plot=plotChoice.getSelectedIndex();
            function=functionChoice.getSelectedIndex();  
            graph.setXAxisTitle(xTitleArray[plot]);
            graph.setYAxisTitle(yTitleArray[plot]);
            if(mapFunction.enforceBRange) {
                  double bMaxLimit=mapFunction.bMaximum;
                  double bMinLimit=mapFunction.bMinimum;
                  b=variables.parseTextField(1, 0.5*(bMinLimit+bMaxLimit),
                              bMinLimit, bMaxLimit);
            }                  
            else  b=variables.parseTextField(1, b);           
            aMaxLimit=mapFunction.getAMaximum(b);
            aMinLimit=mapFunction.getAMinimum(b);
            if(mapFunction.enforceARange)
                  a=variables.parseTextField(0, 0.5*(aMinLimit+aMaxLimit),
                              aMinLimit, aMaxLimit);
            else  a=variables.parseTextField(0, a);                    

            parameters[0]=a;
            if(nParameters>1) parameters[1]=b;
            mapFunction.setParameters(parameters);                   
            diagnostics.updateParameters();
      }      
                
//*********************************************************************
/**
* Resets plot
*/
//*********************************************************************
      
      public void restart() {          
            status.setText("");
            diagnostics.restart();
                               
      }

//*********************************************************************
/**
* Deletes all curves from graph
* @return -1 for no curves
*/
//*********************************************************************
      
      public int resetGraph() {
            return  graph.deleteAllCurves();
      }      

//*********************************************************************
/**
* Iterates Map equations and updates graph according to value of plot 
*/
//*********************************************************************
      
      public boolean iterate() {
              return diagnostics.iterate();
      }

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

/**********************************************************************/
      public void updateSpeed(int in_delay) {
            delay=in_delay;
            if(diagnostics != null) diagnostics.setDelay(delay);
      }
            
//**********************************************************************
/**
* Sets default values of parameters depending on function number
*/
//**********************************************************************          
      private void setFunctionDefaults() {
           
            switch (function) {
                case QUADRATIC:
                    mapFunction = new QuadraticFunction();
                    break;
                case SINE:
                    mapFunction = new SineFunction();
                    break;
                case TENT:
                    mapFunction = new TentFunction();
                    break;   
                case POWER:
                    mapFunction = new PowerFunction();
                    break;                     
                case CIRCLE:
                    mapFunction = new CircleFunction();
                    break; 
                case SHIFT:
                    mapFunction = new ShiftFunction();
                    break;                     
                case CUBIC:
                    mapFunction = new CubicFunction();
                    break;                     
                case MYFUNCTION:
                    mapFunction = new Map1DMyFunction();
                    break;                                                                                                
            }
            nParameters=mapFunction.getNParameters();
            parameters = new double[nParameters];
            if(nParameters==2) variables.show(1);
            else variables.hide(1);                            
            variables.setText(0,
               String.valueOf(mapFunction.aDefault[0]));
            if(nParameters==2) variables.setText(1,
               String.valueOf(mapFunction.aDefault[1]));    
            variables.setText(5,
               String.valueOf(mapFunction.x0Default));
            topRightPanel.validate();           
      }                 

//**********************************************************************
/**
* Sets default values of parameters depending on plot type
*/
//**********************************************************************          
      private void setPlotDefaults() {
            switch (plot) {
                case TIMESERIES:
                    diagnostics = new Map1DTimeSeries(this);
                    break;
                case FOURIER:
                    diagnostics = new Map1DFourier(this);
                    break;   
                case BIN:
                    diagnostics = new Map1DBin(this);
                    diagnostics.setDelay(delay);                    
                    break;  
                case BIFURCATION:
                    diagnostics = new Map1DBifurcation(this);
                    break;    
                case LYAPUNOV:
                    diagnostics = new Map1DLyapunov(this);
                    break;                                                                              
                default:    
                    diagnostics = new Map1DTimeSeries(this);
                    break; 
            }                           
            diagnostics.setPlotDefaults();            
            if(diagnostics.hideab) {
                    variables.hide(0);
            }
            else variables.show(0);                 
            topRightPanel.validate();            
      }                 
      
//********************************************************************** 

//**********************************************************************
/**
* Resets x-range to 0 < x < 1
*/
//**********************************************************************            
      private void resetRange() {
            diagnostics.resetRange();
      }      
//********************************************************************** 

//**********************************************************************
/**
* Responds to mouse events
* @param xcoord x-coordinate of mouse click
* @param xcoordValid true if x-coordinate of mouse click within graph
* @param ycoord y-coordinate of mouse click
* @param ycoordValid true if y-coordinate of mouse click within graph
*/
//**********************************************************************         
      public void respondToMouse(double xcoord, boolean xcoordValid,
                 double ycoord, boolean ycoordValid){
             double exchange;
             if(aThread != null) {
//                if(!clicked) { 
//                   clicked=true; 
// Need to eliminate mouseup event in jdk1.0                                    
                   if(xcoordValid && ycoordValid && (xcoord != xmouse) && (ycoord !=ymouse)) {
                         xmouse=xcoord;
                         ymouse=ycoord;
                         diagnostics.respondToClick(xcoord,ycoord);
                   } 
//                }
//                else clicked=false;   
             } 
             else {               
                   if(xcoordValid && ycoordValid) {          
                        if(!clicked) {
                             xmouse=xcoord;
                             ymouse=ycoord;                
                             clicked=true;
                        }
                        else {
                             clicked=false;
                             if(xmouse != xcoord) {
                                 xmax=xcoord;
                                 xmin=xmouse;
                                 if(xmin > xmax) {
                                        exchange=xmin;
                                        xmin=xmax;
                                        xmax=exchange;
                                 } 
                                 setAxesRange=true;   
                             }
                             if(ymouse != ycoord) {      
                                 ymax=ycoord;
                                 ymin=ymouse;
                                 if(ymin > ymax) {
                                       exchange=ymin;
                                       ymin=ymax;
                                       ymax=exchange;
                                  }                                                                                                
                                  setAxesRange=true;
                             }        
                             if(setAxesRange) 
                             diagnostics.respondToDrag(xmin,xmax,ymin,ymax);
                        }
                  }
                  else {
                       clicked=false;
                       buttons.enableGo();
                       resetRange();
                       updateParameters();
                       restart();
                       graph.paintAll=true;
                       graph.repaint();                 
                  }                                
            }
      }              

//**********************************************************************
/**
* Sets visibility and content of variables textboxes 3,4 ...
* @param label text for label of each box ("" makes invisible)
*/
//**********************************************************************
      
      public void setPlotTextBoxes(String[] label, String[] text) {
            for(int i=0;i<label.length;i++) {
                  if(label[i].length()==0) variables.hide(3+i);
                  else {
                       variables.show(3+i);
                       variables.setLabelText(3+i,label[i]);
                       variables.setText(3+i,text[i]);
                  }
            }
      }          
//**********************************************************************
}                        
                 
                        
                        
