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

//********************************************************************
/**                    
* 1d return map (on Poincare section) diagnostics for Odes <br>
* @version 24 October 1997
* @author Michael Cross
*/
//********************************************************************

public class OdesReturnMap extends OdesDiagnostics {

/** time for ghost */      
      double ghostTime=10.;      
/** number of iterations for ghost */      
      int ghostLength;      
/** updated number of points in plot */      
      int nplot;
/** index of first data curve */      
      int ncurve1; 
/** updated number of points in plot of second curve */         
      int nplot1; 
/** copy of x */       
      double[] xp={0.,0.,0.,0.}; 
/** true if Poincare plane has been crossed */
      boolean crossed=true;
/** true if first data point */    
      boolean firstDataPoint=true;
/** variable for Poincare section */                
      int secVar=3;  
/** value for Poincare section */      
      double sec=20.;       
/** last x[xecVar] for Poincare section calculation */      
      double zHold;  
/** vasiables for return map */      
      double z1,z2;      
/** last time */      
      double tp;       
/** input marker size */
      double marker=1.;    
/** marker size */      
      double markerScale=0;
/** type of marker (see marker.txt) */      
      int markerType;                             
      String[] XTitleArray={"","X_n","Y_n","Z_n"};
      String[] YTitleArray={"","X_n+1","Y_n+1","Z_n+1"};
      

//******************************************************************
/**
* @param inParent parent class
* @see Map1D
*/
//******************************************************************
      public OdesReturnMap(Odes inParent) {
            super(inParent);
            allowDrag=true;                           
      }
      
//********************************************************************
/**
* Updates parameters from the text controls in parent class 
*/ 
//********************************************************************            
      public void updateParameters() { 
            super.updateParameters();
            setAxesRange=false;
            ghostLength=(int)(parent.ghostTime/dtp); 
            secVar=parent.variables.parseTextField(4,secVar,1,3);
            sec=parent.variables.parseTextField(5,sec);
            marker=parent.variables.parseTextField(6,marker, true);                                                              
            if(plot_x==0 || plot_x==plot_y || plot_x+plot_y+secVar !=6) {
                 plot_x=mod(secVar+1,3)+1;
                 plot_y=mod(plot_x+1,3)+1;
                 parent.parameters.setText(5,String.valueOf(plot_x));
                 parent.parameters.setText(6,String.valueOf(plot_y));                                    
            }
            if(marker<=0.01){
                markerType=7;
                markerScale=1.;
            }    
            else {
                markerType=1;
                markerScale=marker;
            }                   
            parent.graph.setXAxisTitle(XTitleArray[plot_x]);
            parent.graph.setYAxisTitle(YTitleArray[plot_x]); 
                                                                 
      }      
                
//*********************************************************************
/**
* Restarts
*/
//*********************************************************************      
      public boolean restart() {
            double data[] = new double[2*ghostLength];
            double data1[] = new double[4];
            int i,j;
            double xSave[] = new double[nVariables];
            double tSave;
            t=0.;
            if(runTrans) {
                if(!eliminateTransient()) return false;
                runTrans=false;
                parent.status.setText("");
            }                                               
            err=0.;
                    
            System.arraycopy(x,0,xp,0,nVariables);
            z1=z2=x[secVar];            
            tp=t;
            if(setAxesRange) {
                  data1[0]=xmin;
                  data1[1]=ymin;
                  data1[2]=xmax;
                  data1[3]=ymax;
                  ncurve = parent.graph.addCurve(data1,2,Color.black,0,7,1);
            } 
                        
            if(ghostLength>0) {                       
                   for(i=0;i<nVariables;i++) {
                       xSave[i]=x[i];
                   }
                   tSave=t;
            
                   nplot=0;
                   firstDataPoint=true;
                   for(i=0,j=0;i<ghostLength;i++) {
                       try {
                              t=parent.solver.timeStep(x,t,dtp);
                       } catch (ArithmeticException e) {
                              alertDialog alert = new alertDialog
                                    (parent,"Value has diverged. Try again! ");
                       return false;
                       }                                 
                       nplot=process(x,data,nplot);
                   }        

                   if(nplot > 0)
                        ncurve = parent.graph.addCurve(data,nplot,
                             parent.transientColor,0,markerType,markerScale);

                   for(i=0;i<nVariables;i++) {
                        x[i]=xSave[i];
                   }
                   t=tSave;                 
            }
            runTrans=false;
            nplot=0;
            firstDataPoint=true;            
            System.arraycopy(x,0,xp,0,nVariables);
            z1=z2=x[secVar];
            tp=t;            
            int nit=0;
            parent.status.setText("Finding initial data...");
            while(nplot < 2 && nit < 10000) {
                try {
                      t=parent.solver.timeStep(x,t,dtp);
                } catch (ArithmeticException e) {
                       alertDialog alert = new alertDialog
                              (parent,"Value has diverged. Try again! ");
                       return false;
                }                                
                nplot=process(x,data1,nplot);
                nit++;
            }
            parent.status.setText("");
            if(nplot==0) {
                  alertDialog alert = new alertDialog(
                        parent,"Bad parameter values: no data found");
                  parent.graph.paintAll=true; 
                  parent.graph.repaint();
                  return false;
            }                             

            ncurve = parent.graph.addCurve(data1,nplot,Color.blue,0,
                                    markerType,markerScale);
            ncurve1=ncurve;

            parent.graph.paintAll=true; 
            parent.graph.repaint();
            return true;
            
      
      }

//*********************************************************************
/**
* Iterates ODEs and updates graph
* @return true if iteration successful
*/
//*********************************************************************      
      public boolean iterate() { 
            double[] moredata = new double[2];
            double[] moredata1 = new double[2];  
            nplot=0;
            nplot1=0;
            try{
                  for(int i=0;i<delay;i++)  
                        t=parent.solver.timeStep(x,t,dt);
            } catch (ArithmeticException e) {
                  alertDialog alert = new alertDialog(
                        parent,"Value has diverged.");
                  return false;
            }                                                
            nplot=process(x,moredata,nplot);

            if(nplot > 0) {
                  parent.graph.paintAll=false;     // Don't paint while updating data  
                  parent.graph.appendToCurve(moredata,1,ncurve1);                      
                  parent.graph.paintAll=true;
                  parent.graph.clearAll=false;
                  parent.graph.repaint();
//                  graph.paint(graph.getGraphics());
            }    
            errp=parent.solver.err[plot_x]+parent.solver.err[plot_y];
            if(errp>err) err=errp;                       
            if(parent.showTime)
                  parent.status.setText("     Time ="+(float)t);
            return true;                        
      }
      
//**********************************************************************
/**
* Sets default values of parameters depending on plot type
*/
//**********************************************************************          
      public void setDefaults() {
                   parent.variables.show(4);
                   parent.variables.show(5);                        
                   parent.variables.show(6);  
                   parent.variables.setText(4,"3");
                   parent.variables.setLabelText(4,"Variable");
                   parent.variables.setText(5,
                        String.valueOf(parent.odesFunction.poincareSection));
                   parent.variables.setLabelText(5,"Section ");
                   parent.variables.setText(6,"1.");
                   parent.variables.setLabelText(6,"PlotMark");                                                        
      
                   parent.topRightPanel.validate();        
      }                                       


//**********************************************************************
/**
* Shifts x to 0<x<1
* @param x input value
* @return value shifted to between 0 and shift
*/
//**********************************************************************      
      private double mod(double x, double shift) {
         while (x>shift) {
            x=x-shift;
         }   
         while (x<0.) {
            x=x+shift;
         }
         return x;
      }

//**********************************************************************      
      private int mod(int x, int shift) {
         while (x>=shift) {
            x=x-shift;
         }   
         while (x<0) {
            x=x+shift;
         }
         return x;
      }      

//**********************************************************************
/**
* Sets delay for movie iteration
*/
//**********************************************************************      
      public void setDelay(int inDelay) {
            delay=inDelay;
            dt=dtp/((double) delay);
            ntrans=(int) (trans/dtp); 
            ghostLength=(int) (parent.ghostTime/dtp);           
      } 

//**********************************************************************

/**
* process data
* @param    x variable
* @param    data for plotting
* @param    nplot number of points indata
* @return   nplot new number of points in data
*/          
//**********************************************************************                        

      int process(double x[], double[] data,  int nplot) {
              if(wrapX) x[1]=mod(x[1],wrapXValue);
              if(wrapY) x[2]=mod(x[2],wrapYValue);
              if(wrapZ) x[3]=mod(x[3],wrapZValue);
              int j=2*nplot; 
              if(xp[secVar] < sec && x[secVar] > sec && !crossed)  {                        
                    t=poincare(xp,x,tp,t,sec,secVar);
                    crossed=true;
                    if(firstDataPoint) {
                            if(checkRange(x[plot_x])) {
                                zHold=x[plot_x];
                                firstDataPoint=false;
                            }    
                    }
                    else{
                          if(checkRange(zHold,x[plot_x])) {
                                data[j]=zHold;
                                j++;
                                data[j]=x[plot_x];
                                nplot++;
                          }
                          if(checkRange(x[plot_x])) zHold=x[plot_x];                          
                          else firstDataPoint=true;
                    }                                
              }
              else crossed=false;
              System.arraycopy(x,0,xp,0,nVariables);
              tp=t;                 
              return nplot;
      }
//**********************************************************************
/**
* Checks if point within plot range
* @param x x-coordinate of point
* @param y y-coordinate of point
* @return true if within range
*/
//**********************************************************************              
      private boolean checkRange(double x,double y) {
            if(!setAxesRange) return true;
            if(x>xmin && x<xmax && y>ymin && y<ymax) return true;
            else return false;
      }
//**********************************************************************
/**
* Checks if x-coordiante within plot range
* @param x x-coordinate of point
* @return true if within range
*/
//**********************************************************************            
      private boolean checkRange(double x) {
            if(!setAxesRange) return true;
            if(x>xmin && x<xmax) return true;
            else return false;
      }
      
//**********************************************************************

      double poincare(double[] x1_in, double[] x2_in, double t1, double t2, double sec, int secVar) {
            double[] x1,x2;
            double t,dt;
            double precision=0.000001;
            int n=0;
            x1=new double[nVariables];
            x2=new double[nVariables];
            System.arraycopy(x1_in,0,x1,0,nVariables);
            System.arraycopy(x2_in,0,x2,0,nVariables);
            if(Math.abs(x1[secVar]-sec) < Math.abs(x2[secVar]-sec)) {
                  System.arraycopy(x1,0,x,0,nVariables);
                  System.arraycopy(x2,0,x1,0,nVariables);
                  t=t1;
                  t1=t2;
            }
            else {                  
                  System.arraycopy(x2,0,x,0,nVariables);
                  t=t2;
            }            

            while(true) {
                  n++;
                  dt=(t1-t)*(x[secVar]-sec)/(x[secVar]-x1[secVar]); 
                  System.arraycopy(x,0,x1,0,nVariables);
                  t1=t;
                  t=parent.solver.timeStep(x,t,dt);
                  if(Math.abs(x[secVar]-sec) < precision) {
                       System.arraycopy(x,0,x2,0,nVariables);
                       return t;
                  }     
            }
     }                  
}      
