
import java.awt.*;
import java.applet.Applet;
import java.net.*;
import java.io.*;
import java.util.StringTokenizer;
//import java.lang.reflect.Array;

public class Guruguru extends Applet implements Runnable {

	MediaTracker mt;	// イメージ読み込みの監視
	// MediaTracker は複数のイメージを扱う場合に利用
	// 通常は ImageObserver の方がいいかも

        Thread kicker = null;
//	Panel p1;
	Color chgColor;	// 暗い色、明るい色を作る時、作成した色をセットする
	// 変な使い方、時間があったら直す事

	Image img;	// イメージ

	Dimension d;	// 表示領域
	Image  offs;	// オフスクリーン
    	Graphics grf;

	// デフォルト設定値
	final int DCSIZE1=20;	// セルのサイズ縦・横（上下を基準）
	final int DCSIZE2=40;	// 上下のセルと左右のセルでは向きが90度変わる
	final int DCXMAX=5;	// セルの横の個数
	final int DCYMAX=5;	// セルの縦の個数
	final int DDRKSTEP=20;	// 基本色から暗い色を作る時の間隔
	final int DBRTSTEP=20;	// 基本色から明るい色を作る時の間隔
	final int DWAIT=200;	// 変化の待ち時間
	final int DCOLMAX=8;	// 基本色の数 ８色
	final Color DDRAWCOLOR=Color.white;	// 下地の色
	final Color DCOL[]={	Color.blue, Color.cyan, Color.green, Color.magenta,
				Color.yellow, Color.pink, Color.red, Color.orange };

	int csize1, csize2;	// セルのサイズ縦・横
	int gsizex;		// 画面のサイズ横
	int gsizey;		// 画面のサイズ縦
	int cxmax;		// セルの横の個数
	int cymax;		// セルの縦の個数
	int darkstep;		// 基本色から暗い色を作る時の間隔
	int brightstep;		// 基本色から明るい色を作る時の間隔
	int wait;		// 変化の待ち時間
	int colmax;		// 基本色の数
	Color col[];		// 基本色の配列
	Color drawcolor;	// 下地の色

	// セルデータ（x[0],y[0],width[0],height[0],color[0][]がひとつのセルを表す）
	// クラスにしてしまう方がきれい
	int x[];	// 表示位置
	int y[];
	int width[];	// サイズ縦・横
	int height[];
	Color color[][];	// グラデーションデータ（表示する全部のセルの半分にあたる）

	int count=0;	// セルにデータをセットするとき使っている（かなり変）
	int henka=0;	// 表示中の色、グラデーションのどの位置か

	String imagefile;	// イメージを表示する場合のファイル名
	String colorfile;	// グラデーションファイルを読み込む場合のファイル名
	int flagimage=0;	// 1 : イメージ表示する
	int flagfile=0;		// 1 : グラデーションファイルを読み込む
	int flagcell=0;		// セルの形 (0="Rect", 1="3DRect", 2="RoundRect")
	int testmode=0;		// 1 : テストモード
				// グラデーション見本表示とグラデーションデータ出力

	public void init() {
		mt = new MediaTracker(this);	// イメージ読み込みの監視

		getPara();	// パラメータの読み込み		

		int kosuu=cxmax*2+cymax*2;
		x = new int[kosuu];
		y = new int[kosuu];
		width = new int[kosuu];
		height = new int[kosuu];
		gsizex = (csize2 * cxmax) + csize1;
		gsizey = (csize2 * cymax) + csize1;

		if (flagfile == 0) {
			color = new Color[kosuu][cxmax+cymax];
			makeColor();			// グラデーション作成
		} else {
			readGradation(colorfile);	// グラデーションファイルの読み込み
		}
		makeCells();	// セルの個数、サイズなどから各セルの表示位置など決めるてセットする

		/* オフスクリーンの設定 */
		//
		//d = getSize();	// 表示領域の取得 Netscape 4.04 でエラー
		d = size();		// 表示領域の取得
		offs = createImage(d.width, d.height);
			// java.awt.Component
			// ダブルバッファリング用にオフスクリーン作成 
		grf  = offs.getGraphics();
			// java.awt.Component
			// Graphics コンテキストを得る
// 文字列表示するときの準備
//		p1 = new Panel();
//		p1.setLayout(new BorderLayout());

		setBackground(drawcolor);		// 背景色のセット
		//setSize(gsizex, gsizey);	// サイズ変更 Netscape 4.04 でエラー
		resize(gsizex, gsizey);		// サイズ変更
		grf.clearRect(0, 0, gsizex, gsizey);	// 背景色でクリア

		if (flagimage == 1) {
			//System.out.println("Image ON!");
			img = getImage(getDocumentBase(), imagefile);
				// java.applet.Applet
				// Image オブジェクトを返す
			mt.addImage(img, 0);	// イメージ読み込み監視リストへ追加
		}
	}

	public void start() {
		if(kicker == null) {
			/* スレッドを実行させる */
			kicker=new Thread(this);
			kicker.start();
		}
	}

	public void stop() {
		if (kicker != null) {
			/* スレッドを止める */
			kicker.stop();
			kicker = null;
		}
	}

	public void run() {
		/* 全てのイメージの読み込みを待つ */
		try{
			mt.waitForID(0);	// ID=0 のイメージの読み込み開始と終了待ち
		} catch (InterruptedException e) {
			return;
		}
		if (flagimage == 1) {
			grf.drawImage(img, 0, 0, gsizex, gsizey, this);
		}
		boolean loop = true;      // 繰り返すための変数
		/* ずっと繰り返し */
		while(loop) {
			henka++;	// 色が順に変化していく
			if (henka >= (cxmax+cymax)) henka=0;
			repaint();
			try {
				/* スレッドのお休み時間＝表示時間の設定 */
				Thread.sleep(wait);
			} catch (InterruptedException e){}
		}
	}

	public void update(Graphics g) {
		// オリジナル update() は画面クリアでちらつくので
		// クリアなし
		paint(g);
	}

	public void paint(Graphics g) {
		if (testmode == 0) {
			dispCell(grf);
		} else {
			dispColorSample(grf);	// 色見本の表を表示する
		}
		/* オフスクリーンから表示する */
		g.drawImage(offs, 0, 0, this);
	}

	/* 上から右、下、左の順でセルをセットする */
	void makeCells() {
		int i, j;

		count = 0;
		j=0;
		for (i=0; i<(gsizex-csize1); i+=csize2) {
			setCell(i, j, csize2, csize1);
			count++;
		}
		//System.out.println(count);
		for (; j<(gsizey-csize1); j+=csize2) {
			setCell(i, j, csize1, csize2);
			count++;
		}
		//System.out.println(count);
		for (i-=(csize2-csize1); i>=csize1; i-=csize2) {
			setCell(i, j, csize2, csize1);
			count++;
		}
		//System.out.println(count);
		i=0;
		for (j-=(csize2-csize1); j>=csize1; j-=csize2) {
			setCell(i, j, csize1, csize2);
			count++;
		}
		//System.out.println(count);
	}

	/* セルの配列へ表示位置と縦横の長さをセットする */
	public void setCell(int x, int y, int width, int height) {
		this.x[count] = x;
		this.y[count] = y;
		this.width[count] = width;
		this.height[count] = height;
		// カラーは別にセット済み
	}

	void dispCell(Graphics g) {
		int i, j;

		count = 0;
		for (i=0; i<(cxmax*2+cymax*2); i++) {
			g.setColor(color[count][henka]);
			if (flagcell == 0) {
				g.fillRect(x[count], y[count], width[count], height[count]);
			}
			if (flagcell == 1) {
				g.fill3DRect(x[count], y[count], width[count], height[count], true);
			}
			if (flagcell == 2) {
				g.fillRoundRect(x[count], y[count], width[count], height[count], width[count]/3, height[count]/3);
			}
			count++;
		}
	}


	/* セルの配列に色のデータをセットする */
	/* 光ってから暗くなるまでを表すために */
	/* 色が変化するのをセットする         */
	public void makeColor() {
		int i,j;
		int c=0;	// カラーの繰り返し使用に対応する	
		int kosuu=cxmax*2+cymax*2;
		int ihenka=cxmax+cymax;
		for (i=0; i<kosuu/2; i++) {
			for (j=0; j<ihenka; j++) {
				if (j <= ihenka/2) {
					bright(col[c], brightstep*(ihenka/2-j));
					// ２個所に同じ色を設定する
					color[i][j] = chgColor;
					color[i+cxmax+cymax][j]=chgColor;
				} else {
					dark(col[c], darkstep*(j-ihenka/2));
					color[i][j] = chgColor;
					color[i+cxmax+cymax][j]=chgColor;
				}
			}
			c++;
			//if (c > Array.getLength(col)) c=0;	// java.lang.reflect.Array
			if (c >= col.length) c=0;


		}

//		for (i=0; i<kosuu; i++) {
//			for (j=0; j<ihenka; j++) {
//				System.out.println("color[" + i + "][" + j + "] = " + color[i][j]);
//			}
//		}

		// 色をずらす
		int jj, jjj, tmpj;
		Color tmpcolor;
		// i=0 は ずらさないでいい 
		for (i=1; i<kosuu/2; i++) {
			tmpj=0;
			tmpcolor = color[i][0];
			jj=0;
			jjj=ihenka-i;
			while(jjj <  0) jjj+=ihenka;
			for (j=0; j<ihenka; j++) {
				if (jjj != tmpj) {
					color[i][jj] = color[i][jjj];
					color[i+cxmax+cymax][jj] = color[i+cxmax+cymax][jjj];
					jj = jjj;
					jjj -= i;
				} else {
					color[i][jj] = tmpcolor;
					color[i+cxmax+cymax][jj] = tmpcolor;
					tmpj++;
					jj=tmpj;
					tmpcolor = color[i][jj];
					jjj=jj-i;
				}
				while(jjj <  0) jjj+=ihenka;
			}
		}
		// グラデーションのデータを出力する
		if (testmode != 0) {

			System.out.println("datamax = ["+kosuu+"]["+ihenka+"]\n");
			for (i=0; i<kosuu; i++) {
				for (j=0; j<ihenka; j++) {
					System.out.println("["+i+"]["+j+"] = " 
						+ color[i][j].getRed()
						+ "," + color[i][j].getGreen()
						+ "," + color[i][j].getBlue());
					
				}
				System.out.println("\n");
			}


		}
	}

	public void bright(Color c, int n) {
		chgColor = new Color(Math.min((int)(c.getRed()+n), 255), 
				 Math.min((int)(c.getGreen()+n), 255),
				 Math.min((int)(c.getBlue()+n), 255));
	}

	public void dark(Color c, int n) {
		chgColor = new Color(Math.max((int)(c.getRed()-n), 0), 
				 Math.max((int)(c.getGreen()-n), 0),
				 Math.max((int)(c.getBlue()-n), 0));
	}

	void dispColorSample(Graphics g) {
		int i, j;
		int kosuu=cxmax*2+cymax*2;
		int ihenka=cxmax+cymax;

		for(i=0; i<kosuu; i++) {
			for (j=0; j<ihenka; j++) {
				g.setColor(color[i][j]);
				g.fill3DRect(i*10, j*10, 10, 10, true);
			}
		}
	}

	// グラデーションファイルの読み込み
	public void readGradation(String fname){
		int lineno = 0;
		int i=0, j=0;
		int index1, index2;
		String s;
		int kosuu=0;
		int ihenka=0;

		try {
			URL u = new URL(getDocumentBase(), fname);
			InputStream in = u.openStream() ;
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
				while( ( s = br.readLine() ) != null ) {
				// １行ごと読み込む
				if (lineno == 0) {
					kosuu = getIndex(s, 1);
					ihenka = getIndex(s, 2);
					// System.out.println("kosuu="+kosuu+"   ihenka="+ihenka);
					color = new Color[kosuu][ihenka];
				} else {
					//System.out.println(lineno+" :"+s);
					if (s.length() > 3) {
						index1 = getIndex(s, 1);
						index2 = getIndex(s, 2);
						getRGB(s.substring(s.indexOf('=', 0)+1, s.length()));
						// System.out.println("["+index1+"]["+index2+"]"+colorRGB[0]+","+colorRGB[1]+","+colorRGB[2]);
						// データのセット
						color[i][j] = new Color(colorRGB[0], colorRGB[1], colorRGB[2]);
						j++;
						if (j == ihenka) {
							j = 0;
							i++;
						}
					}
				}
				lineno++;		// 行カウント
			}
			colmax = i;
		} catch (Exception e){
			System.err.println("Exception : "+e);
		}
	}


	// 255, 255, 0 など ','で区切られた３つの数値を
	// 配列 colorRGB にセットする
	//import java.util.StringTokenizer;
	//int colorRGB[3];
	// こうして呼び出す getRGB(s.substring(s.indexOf('=', 0)+1, s.length()));
	static int colorRGB[] = new int[3];
	static void getRGB(String s) {
		int i = 0;	
		// java.util.StringTokenizer を使う
		// StringTokenizer(String str, String delim)
		// nextToken() で取り出す
		StringTokenizer st = new StringTokenizer(s, ",");
		while (st.hasMoreTokens()) {
			colorRGB[i++] =  Integer.parseInt(st.nextToken().trim());
		}
	}


	// 文字列を先頭からチェックして、
	// n 番目の [ ] で囲まれた文字を数値として返す
	// こうして呼び出す
	// index1 = getIndex(s, 1);
	// index2 = getIndex(s, 2);
	static int getIndex(String s, int n) {
		int i, p1, p2;
		String tmp;
		p1 = 0;

		for (i=1; i < n; i++ ) {
			p1 = s.indexOf('[',p1);
			p1++;
		}
		if ((p1 = s.indexOf('[', p1)) != -1) {
			if ((p2 = s.indexOf(']',p1+1)) != -1) {
				tmp = s.substring(p1+1,p2);	// []内の文字列を得る
				return  Integer.parseInt(tmp);		// 数値にしてreturn
			}
		}
		return -1;
	} 


	public void getPara() {
		int i;
		String para; 

		// cellwidth､cellheight セルの縦、横
		para = getParameter( "cellheight" );
		csize1 = (para != null) ? Integer.parseInt(para) : DCSIZE1;
		para = getParameter( "cellwidth" );
		csize2 = (para != null) ? Integer.parseInt(para) : DCSIZE2;


		// testmode 1 なら カラーのグラデーション表示と色データ出力
		para = getParameter( "testmode" );
		testmode = (para != null) ? Integer.parseInt(para) : 0;
			// グラデーション・データ出力について
			// Win95なら appletviewer で表示した時のDOSプロンプト画面に表示
			// これをファイルに落として修正して次の指定で読み込ませる事が出来る
			// データは	[セル][グラデーション] = 赤, 緑, 青（各 0 から 255）となっている
			// 例）		[0][0] = 255, 255, 255


		// cellxmax､cellymax セルの横の数、縦の数
		// 	この合計でグラデーションがとられる。例）5, 5（合計10）
		para = getParameter( "cellxmax" );
		cxmax = (para != null) ? Integer.parseInt(para) : DCXMAX;
		para = getParameter( "cellymax" );
		cymax = (para != null) ? Integer.parseInt(para) : DCYMAX;


		// colorfile グラデーションのデータファイルの指定
		// 	testmode=1で出力されるデータをファイルに落として修正したもの
		// 	この指定があるとき、colormax,cellcolorX,colorbright,colordarkは無視される
		colorfile = getParameter("colorfile");	// グラデーションファイルの名前
		// System.out.println("colorfile="+colorfile);
		if (colorfile != null) {
			flagfile = 1;
		}


		// colorbright デフォルト 20、明るい方のグラデーションの間隔
		// colordark   デフォルト 20、暗い方のグラデーションの間隔
		if (flagfile == 0) {
			para = getParameter( "brightstep" );
			brightstep = (para != null) ? Integer.parseInt(para) : DBRTSTEP;
			para = getParameter( "darkstep" );
			darkstep = (para != null) ? Integer.parseInt(para) : DDRKSTEP;
		}

		// drawimage 中に表示するイメージ、周りはセルで見えなくなる、指定なしなら表示しない
		imagefile = getParameter("drawimage");	// イメージファイルの名前
		if (imagefile != null) flagimage = 1;
		//System.out.println("imagefile="+imagefile);


		// wait デフォルト 200
		para = getParameter( "wait" );
		wait = (para != null) ? Integer.parseInt(para) : DWAIT;


		// cell セルの形 デフォルト "Rect",他に "3DRect", "RoundRect"
		para = getParameter( "cell" );
		//System.out.println("cell=("+para+")");
		if (para != null) {
			if ((para.compareTo("Rect"))==0) flagcell=0;
			else if ((para.compareTo("3DRect"))==0) flagcell=1;
			else if ((para.compareTo("RoundRect"))==0) flagcell=2;
		}


		// colormax セルの基本色をいくつ定義するか示す
		// この定義がなければ、デフォルトの基本色が使われる
		if (flagfile == 0) {
			para = getParameter( "colormax" );
			if (para != null) {
				// cellcolor1,2,3...n セルの基本色 例）"blue", "cyan", "green" など
				// 	cellxmaxと cellymaxの合計を超えた分は使われない、小さい場合は繰り返し
				// black	黒 
				// blue		青 
				// cyan		シアン 
				// darkGray	ダークグレイ 
				// gray		灰色 
				// green	緑 
				// lightGray	ライトグレイ 
				// magenta	マジェンタ 
				// orange	オレンジ 
				// pink		ピンク 
				// red		赤 
				// white	白 
				// yellow	黄色

				colmax = Integer.parseInt(para);
				col = new Color[colmax];
				for(i=0; i<colmax; i++) {
					para = getParameter( "cellcolor"+ (i+1) );
					//System.out.println("cellcolor="+para);
					col[i] = setColorPara(para);
				} // END-FOR
			} else {
				colmax = DCOLMAX;
				col = new Color[colmax];
				for(i=0; i<colmax; i++) {
					col[i] = DCOL[i];
				}
			}
		}

		// drawcolor 中につける下地の色
		para = getParameter( "drawcolor" );
		drawcolor = (para != null) ? setColorPara(para) : DDRAWCOLOR;
		//System.out.println("drawcolor="+drawcolor);
	}

	public Color setColorPara(String s) {
		Color ret = Color.white; 

		if(s.equals("black")){ ret = Color.black; }
		else if(s.equals("blue")){ ret = Color.blue; }
		else if(s.equals("cyan")){ ret = Color.cyan; }
		else if(s.equals("darkGray")){ ret = Color.darkGray; }
		else if(s.equals("gray")){ ret = Color.gray; }
		else if(s.equals("green")){ ret = Color.green; }
		else if(s.equals("lightGray")){ ret = Color.lightGray; }
		else if(s.equals("magenta")){ ret = Color.magenta; }
		else if(s.equals("orange")){ ret = Color.orange; }
		else if(s.equals("pink")){ ret = Color.pink; }
		else if(s.equals("red")){ ret = Color.red; }
		else if(s.equals("white")){ ret = Color.white; }
		else if(s.equals("yellow")){ ret = Color.yellow; }
		return ret;
	}
}
