//modal analysis 
//Yves Moreau, Jérôme Porque, may 98 
//inspired by NPLibrary  (F.Ladouceur, Australian National University)
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;

public class modAppl extends Applet implements ActionListener, ItemListener{
   shape last =null;
   drawing pane;
   s_solver solver=null;
   h_solver hSolver=null;
   s_Mode mode = null;
	 int modeNum=0; Frame pFrame;
	 String simulType=null;
	 double nmin=0; double nmax=0; // limits for effective index
   public TextArea msg;
	 public final static long CHECKPOL =1;// codes de debugging
	 public final static long DISPMAT =2;
	 public final static long KEEPSIZE =4;// no centering, sizing
	 public final static long HERMITE =8;// no centering, sizing
	 public final static long TIME =16;//
	 public final static long EIMFIT =32;//
	 public final static long QUADRATIC =64;//
	 public final static long EIMFITDEBUG =128;//
	 public long debugCode = EIMFIT | QUADRATIC;
	 Panel controls;
   public Checkbox checkSel;
   Label label;Label xLabel; Label yLabel;
   public Panel propPanel;
   public Choice sinesC; Button refB;Button delB;
   public Button nextB; Button prevB; Choice modeC, what;
   public Button okB; public Button cancelB; Choice testC;
   public Vector xField = new Vector(); 
   public Vector yField = new Vector();
   public Label wl_nbLabel; public Label  indexLabel;
   public TextField wl_nbField; public TextField  indexField;
   public static void main(String args[]) {
	 Frame f = new Frame("mode_Solver");
               modAppl appl = new modAppl();
               f.setSize(960,520);
               appl.init();
               f.add("Center",appl);
   							f.show();
   							f.addWindowListener(new WindowAdapter() {
     						public void windowClosing(WindowEvent e) {System.exit(0);}});
               appl.start();
               appl.processChoice("sines,10,10,4,4");
               appl.processChoice("mode,1");
               appl.processChoice("profile");
    }
   public void showMsg(String s) { try {this.getAppletContext().showStatus(s);} catch (Exception e){}}
   public void displayMsg(String s) { msg.append(s);}
   public static String format(String tx,int nbCar) { String sp="0         ";
        if (tx.length()>=nbCar) return (tx.substring(0,nbCar));
   			return(tx+sp.substring(0,1+nbCar-tx.length()));}
   public void init() { 
                 setLayout(new BorderLayout());
                 add("Center",pane =new drawing(this));
                 add("North", controls= new Panel());
                 controls.setLayout(new GridLayout(1,0)); 
                 controls.add(checkSel = new Checkbox("select"));
                 controls.add(sinesC = new Choice());
                 sinesC.addItem("sines,4,4,4,4");
								  sinesC.addItem("sines,6,6,6,6");
                 sinesC.addItem("sines,6,6,6,0");
                 sinesC.addItem("sines,6,0,6,6");
   							  sinesC.addItem("sines,6,0,6,0");
                 sinesC.addItem("sines,8,0,8,0");
                 sinesC.addItem("sines,8,8,4,4");
   							  sinesC.addItem("sines,0,8,0,8");
   							  sinesC.addItem("sines,8,8,8,8");
                 sinesC.addItem("sines,12,12,6,6");
                 sinesC.addItem("sines,12,0,12,12");
   								sinesC.addItem("sines,20,0,4,0");
									sinesC.addItem("sines,20,0,4,4");
									sinesC.addItem("sines,20,0,6,0");
                 sinesC.addItem("sines,0,20,4,0");
                 sinesC.addItem("sines,0,20,4,4");
   								sinesC.addItem("sines,20,20,4,0");
   								sinesC.addItem("sines,20,20,4,4");
									sinesC.addItem("sines,20,20,6,0");
   								sinesC.addItem("sines,30,0,4,4");
									sinesC.addItem("sines,0,30,4,4");
									sinesC.addItem("sines,30,30,4,4");
									controls.add(modeC = new Choice());
                 modeC.addItem("Mode 0"); modeC.addItem("Mode 1");modeC.addItem("Mode 2");modeC.addItem("Mode 3");
                 modeC.addItem("Mode 4"); modeC.addItem("Mode 5");modeC.addItem("Mode 6");modeC.addItem("Mode 7");
   								modeC.addItem("Mode 8"); modeC.addItem("Mode 9");modeC.addItem("Mode 10");modeC.addItem("Mode 11");
   								modeC.addItem("Mode 12"); modeC.addItem("Mode 13");modeC.addItem("Mode 14");modeC.addItem("Mode 15");
                 modeC.addItem("Mode 16"); modeC.addItem("Mode 17");modeC.addItem("Mode 18");modeC.addItem("Mode 19");
									controls.add(what = new Choice());
									//modeC.addItem("print");
   	              what.addItem("profile");
                  what.addItem("coeff.");
                  what.addItem("FFT");
                  
                  try { simulType=this.getParameter("simul");}catch (Exception ex) {}
                  if ((simulType instanceof String)&&(simulType.indexOf("demo")>=0))
                  					add("South",msg = new TextArea("",2,33));
                  		else add("South",msg = new TextArea("",12,33));               
                  if ( readArgs(pane)==0) { // if nothing is read, a rectangular guide is built
                  						 readGlobals("1.51/20,10/0,0/1.31,0/20,22",pane);
                              readShape("1.523,r/-12,-3/12,-3/12,3/-12,3",pane);
                              register(last);}
                  sinesC.addItemListener(this); modeC.addItemListener(this);what.addItemListener(this);


                  propPanel = addProperties();
                  add("East", propPanel);
                  Component cc= this;
 									 while (cc !=null) { if (cc instanceof Frame) {pFrame=(Frame)cc;return;}
 													try {cc = cc.getParent();} catch (Exception ex) {pFrame=null;}
											 	}
               }
    public void start() { pane.refresh(true); super.start();}
    public Panel addProperties()
        { Panel p= new Panel();
           p.setLayout(new GridLayout(0,2));
           p.add(delB = new Button("delete"));delB.addActionListener(this);   
           p.add(refB = new Button("refresh"));refB.addActionListener(this); 
           p.add(okB= new Button("validate"));okB.addActionListener(this); 
           p.add(cancelB= new Button("cancel"));cancelB.addActionListener(this); 
           p.add(nextB=new Button(" next      "));nextB.addActionListener(this); 
           p.add(prevB=new Button(" previous  "));prevB.addActionListener(this); 
           p.add(wl_nbLabel= new Label("shape nb:"));
           p.add(indexLabel= new Label("index :"));          
           p.add(wl_nbField=new TextField(String.valueOf(1.9999)));wl_nbField.addActionListener(this);
           p.add(indexField=new TextField(String.valueOf(0))); indexField.addActionListener(this);
           p.add(xLabel=new Label(" x "));  p.add(yLabel=new Label(" y "));
           for (int i=0; i<4; i++)
                { TextField tf; xField.addElement(tf= new TextField(5));
                   p.add(tf);tf.addActionListener(this);
                   yField.addElement(tf=new TextField(5));
                   p.add(tf);tf.addActionListener(this);
                 }
           toGlobals(true);
           return(p);
        }
    public void actionPerformed(ActionEvent ev) {
    	 if (ev.getSource() instanceof TextField) processButton("validate");
    	 else processButton(ev.getActionCommand());
      }
    public shape last_() { return(last);}
    public shape first() { shape sh=last; while (sh != null) { if (sh.prev_()==null)  return(sh); sh=sh.prev_();}
                                    return(null);}
    public shape nextOf(shape aShape )
          {shape sh=last_(); if (aShape==last) return(last);
            while (sh.prev_() !=aShape && shape.valid(pane.selected)){ sh = sh .prev_();}
                       if (shape.valid(sh)) return (sh); else return(null);
        }
   public int readArgs(drawing pane)  {
        String txt = null;
       try {  txt= getParameter("globals"); } catch (Exception e) {}
         if (txt !=null) readGlobals(txt, pane);
         try { txt =  getParameter("nbShapes");} catch (Exception e) {return(0);}
         int i=0;int  nbShapes = Integer.parseInt(txt); char letter='@';
         this.displayMsg("\nbegin reading "+nbShapes+" shapes...");
				 for (i=0;i<nbShapes;i++)
              { StringBuffer name=new StringBuffer("shape").append(++letter);
                try{ txt =  getParameter(name.toString()); readShape(txt,pane);
                    register(last);} catch (Exception e) {this.displayMsg("Can't read shape "+letter+e.toString());break;}
							  }
         return(i);
	   }
  private int shapeNb(shape sel) { if (sel==null) return(0); else return(1+shapeNb(sel.prev_()));}
  public int readGlobals(String txt, drawing pane) {
  	  pane.scaleX=pane.scaleY=100;// provisoire
      readShape(txt,pane);pane.index = last.index;// dummy shape quickckly suppressed
      double multX =  last.x[3]/pane.scaleX/pane.scaleX; double multY =  last.y[3]/pane.scaleY/pane.scaleY;
      pane.halfX= (int)Math.round(last.x[0]*multX); pane.halfY=(int)Math.round(last.y[0]*multY);
      if (last.x[2]!=0) pane.lambda = last.x[2]/pane.scaleX;
      pane.scaleX= last.x[3]/pane.scaleX; pane.scaleY=last.y[3]/pane.scaleY;
      last = null;
      return(1);
  		}
  public int readShape(String txt, drawing pane)
     {double xx[]= new double[5]; double yy[] = new double[5]; int i=0;
      if (txt !=null) { 
            for ( StringTokenizer t = new StringTokenizer(txt, "/") ; t.hasMoreTokens() ; )
              {String str = t.nextToken(); 
               int k= str.indexOf(','); 
               if (i==0) { if (str.substring(k+1).equals("r")) last= new rect(last,pane);
								else last = new shape(last,pane);}
               if (k<0) k=str.length(); 
               	 try {xx[i] =Double.valueOf(str.substring(0,k)).doubleValue();}catch (Exception ex) {} 
                 try {yy[i] = Double.valueOf(str.substring(k+1)).doubleValue();}catch (Exception ex) {}
                 last.toInst(i,xx[i]*pane.scaleX,yy[i]*pane.scaleY);
               //  }
               //  else last.toInst(9,(double) Double.valueOf(str.substring(0,k)).doubleValue(),0.);
                 i++;
               }
            }
      return(i-1);
      }
    public void register(shape sel) 
    		{last=sel;  double q= sel.positiveSurface();
         displayMsg("shape "+ new Character((char)(64+last.id)) +" created\n"/*+", area :"+q*/);}
    public void remove(shape aShape )
        { if (!(aShape instanceof shape)) return;
          if (last==aShape) {last = aShape.prev_();}
          else { shape sh= nextOf(aShape);
                     if ((sh instanceof shape) && (aShape instanceof shape))
                                 {sh.setPrev(aShape.prev_());}
                 }
        }
   public int substitute (shape nn, shape oo)
            { if (!(nn instanceof shape)) return(0);
              nn.setPrev(oo.prev_());
              if (last==oo) last=nn;
                 else { shape sh= nextOf(oo);
                        if (sh instanceof shape) sh.setPrev(nn);}
              pane.selected = nn;toGlobals(pane.selected==null);
              return(nn.id);
             }
                       
   public void displayMat( double m[][], int nm) { 
         for (int i=0; i<=nm;i++) {
                for (int j=0; j<=nm; j++)
                      { msg.append(((j==0)?"\n":"\t")+((Math.abs(m[i][j])<1e-13)?0.:m[i][j]));} }
        }
    public void itemStateChanged(ItemEvent evt) {
            if (evt.getSource()instanceof Choice) processChoice((String)evt.getItem());
            }
    public synchronized void  processChoice (String str){
    	     if (str.startsWith("sines"))   { int mm[]=new int[5]; int k=-1;
                for ( StringTokenizer t = new StringTokenizer(str, ",") ; t.hasMoreTokens() ; )
                                              {String tx = t.nextToken(); 
                                               if (++k==0) continue;// token : "sines"
                                               mm[k]=Integer.parseInt(tx);}
    	      try {solver = new s_solver(this,mm[1],mm[2],mm[3],mm[4]);}catch (Exception e) {};
              displayMsg("\nExpansion of modes:\n*"+mm[1]+ " even and "+mm[2]+ " odd sine functions in x,\n*"+ mm[3]+" even,"+mm[4]+" odd functions in y");
              showMsg("Expansion of modes:\n*"+mm[1]+ " even and "+mm[2]+ " odd sine functions in x,\n*"+ mm[3]+" even,"+mm[4]+" odd functions in y");
             	 this.pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
             	long duration = (new Date()).getTime();
              int nmodes = solver.solve(((debugCode & DISPMAT)!=0));
              displayMsg("\nwavelength:"+pane.lambda+"-> nb modes :"+nmodes);
              showMsg("wavelength:"+pane.lambda+"-> nb modes :"+nmodes);
              duration -= (new Date()).getTime();double ne0=0,ne1=0,ngEIM=0;
             	displayMsg(", computed in "+(-duration/1000.)+" sec");
							for (k=0; k<nmodes; k++)
                           { s_Mode mode = solver.getMode(k);
                             //displayMsg("\nn eff.& spatial freq.(mode:"+k+"):\t"+solver.EffectiveIndex(k)+"\t"+(float)mode.kxFromSquareFitting(0,false));
                            	double ncore= mode.indexCoreEIM(mode.kxFromSquareFitting(0,false));
															if ((debugCode & EIMFIT)==EIMFIT) displayMsg("\nMode"+k+","+mode.nPeaks+" peaks\tn_core,mx,psi,kx,neff:"+modAppl.format("\t"+(float)ncore,10)+modAppl.format("\t"+(float)mode.mxEIM,10)+"\t"+((mode.psiEIM==0)?"0.0   ":"-1.571")
																			+modAppl.format("\t"+(float)mode.kxEIM,11)+modAppl.format("\t"+(float)mode.neff,10));
																else displayMsg("\nMode : "+k+"\t"+(float)mode.neff);
															if ((debugCode & QUADRATIC)==QUADRATIC)
																{ if (k==0) {ne0=mode.neff; ngEIM=ncore;}
																  else if (k==1) {ne1=mode.neff;}
																  else displayMsg("\t"+(ne0-(mode.nPeaks-1)*((double)mode.nPeaks+1.)/3.*(ne0-ne1)));
																}
															}
							 if ((debugCode & QUADRATIC)==QUADRATIC) displayMsg("\nguide effective width : "+(float)(pane.lambda*Math.sqrt(3/(8*(ne0-ne1)*ngEIM))));
              this.pane.setCursor(Cursor.getDefaultCursor());             	
             	repaint();
    	}// fin evt.sines
    	else  if (str.equals("print"))
                     {pane.print(pane.gr_());return;}
      else { // evt modes
    		  int num=-1;
    		  int k=0; while (k<=str.length()) {
    			try {num = Integer.parseInt(str.substring(k));break;}
    		       catch (Exception ex) {k++;}}
    			if (num>=0) modeNum=num;
    			try {mode = solver.getMode(modeNum);
    					 displayMsg("\nMODE "+ modeNum);}catch (Exception ex){ displayMsg(" not yet computed");return;}
    			if (mode ==null) { displayMsg(" not valid");return;}
    			//mode.nPeaks=modeNum+1;
	        if (str.startsWith("coeff"))
              {if (mode instanceof s_Mode) mode.displayOrdered(this);
              															//mode.display(this);
                                          // pane.fPaintCoeff(mode); To debug
                                          return;}
    			if (str.startsWith("profile"))
                                    {pane.drawProfile(mode,checkSel.getState());
                                    	return;}
          if (str.startsWith("FFT"))
    																{	if (!(mode instanceof s_Mode)){ this.displayMsg("\nNo valid mode!"); this.showMsg("No valid mode!");return;}
    																  mode.spatialFrequencies(2<<5, this, true); 
    																  return;}
    								/*else*/
    								this.pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
										this.pane.fPaintMode(mode);
                   this.pane.setCursor(Cursor.getDefaultCursor());											
    	}// fin evt: show a selected mode
    	/*			// rajout du menu Hermite si Debug
    				if (((debugCode & HERMITE)!=0) && !(testC instanceof Choice))
    						{ controls.add(testC = new Choice());
                  testC.addItem("H-Gauss,4,4");
                  testC.addItem("H-Gauss,3,3");
   								testC.addItem("H-Gauss,2,2");
    							controls.update(controls.getGraphics());
    						}
    				if (((debugCode & HERMITE)==0) && (testC instanceof Choice))
    						{ testC.removeAll();
    							controls.remove(testC); testC=null;
    							controls.update(controls.getGraphics());
    						}

            if (evt.source()==testC) { int mm[]=new int[5]; int k=-1;
                         for ( StringTokenizer t = new StringTokenizer(evt.getItem(), ",") ; t.hasMoreTokens() ; )
                                              {String str = t.nextToken(); 
                                               if (++k==0) continue;// token : "H-Gauss"
                                               mm[k]=Integer.parseInt(str);}
    	       try {hSolver = new h_solver(this,mm[1],mm[2]);}catch (Exception e) {};
                        displayMsg("\nexpansion of modes on "+mm[1]+ " H-Gauss functions in x, and "+mm[2]+" in y");
                        displayMsg("\nnot yet working, debugging in progress !");
                        int nmodes = hSolver.solve(0,0,debugCode);
                        displayMsg("\nwavelength:"+pane.lambda+"-> nb modes :"+nmodes);
                        
    	         return(true);
    	}// fin evt.H_Gauss
    	*/
    }
  public boolean processButton(String tx){
             if (tx.startsWith("delete")) {
    	        if (shape.valid(pane.selected))  {displayMsg("\nShape #"+pane.selected.id+" deleted");
                                                 remove(pane.selected); pane.selected= null;
                                                 toGlobals(true);
                                                 pane.refresh(true);}
    	         return(true);
    	}// fin evtdelB
             else if (tx.startsWith("refresh")) {
    	        pane.refresh(true);
    	         return(true);
    	}// fin evt.refB
              if (tx.startsWith(" next") || tx.startsWith("First shape")) {
    	         if (shape.valid(pane.selected)) pane.selected = nextOf(pane.selected);
                                            else pane.selected = first();
                           toGlobals(pane.selected==null);
    	         return(true);
    	}// fin evt.nextB
               if (tx.startsWith(" prev") || tx.startsWith("globals"))  {
    	         if (shape.valid(pane.selected)) pane.selected = pane.selected.prev_();
                           toGlobals(pane.selected==null);
    	         return(true);
    	}// fin evt.prevB 
                else  if (tx.startsWith("valid")) {
    	         if (shape.valid(pane.selected)) pane.selected.fromProp(-1,pane.scaleX, pane.scaleY,this);
                           else pane.fromProp(-1);
                           pane.refresh(true);
    	         return(true);
    	}// fin evt.okB or KEY_PRESS
                else  if (tx.startsWith("cancel")){
                            if (shape.valid(pane.selected)) pane.selected.toProp(-1,pane.scaleX, pane.scaleY,this);
    	         return(true);
    	}// fin evt.cancelB
    return(false);
  }
  public void toGlobals(boolean flag) {
            if (flag) { this.wl_nbLabel.setText("wavelength:");
                          this.indexLabel.setText("index:");
                          wl_nbField.setText(String.valueOf(pane.lambda));
                          indexField.setText(String.valueOf(pane.index));
                           if (!(this.xField.isEmpty())) {
                                     ((TextField)this.xField.elementAt(2)).setText("(pixels/um)");
                                     ((TextField)this.yField.elementAt(2)).setText("(pixels/um)");
                                     ((TextField)this.xField.elementAt(0)).setText(String.valueOf(pane.halfX/pane.scaleX));
                                     ((TextField)this.yField.elementAt(0)).setText(String.valueOf(pane.halfY/pane.scaleY));
                                     ((TextField)this.xField.elementAt(3)).setText(String.valueOf(pane.scaleX));
                                     ((TextField)this.yField.elementAt(3)).setText(String.valueOf(pane.scaleY));}                         
                        // if (pane.selected==null) return;
                          pane.selected= null;
                          this.xLabel.setText("half width");
                          this.yLabel.setText("half height");
                          this.nextB.setLabel("First shape");
                          this.prevB.setLabel("                  ");
                         // propPanel.remove(propPanel.getComponent(12));
                          //propPanel.add(new Label("scale X",12));
                          //propPanel.remove(propPanel.getComponent(13));
                         //propPanel.add(new Label("scale Y",13));
                         ((TextField)this.xField.elementAt(1)).setText("scale X");
                          ((TextField)this.yField.elementAt(1)).setText("scale Y");
                              return;}
           else {  if (pane.selected==null) { toGlobals(true);return;}// strange logic
                            this.xLabel.setText("x   ");
                            this.yLabel.setText("y   ");
                            this.nextB.setLabel(" next   ");
                            if (pane.selected.prev_()==null)  this.prevB.setLabel("globals   "); else  this.prevB.setLabel(" previous  ");
                            //propPanel.remove(propPanel.getComponent(12));
                            //propPanel.add((TextField) (xField.elementAt(2)),12);
                            //propPanel.remove(propPanel.getComponent(13));
                            //propPanel.add((TextField) (yField.elementAt(2)),13);
                            pane.selected.toProp(-1,pane.scaleX,pane.scaleY,this);
                         }
      }
}
