/*
汎用シミュレータ
System Simulator coded by 
	製作者:
	石井 竜次 (ISHII Tatsuji) &
	喜多 敏博 (KITA Toshihiro; t-kita@eecs.kumamoto-u.ac.jp)
'97 Apr.

Copyright by Hiyama-Miyauchi lab. Kumamoto Univ. 1997
*/

import java.awt.*;     // Applet Window T... のロード
import java.applet.*;  // アプレット(WWW ブラウザで動く) クラスのロード

public class ssim01_s extends Applet implements Runnable {
// 作成するアプレットクラスの宣言             ^^^^^^^^
// アプレット・クラスでスレッドを使用できるようにするため
// Runnableインターフェースをimplementsする

  // シミュレータのなかで共通に用いるオブジェクトの宣言
  Thread th = null;        // ルンゲクッタ法による求解と描画を行なうための
                           // スレッドオブジェクトの宣言 
  // 各パラメータの数値入力を行なうテキストフィールドオブジェクトの宣言
  TextField  x_T, y_T, z_T;
  TextField  xmin_T, xmax_T, ymin_T, ymax_T;
  TextField  a_T, b_T, c_T, prminc_T, H_T, T_T;
  // ボタンオブジェクトの宣言
  Button  start, stop, clear, prmup_B, prmdown_B;
  // 状態平面の宣言
  StatePlane sPlane = new StatePlane(this);
  // ルンゲクッタ法のオブジェクト
  // 対象となる微分方程式が記述してあるオブジェクトでもある.
  // Pm, Xe 等のパラメータの値も保持している. .... f.Pm のように参照.
  MyRungeKutta f = new MyRungeKutta();

  //フィールド変数の宣言
  double PI= 3.14159265358979;
  double[] x = new double[f.dim+1];
  double T;  // 時刻
  double H;  // ルンゲクッタ法の時間刻 ... f.toNextPoint()の引数の一つ

  Double Dtmp1,Dtmp2; // 実数型オブジェクトの宣言 ... 実数型の変数とは別.
  boolean ThreadIsRunning;

  // init() ... アップレットがロードされたときに呼び出されるメソッド
  // ----------------------------------------------------------------------
  public void init() {
    // 初期値
    T = 0.0;
    H = 0.1;
    x[1]= 5.0;
    x[2]= -4.5;
    x[3]= 0.0;

    //シミュレーション操作のボタン
    Panel bp=new Panel();//ボタン設定位置の範囲
    bp.add(start=new Button("start"));
    bp.add(stop=new Button("stop"));
    bp.add(clear=new Button("clear"));

    Panel px=new Panel(); //パラメータ値入力部設定位置の範囲
    px.setLayout(new GridLayout(0,4)); //４列に並べる
    //各パラメータの入力欄を作る ... 戻り値は TextField 
    x_T 	= paramPanel(px, "x",	x[1],	"");
    y_T 	= paramPanel(px, "y",	x[2],	"");
    z_T 	= paramPanel(px, "z",	x[3],	"");
    px.add(new Label(""));
    xmin_T 	= paramPanel(px, "xmin", sPlane.xmin,	"");
    xmax_T 	= paramPanel(px, "xmax", sPlane.xmax,	"");
    ymin_T 	= paramPanel(px, "ymin", sPlane.ymin,	"");
    ymax_T 	= paramPanel(px, "ymax", sPlane.ymax,	"");
    a_T 	= paramPanel(px, "a",	f.a,	"");
    b_T 	= paramPanel(px, "b",	f.b,	"");
    c_T 	= paramPanel(px, "c",	f.c,	"");
    prminc_T 	= paramPanel(px, "prm inc",	f.prminc,	"");
    px.add(prmup_B= new Button("prm up"));
    px.add(prmdown_B= new Button("prm down"));
    H_T 	= paramPanel(px, "Tstep",	H,	"(sec)");
    T_T 	= paramPanel(px, " Time",	T,	"(sec)");

    setLayout(new BorderLayout()); //画面全体のレイアウト
    add("North",  bp); //ボタン類は一番上
    add("Center", sPlane); //キャンバスは中心
    add("South",  px); //パラメータ入力部とスクロールバーは一番下

//    sPlane.initialize(); // 現在のキャンバスの大きさに gxmax などを合わせる
  }

  // paramPanel() ... パラメータ値代入のための欄を作って張り付ける
  // ----------------------------------------------------------------------
  public TextField paramPanel(Panel p, String paramlabel, 
			      double ini, String paramunit){
    Panel pp = new Panel();
//    TextField t = new TextField(ini+"",6);
    TextField t = new TextField(ini+"",10);
    pp.add(new Label(paramlabel,Label.RIGHT));
    pp.add(t);
    pp.add(new Label(paramunit));
    p.add(pp);
    return t;
  }

  // start() ... アプレットの開始時にスレッドオブジェクトを生成
  //             し、開始する。これによりrunメソッドが実行される
  // ----------------------------------------------------------------------
  public void start(){
    if(th==null){
      th=new Thread(this);             // スレッド生成
      th.start();
    }
  }

  // run() ... スレッドの実行を制御する
  // ユーザーの処理したい内容を記述
  // ----------------------------------------------------------------------
  public void run(){
    th.suspend();                     // スレッドの一時停止
//    sPlane.initialize(); // 現在のキャンバスの大きさに gxmax などを合わせる
    while(true){
      try{ th.sleep(30); }            // 遅延時間の設定
      catch(InterruptedException e){};// 例外処理無し... おまじない
      for(int co=0; co<20; co++,T+=H){ //この間は他のことはあまりしない
	sPlane.plot(x[1], x[2], sPlane.withLine, Color.white, 1);
	x = f.toNextPoint(x,T,H);
      }
//      T_T.setText(T+""); // T の表示更新 ... を行なうと描画が狂う？
    }
  }
  
  // action()... アプレットでアクションが起こった時呼び出される
  // actionの種類 : テキストフィールド入力、ボタン操作
  // ----------------------------------------------------------------------
  public boolean action(Event e,Object o) {
    // テキストフィールドでリターンキー??を押した場合
    if(e.target instanceof TextField){ 
      TextField tf = (TextField) e.target;
      Dtmp2= Dtmp1.valueOf(tf.getText());
      if      (e.target==x_T){
	x[1] = Dtmp2.doubleValue();
	sPlane.resetLastXy(); // 不連続に点が動くから.
      }else if(e.target==y_T){
	x[2] = Dtmp2.doubleValue();
	sPlane.resetLastXy(); // 不連続に点が動くから.
      }else if(e.target==z_T){
	x[3] = Dtmp2.doubleValue();
	sPlane.resetLastXy(); // 不連続に点が動くから.
      }else if(e.target==xmin_T){
	sPlane.setXrange(Dtmp2.doubleValue(), sPlane.xmax);
	sPlane.clear();            //状態平面をクリア
      }else if(e.target==xmax_T){
	sPlane.setXrange(sPlane.xmin, Dtmp2.doubleValue());
	sPlane.clear();            //状態平面をクリア
      }else if(e.target==ymin_T){
	sPlane.setYrange(Dtmp2.doubleValue(), sPlane.ymax);
	sPlane.clear();            //状態平面をクリア
      }else if(e.target==ymax_T){
	sPlane.setYrange(sPlane.ymin, Dtmp2.doubleValue());
	sPlane.clear();            //状態平面をクリア
      }else if(e.target==a_T){
	f.set_a(Dtmp2.doubleValue());
      }else if(e.target==b_T){
	f.set_b(Dtmp2.doubleValue());
      }else if(e.target==c_T){
	f.set_c(Dtmp2.doubleValue());
      }else if(e.target==prminc_T){
	f.set_prminc(Dtmp2.doubleValue());
      }else if(e.target==H_T){
	H = Dtmp2.doubleValue();
      }else if(e.target==T_T){
	T = Dtmp2.doubleValue();
      }
    }else if(e.target instanceof Button){ // ボタンを押した場合
      if(e.target==start){
	sPlane.initialize(); // 現在のキャンバスの大きさに gxmax などを合わせる
	th.resume();
	ThreadIsRunning= true;
      }else if(e.target==stop){
	th.suspend();
	ThreadIsRunning= false;
	updateTextField(); // テキストフィールドの表示更新
      }else if(e.target==clear){
	sPlane.clear();            //状態平面をクリア
      }else if(e.target==prmup_B){
	f.set_a(f.a+f.prminc);
	a_T.setText(f.a+"");
      }else if(e.target==prmdown_B){
	f.set_a(f.a-f.prminc);
	a_T.setText(f.a+"");
      }
    }               
    return true;
  }

  void updateTextField(){  // テキストフィールドの表示更新
    x_T.setText(x[1]+"");
    y_T.setText(x[2]+"");
    z_T.setText(x[3]+"");
    xmin_T.setText(sPlane.xmin+"");
    xmax_T.setText(sPlane.xmax+"");
    ymin_T.setText(sPlane.ymin+"");
    ymax_T.setText(sPlane.ymax+"");
    a_T.setText(f.a+"");
    b_T.setText(f.b+"");
    c_T.setText(f.c+"");
    prminc_T.setText(f.prminc+"");
    H_T.setText(H+"");
    T_T.setText(T+"");
  }

  public synchronized void setStateVariable(double[] v){
    sPlane.resetLastXy(); // これを行なうべきタイミングはかなり微妙
    this.x[1]= v[1];
    this.x[2]= v[2];
    x_T.setText(x[1]+"");
    y_T.setText(x[2]+"");
  }

  // stop()...アプレット終了時にスレッドを停止し
  //          スレッド・オブジェクトをクリアする
  // ----------------------------------------------------------------------
  public void stop(){
    if(th!=null){
      th.stop();
      th=null;
    }
  }
  
} // ********************* ssim01_s クラスの終わり ***************************


// ######################################################################## 
class MyRungeKutta extends RungeKutta{
  double a= 0.172;
  double b= 0.2; 
  double c= 5.7;
  double prminc= 0.002;

  MyRungeKutta(){
    super(3); // superのコンストラクタが最初.  この引数は方程式の次元.
  }

  public double[] dx(double[] x, double time){
  // dx(double y[])...システムのモデルを表す微分方程式の微分値を返すメソッド
    // xx[]は絶対にフィールド変数にしてはいけない！ ここで宣言すべし.
    // dx() によってreturn されるから.
    double[] xx = new double[dim+1]; 

    xx[1]= -x[2] - x[3];      // dxdt=  -y - z
    xx[2]= x[1] + a*x[2];     // dydt=  x + a*y
    xx[3]= b + x[3]*(x[1]-c); // dzdt=  b + z*(x - c)
    return xx;
  }
  void set_a(double v){ a = v; }
  void set_b(double v){ b = v; }
  void set_c(double v){ c = v; }
  void set_prminc(double v){ prminc = v; }
}

// ######################################################################## 
class StatePlane extends PlotPlane{
  ssim01_s app;

  StatePlane(ssim01_s app){
//  super(xmin, xmax, ymin, ymax, xmargin, ymargin);
    super(-20,  20,   -20,  20,   45,      30);
    this.app = app;
    xtic[0]=-100.0;  xtic[1]= 10.0;  xtic[2]= 100.0;
    ytic[0]=-100.0;  ytic[1]= 10.0;  ytic[2]= 100.0;
    xlabel= "x";
    ylabel= "y";
  }

  public boolean mouseDown(Event e,int mx,int my){
    double[] xa= new double[3];

    xa[1]= toRealx(mx);
    xa[2]= toRealy(my);
   // resetLastXy(); // これを行なうべきタイミングはかなり微妙
    app.setStateVariable(xa);
    return true;
  }
}
