/*
 * @(#)Balls.java	1.15 98/09/13
 *
 * Copyright 1998 by Sun Microsystems, Inc.,
 * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information
 * of Sun Microsystems, Inc. ("Confidential Information").  You
 * shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement
 * you entered into with Sun.
 */

package demos.Mix;

import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.swing.*;
import AnimatingContext;
import DemoSurface;
import DemoPanel;
import CustomControls;


/**
 * Animated color bouncing balls with custom controls.
 */
public class Balls extends DemoSurface implements AnimatingContext, CustomControls {

    private static Color colors[] = 
            { Color.red, Color.orange, Color.yellow, Color.green.darker(),
              Color.blue, new Color(75, 00, 82), new Color(238,130,238) };
    private long now, deltaT, lasttime;
    private boolean active;
    protected Ball balls[] = new Ball[colors.length];


    public Balls() {
        setBackground(Color.white);
        for (int i = 0; i < colors.length; i++) {
            balls[i] = new Ball(colors[i], 30);
        }
        balls[0].isSelected = true;
        balls[3].isSelected = true;
        balls[4].isSelected = true;
        balls[6].isSelected = true;
    }


    public String getCustomControlsConstraint() {
        return "North";
    }


    public Component getCustomControls() {
        return new AnimControls(this);
    }


    public void reset(int w, int h) { }


    public void step(int w, int h) {
        if (lasttime == 0) {
            lasttime = System.currentTimeMillis();
        }
        now = System.currentTimeMillis();
        deltaT = now - lasttime;
        active = false;
        for (int i = 0; i < balls.length; i++) {
            if (balls[i] == null) {
                return;
            }
            balls[i].step(deltaT, w, h);
            if (balls[i].Vy > .02 || -balls[i].Vy > .02 ||
                    balls[i].y + balls[i].bsize < h) {
                active = true;
            }
        }
        if (!active) {
            for (int i = 0; i < balls.length; i++) {
                balls[i].Vx = (float)Math.random() / 4.0f - 0.125f;
                balls[i].Vy = -(float)Math.random() / 4.0f - 0.2f;
            }
        }
    }


    public void drawDemo(int w, int h, Graphics2D g2) {
        for (int i = 0; i < balls.length; i++) {
            Ball b = balls[i];
            if (b == null || b.imgs[b.index] == null || !b.isSelected) {
                continue;
            }
            g2.drawImage(b.imgs[b.index], (int) b.x, (int) b.y, this);
        }
        lasttime = now;
    }


    public static void main(String argv[]) {
        final DemoPanel dp = new DemoPanel(new Balls());
        JFrame f = new JFrame("Java2D Demo - Balls");
        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {System.exit(0);}
            public void windowDeiconified(WindowEvent e) { 
                dp.surface.start(); 
            }
            public void windowIconified(WindowEvent e) { 
                dp.surface.stop(); 
            }
        });
        f.getContentPane().add("Center", dp);
        f.pack();
        f.setSize(new Dimension(400,300));
        f.show();
        dp.surface.start();
    }


    static class AnimControls extends JPanel implements ActionListener {

        Balls ba;
        JToolBar toolbar;
        JComboBox combo;

        public AnimControls(Balls ba) {
            this.ba = ba;
            setBackground(Color.gray);
            add(toolbar = new JToolBar());
            addTool("Clear", true);
            addTool("R", ba.balls[0].isSelected);
            addTool("O", ba.balls[1].isSelected);
            addTool("Y", ba.balls[2].isSelected);
            addTool("G", ba.balls[3].isSelected);
            addTool("B", ba.balls[4].isSelected);
            addTool("I", ba.balls[5].isSelected);
            addTool("V", ba.balls[6].isSelected);
            add(combo = new JComboBox());
            combo.addItem("10");
            combo.addItem("20");
            combo.addItem("30");
            combo.addItem("40");
            combo.addItem("50");
            combo.addItem("60");
            combo.addItem("70");
            combo.addItem("80");
            combo.addItem("90");
            combo.setSelectedIndex(2);
            combo.addActionListener(this);
        }


        public void addTool(String str, boolean state) {
            JButton b = (JButton) toolbar.add(new JButton(str));
            b.setBackground(state ? Color.green : Color.lightGray);
            b.setSelected(state);
            b.addActionListener(this);
        }


        public void actionPerformed(ActionEvent e) {
            if (e.getSource() instanceof JComboBox) {
                int size = Integer.parseInt((String) combo.getSelectedItem());
                for (int i = 0; i < ba.balls.length; i++) {
                    ba.balls[i].makeImages(size);
                }
                return;
            }
            JButton b = (JButton) e.getSource();
            b.setSelected(!b.isSelected());
            b.setBackground(b.isSelected() ? Color.green : Color.lightGray);
            if (b.getText().equals("Clear")) {
                ba.clearSurface = b.isSelected();
            } else {
                int index = toolbar.getComponentIndex(b)-1;
                ba.balls[index].isSelected = b.isSelected();
            }
        }


        public Dimension getPreferredSize() {
            return new Dimension(200,37);
        }
    }


public class Ball {

    public int bsize;
    public float x, y;
    public float Vx = 0.1f;
    public float Vy = 0.05f;
    public int nImgs = 5;
    public BufferedImage imgs[];
    public int index = (int) (Math.random() * (nImgs-1));

    private final float inelasticity = .96f;
    private final float Ax = 0.0f;
    private final float Ay = 0.0002f;
    private final float Ar = 0.9f;
    private final int UP = 0;
    private final int DOWN = 1;
    private int indexDirection = UP;
    private boolean collision_x, collision_y;
    private float jitter;
    private Color color;
    private boolean isSelected;


    public Ball(Color color, int bsize) {
        this.color = color;
        makeImages(bsize);
    }


    public void makeImages(int bsize) {
        this.bsize = bsize*2;
        int R = bsize;
        byte[] data = new byte[R * 2 * R * 2];
        int maxr = 0;
        for (int Y = 2 * R; --Y >= 0;) {
            int x0 = (int) (Math.sqrt(R * R - (Y - R) * (Y - R)) + 0.5);
            int p = Y * (R * 2) + R - x0;
            for (int X = -x0; X < x0; X++) {
                int x = X + 15;
                int y = Y - R + 15;
                int r = (int) (Math.sqrt(x * x + y * y) + 0.5);
                if (r > maxr) {
                    maxr = r;
                }
                data[p++] = r <= 0 ? 1 : (byte) r;
            }
        }

        imgs = new BufferedImage[nImgs];

        int bg = 255;
        byte red[] = new byte[256];
        red[0] = (byte) bg;
        byte green[] = new byte[256];
        green[0] = (byte) bg;
        byte blue[] = new byte[256];
        blue[0] = (byte) bg;

        for (int r = 0; r < imgs.length; r++) {
            float b = 0.5f + (float) ((r+1f)/imgs.length/2f);
            for (int i = maxr; i >= 1; --i) {
                float d = (float) i / maxr;
                red[i] = (byte) blend(blend(color.getRed(), 255, d), bg, b);
                green[i] = (byte) blend(blend(color.getGreen(), 255, d), bg, b);
                blue[i] = (byte) blend(blend(color.getBlue(), 255, d), bg, b);
            }
            IndexColorModel icm = new IndexColorModel(8, maxr + 1,
                        red, green, blue, 0);
            DataBufferByte dbb = new DataBufferByte(data, data.length);
            int bandOffsets[] = {0};
            WritableRaster wr = Raster.createInterleavedRaster(dbb,
                R*2,R*2,R*2,1, bandOffsets,null);
            imgs[r] = new BufferedImage(icm, wr,icm.isAlphaPremultiplied(),null);
        }
    }


    private final int blend(int fg, int bg, float fgfactor) {
        return (int) (bg + (fg - bg) * fgfactor);
    }


    public void step(long deltaT, int w, int h) {
        collision_x = false;
        collision_y = false;

        jitter = (float) Math.random() * .01f - .005f;

        x += Vx * deltaT + (Ax / 2.0) * deltaT * deltaT;
        y += Vy * deltaT + (Ay / 2.0) * deltaT * deltaT;
        if (x <= 0.0f) {
            x = 0.0f;
            Vx = -Vx * inelasticity + jitter;
            collision_x = true;
        }
        if (x + bsize >= w) {
            x = w - bsize;
            Vx = -Vx * inelasticity + jitter;
            collision_x = true;
        }
        if (y <= 0) {
            y = 0;
            Vy = -Vy * inelasticity + jitter;
            collision_y = true;
        }
        if (y + bsize >= h) {
            y = h - bsize;
            Vx *= inelasticity;
            Vy = -Vy * inelasticity + jitter;
            collision_y = true;
        }
        Vy = Vy + Ay * deltaT;
        Vx = Vx + Ax * deltaT;

        if (indexDirection == UP) {
            index++; 
        }
        if (indexDirection == DOWN) {
            --index; 
        }
        if (index+1 == nImgs) {
            indexDirection = DOWN;
        }
        if (index == 0) {
            indexDirection = UP;
        }
    }
}  // End class Ball

} // End class Balls
