/** [BufferTest.java] Copyright(c) 2000-2004 WakuWaku. */
import java.awt.*;
import java.applet.Applet;
import java.util.Random;

public class BufferTest extends Applet implements Runnable
{
        final static int MAX = 10, SZ = 38;       //左からボールの最大表示数, ボールのサイズ
        final static Color[] COL = new Color[SZ];
        Graphics gBuf;        //オフスクリーンバッファのグラフィックコンテキスト
        Image imgBuf;
        Thread thread = null;
        int[] x, y, m;        //左からボールのX, Y座標, 移動量
        int w, h;

        static
        {
                for(int i = 0; i < SZ; i++)
                {
                        int c = 255 - i * 255 / (SZ - 1);
                        COL[i] = new Color(c, c, 255 - c);
                }
        }

        public void init()  //アプレットの初期化時呼び出される
        {
                Dimension d = getSize();      //アプレットサイズ取得
                this.w = d.width;
                this.h = d.height;
                //オフスクリーンバッファ(以下バッファ)の作成
                imgBuf = createImage(w, h);     //バッファ生成
                gBuf = imgBuf.getGraphics();    //グラフィックコンテキスト取得
        }

        private void drawBackImage(Graphics g)    //背景イメージ描画メソッド
        {
                Random rand = new Random(123);    //種を固定
                for(int i = 0; i < h; i++)
                {
                        g.setColor(new Color(0, 0, (int)(rand.nextFloat() * 255)));
                        g.drawLine(0, i, w, i);
                }
                g.setFont(new Font("Arial", Font.ITALIC, 48));
                g.setColor(Color.gray);
                g.drawString("Buffer Test", 40, h / 2 + 16);
        }

        public void start() //アプレットが表示されると呼び出される
        {
                if(thread == null)
                {
                        thread = new Thread(this);        //スレッドの生成
                        thread.start(); //スレッドを開始させ、runメソッドを呼び出す
                }
        }

        public void run()   //メインルーチン
        {
                //ボール初期化
                Ball[] ball = new Ball[MAX];
                for(int i = 0; i < MAX; i++)
                        ball[i] = new Ball(-99, 0, 0);

                //背景画像の作成
                Image imgBack = createImage(w, h);    //背景画像生成
                Graphics gBack = imgBack.getGraphics();       //グラフィックコンテキスト取得
                drawBackImage(gBack);
                gBack.dispose();        //グラフィックコンテキスト破棄

                int wait = 0;
                Random rand = new Random(456);    //種を固定

                while(thread != null)
                {
                        wait--;
                        gBuf.drawImage(imgBack, 0, 0, this);  //背景画像をバッファに描画
                        for(int i = 0; i < MAX; i++)
                        {
                                Ball target = ball[i];
                                if(target.x == -99 && wait < 0)
                                {
                                        target.x = -SZ;
                                        target.y = (int)(rand.nextFloat() * (h - SZ));
                                        target.move = (int)(rand.nextFloat() * 7) + 2;
                                        wait = (int)(rand.nextFloat() * 20) + 4;
                                }else
                                {
                                        target.move();  //ボールを移動
                                        if(target.x > w)
                                                target.x =- 99;
                                        for(int r = SZ - 1; r >= 0; r--)
                                        {
                                                gBuf.setColor(COL[r]);  //ボールの色設定
                                                gBuf.fillOval(target.x + (SZ - r) / 2,
                                                        target.y + (SZ - r) / 2, r, r); //ボールをバッファに描画
                                        }
                                }
                        }
                        repaint();      //画面の強制更新
                        try
                        {
                                Thread.sleep(50);     //0.05秒間スリープ。これを忘れるとハングアップする
                        }catch(InterruptedException e) {}
                }
        }

        public void update(Graphics g)    //オーバーライドして最低限のことだけをする
        {
                paint(g);
        }
        public void paint(Graphics g)
        {
                g.drawImage(imgBuf, 0, 0, this);      //バッファを画面に描画
        }

        public void stop()  //アプレットが画面から消えると呼び出される
        {
                if(thread != null)
                        thread = null;
        }
}

//一つのボールを表すクラス
class Ball
{
        int x;        //x座標
        int y;        //y座標
        int move;     //x方向への移動量

        public Ball(int x, int y, int move)
        {
                this.x = x;
                this.y = y;
                this.move = move;
        }

        //ボールを移動
        public void move()
        {
                this.x += this.move;
        }
}