package benham;

import java.applet.Applet;
import java.awt.Button;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Label;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Constructor;
/**
 * Benham applet
 * showing color perception from a succession of black and white pictures.
 * Antialiasing is not used here so that we have a pure black and white picture.
 * This code is JDK 1.1 compatible.
 *
 * @author Fabien Le Floc'h <31416.org>
 * @version 1.1
 */


public class BenhamApplet extends Applet
{
    public static final String VERSION            = "1.1";
    public static final long   DEFAULT_DELAY      = 40;
    public static final String START_ACTION_LABEL = "Start";
    public static final String STOP_ACTION_LABEL  = "Stop";
    public static final String REVERSE_ACTION_LABEL = "Reverse";

    public void init() 
    {
        super.init();
        final ImageCanvas imageCanvas = getImageCanvas(getWidth()-4);
        final TextField delayField    = new TextField(new Long(DEFAULT_DELAY).toString());
        delayField.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                imageCanvas.setDelay(delayField.getText());
            }
        });
        final Button startButton      = new Button(START_ACTION_LABEL);
        setBackground(Color.white);
        setForeground(Color.black);
        GridBagLayout layout = new GridBagLayout();
        setLayout(layout);
        startButton.setActionCommand(START_ACTION_LABEL);
        startButton.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent e)
                {
                    String action = e.getActionCommand();
                    if (action.equals(START_ACTION_LABEL))
                    {
                        startButton.setActionCommand(STOP_ACTION_LABEL);
                        startButton.setLabel(STOP_ACTION_LABEL);
                        imageCanvas.setDelay(delayField.getText());
                        imageCanvas.start();
                        Thread thread = new Thread(imageCanvas);
                        thread.start();
                    }
                    else if (action.equals(STOP_ACTION_LABEL))
                    {
                        startButton.setActionCommand(START_ACTION_LABEL);
                        startButton.setLabel(START_ACTION_LABEL);
                        imageCanvas.stop();
                    }
               }
            });
        final Button reverseButton = new Button(REVERSE_ACTION_LABEL);
        reverseButton.setActionCommand(REVERSE_ACTION_LABEL);
        reverseButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                imageCanvas.reverse();
            }
        });
        Label label = new Label("delay (in ms):",Label.RIGHT);
        GridBagConstraints c = new GridBagConstraints();
        Insets insets = new Insets(2,2,2,2);
        c.gridx = 1; c.gridy = 1; c.weightx = 0.5; c.insets = insets;
        c.anchor=GridBagConstraints.NORTHEAST; c.fill=GridBagConstraints.BOTH;
        layout.addLayoutComponent(label,c); add(label);
        c = new GridBagConstraints();
        c.gridx = 2; c.gridy = 1; c.weightx = 0;c.insets = insets;
        c.anchor=GridBagConstraints.NORTH; c.fill=GridBagConstraints.VERTICAL;
        layout.addLayoutComponent(delayField,c); add(delayField);
        c = new GridBagConstraints();
        c.gridx = 3; c.gridy = 1; c.weightx = 0.5;c.insets = insets;
        c.anchor=GridBagConstraints.NORTHWEST; c.fill=GridBagConstraints.VERTICAL;
        layout.addLayoutComponent(startButton,c); add(startButton);
        c = new GridBagConstraints();
        c.gridx = 1; c.gridy = 2; c.gridwidth=3; c.weightx = 0;c.insets = insets;
        c.anchor=GridBagConstraints.NORTH; c.fill=GridBagConstraints.VERTICAL;
        layout.addLayoutComponent(reverseButton,c); add(reverseButton);
        c = new GridBagConstraints();
        c.gridx = 1; c.gridy = 3; c.gridwidth=3; c.weightx = 1; c.weighty = 1;c.insets = insets;
        c.anchor=GridBagConstraints.CENTER; c.fill=GridBagConstraints.BOTH;
        layout.addLayoutComponent(imageCanvas,c); add(imageCanvas);
        label = new Label("copyright 2003 (c) Fabien Le Floc'h <31416.org>");
        c = new GridBagConstraints();
        c.gridx = 1; c.gridy = 4; c.gridwidth=3; c.weightx = 0; c.weighty = 0;c.insets = insets;
        c.anchor=GridBagConstraints.SOUTHWEST; c.fill=GridBagConstraints.VERTICAL;
        layout.addLayoutComponent(label,c); add(label);

    }

    public ImageCanvas getImageCanvas(int length)
    {
        try
        {
            Class.forName("java.awt.Graphics2D"); //break with JDK1.1
            // load jdk12 class (antialiased version)
            // dynamically to avoid compiler check. this
            // allows compilation with jdk1.1
            Class jdk12Class = Class.forName("benham.Jdk12ImageCanvas");
            Constructor c = jdk12Class.getConstructor(new Class[]{Integer.TYPE});
            return (ImageCanvas) c.newInstance(new Object[] {new Integer(length)});
        }
        catch (Throwable t)
        {
            t.printStackTrace();
            return new ImageCanvas(length);
        }
    }

    protected static class ImageCanvas extends Canvas implements Runnable
    {
        protected Image[] _imageCache;
        protected int _length;
        protected long _delay;
        protected boolean _started;

        public ImageCanvas(int length)
        {
            _imageCache = new Image[3];
            _length = length;
            _delay = DEFAULT_DELAY;
        }

        public synchronized void setDelay(String delay)
        {
            try
            {
                _delay = Long.parseLong(delay);
            }
            catch (Exception e)
            {
                // don't notify stupid user
            }
        }
        
        public synchronized void start()
        {
            _started = true;
        }

        public synchronized void stop()
        {
            _started = false;
        }

        public void run()
        {
            while (_started)
            {
                repaint();
            }
        }

        protected synchronized void initCache()
        {
            Image image;

            image = createImage(_length,_length);
            paintDisc(image.getGraphics(),-180);
            _imageCache[0] = image;

            image= createImage(_length,_length);
            paintDisc(image.getGraphics(),60);
            _imageCache[1] = image;

            image = createImage(_length,_length);
            paintDisc(image.getGraphics(),-60);
            _imageCache[2] = image;
        }

        protected void paintDisc(Graphics g, int startArcAngle)
        {
            paintImage(g,startArcAngle+120,-60,0.9); // green            
            paintImage(g,startArcAngle+120,-60,0.8); // green
            paintImage(g,startArcAngle+120,-60,0.7);
            paintImage(g,startArcAngle+180,-60,0.6); //blue
            paintImage(g,startArcAngle+180,-60,0.5);            
            paintImage(g,startArcAngle+180,-60,0.4);
            paintImage(g,startArcAngle,60,0.3); //red
            paintImage(g,startArcAngle,60,0.2); //red
            paintImage(g,startArcAngle,60,0.1); //red
            paintHalfDisc(g,startArcAngle);
        }


        protected void paintImage(Graphics g, int startArcAngle, int arcAngle, double radius)
        {
            int width = (int) Math.round(_length*radius);
            int length = width;
            int x = (_length-width)/2;
            int y = x;
            g.setColor(Color.black);
            g.fillArc(x-1,y-1,width+2,length+2,startArcAngle,arcAngle);
            g.setColor(Color.white);
            g.fillArc(x+2,y+2,width-4,length-4,startArcAngle,360);
        }

        protected void paintHalfDisc(Graphics g, int startAngle)
        {
            g.setColor(Color.black);
            g.fillArc(0,0,_length,_length,startAngle,-180);
        }

        public void update(Graphics g)
        {
            paint(g);
        }

        public synchronized void reverse()
        {
            Image temp = _imageCache[0];
            _imageCache[0] = _imageCache[2];
            _imageCache[2] = temp;
        }
        
        protected void drawCycle(Graphics g)
        {
            try
            {
                g.drawImage(_imageCache[0],0,0,this);
                Thread.sleep(_delay);
                g.drawImage(_imageCache[1],0,0,this);
                Thread.sleep(_delay);
                g.drawImage(_imageCache[2],0,0,this);
                Thread.sleep(_delay);
            } 
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }

        public void paint(Graphics g)
        {
            if (_imageCache[0] == null) 
            {
                initCache();
            }
            if (_started)
            {
                drawCycle(g);
            }
            else 
            {
                g.drawImage(_imageCache[0],0,0,this);
            }
        }

        public void setBounds(int x, int y, int width, int height)
        {
            _length = Math.min(height,width);
            initCache();
            super.setBounds(x, y, width, height);
        }


    }
}