import java.applet.Applet;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.MemoryImageSource;
import java.io.PrintStream;
import java.util.EventObject;

/** 
 * This is the main class representing the traffic flow simulations. It has been written
 * during a seminar held at Konstanz University. The literature for the algorithms in this
 * applet has been "Numerical Methods for Conservation Laws" from R.J. LeVeque
 * It contains the algorithm for solving the Burgers equation.
 * The input data are read with the included get...-methods
 * @see NumerikApplet#computation1
 * @see NumerikApplet#getRed()
 * @see NumerikApplet#getGreen() 
 * @see NumerikApplet#getRho()
 * @version 1.0
 * @author c
 */
public class NumerikApplet extends Applet
    implements Runnable, ActionListener
{
    class BorderElement extends Canvas
    {

        public Dimension getMinimumSize()
        {
            return new Dimension(8, 8);
        }

        public Dimension getPreferredSize()
        {
            return getMinimumSize();
        }

        public BorderElement()
        {
            setBackground(Color.lightGray);
        }
    }

    /**
     * Constructor of the applet, sets the default values for the simulation parameters
     * rho, green, red. The components are added with the call to init()
     * @see #init()
     */
    public NumerikApplet()
    {
	rho = 0.4D;
	green = 0.7D;
	red = 0.3D;
	line = 0;
	//so = new SolutionObservable();
    }
    
    public void actionPerformed(ActionEvent actionevent)
    {
        if(actionevent.getSource() instanceof Button)
            start();
    }
    
    /** 
     * Perform the actual computations.
     * Perhaps this method should be synchronized with event handling such that the
     * computaiton process is not disturbed from parameter changes made in the
     * meanwhile. This should only be a problem on very slow computers, as the computation
     * on typical machines takes less than a second. <p>
     *
     * Numerically we use a Godunov scheme to optain sharp shock fronts. 
     */
    public final void computation1()
    {
        System.out.println("doing computations");
        line = 0;
        double ad[] = new double[u.length];
        for(int i = 0; i <= phases; i++)
        {
            u[0] = rho;
            for(int j = 0; (double)j < Math.floor(red / 0.005D); j++)
                if(line < holeU.length)
                {
                    plot(u);
                    Thread.yield();
                    u[0] = rho;
                    u[200] = 1.0D;
                    for(int i1 = 1; i1 < 201; i1++)
                        if(quotf(u[i1 + 1], u[i1 - 1]) <= 0.0D)
                            ad[i1] = u[i1] - (f(u[i1 + 1]) - f(u[i1]));
                        else
                            ad[i1] = u[i1] - (f(u[i1]) - f(u[i1 - 1]));

                    ad[201] = 0.0D;
                    for(int k1 = 202; k1 < 400; k1++)
                        if(quotf(u[k1 + 1], u[k1 - 1]) <= 0.0D)
                            ad[k1] = u[k1] - (f(u[k1 + 1]) - f(u[k1]));
                        else
                            ad[k1] = u[k1] - (f(u[k1]) - f(u[k1 - 1]));

                    ad[0] = ad[1];
                    System.arraycopy(ad, 0, u, 0, ad.length);
                }

            for(int j1 = 0; (double)j1 < Math.floor(green / 0.005D); j1++)
            {
                if(u[199] - 0.5D > 0.01D)
                    u[200] = 0.5D;
                u[0] = rho;
                if(line < holeU.length)
                {
                    plot(u);
                    Thread.yield();
                    for(int l1 = 1; l1 < 201; l1++)
                        if(quotf(u[l1 + 1], u[l1 - 1]) > 0.0D)
                            ad[l1] = u[l1] - (f(u[l1]) - f(u[l1 - 1]));
                        else
                            ad[l1] = u[l1] - (f(u[l1 + 1]) - f(u[l1]));

                    for(int i2 = 201; i2 < 399; i2++)
                        if(quotf(u[i2 + 1], u[i2 - 1]) > 0.0D)
                            ad[i2] = u[i2] - (f(u[i2]) - f(u[i2 - 1]));
                        else
                            ad[i2] = u[i2] - (f(u[i2 + 1]) - f(u[i2]));

                    ad[400] = ad[399];
                    System.arraycopy(ad, 0, u, 0, ad.length);
                }
            }

        }

    }

    /** 
     * The flux function f(u)=(1-u)u
     * @param d the actual value of the function u.
     * @return the evaluation of the above function.
     */
    public final double f(double d)
    {
        return (1.0D - d) * d;
    }

    /** Obtains the length of the green phase from  the argument panel stored in the field
     * @see #args
     * @see ArgumentsPanel#getGreen()
     */
    private double getGreen()
    {
        try
        {
            return Double.valueOf(args.getGreen()).doubleValue();
        }
        catch(Exception exception)
        {
            exception.printStackTrace();
        }
        return 0.5D;
    }

    /** Obtains the length of the red phase from  the argument panel stored in the field
     * @see #args
     * @see ArgumentsPanel#getRed()
     */
    private double getRed()
    {
        try
        {
            return Double.valueOf(args.getRed()).doubleValue();
        }
        catch(Exception exception)
        {
            exception.printStackTrace();
        }
        return 0.5D;
    }

    /** Obtains the initial density in front of the traffic light from the argument panel
     * stored in the field 
     * @see #args
     * @see ArgumentsPanel#getRho()
     */
    private double getRho()
    {
        try
        {
            return Double.valueOf(args.getRho()).doubleValue();
        }
        catch(Exception exception)
        {
            exception.printStackTrace();
        }
        return 0.40000000000000002D;
    }

    public void init()
    {
        setBackground(Color.lightGray);
        heightI = 800;
        widthI = 401;
        System.out.println(heightI + " " + widthI);
        holeU = new double[heightI][];
        pix = new int[widthI * heightI];
        ct = new LinearColorTransform(0.0D, 1.0D);
        u = new double[401];
        Panel panel = new Panel(new BorderLayout(5, 0));
        Panel panel1 = new Panel(new BorderLayout(5, 0));
        args = new ArgumentsPanel(this);
        panel.add(args, "West");
        sc = new SolutionCanvas();
        panel.add(sc, "Center");
        anim = new AnimatedCanvas();
        ((Component)anim).setBackground(Color.white);
        ((Component)anim).setForeground(Color.black);
        panel1.add(new AnimatedControl(anim), "West");
        panel1.add((AnimatedCanvas)anim, "Center");
        Panel panel2 = new Panel(new BorderLayout(5, 8));
        panel2.add(panel, "Center");
        panel2.add(panel1, "South");
        setLayout(new BorderLayout());
        add(new BorderElement(), "North");
        add(new BorderElement(), "South");
        add(new BorderElement(), "East");
        add(new BorderElement(), "West");
        add(panel2, "Center");
    }

    void initCanvas()
    {
        for(int i = 0; i < pix.length; i++)
            pix[i] = 0;

        source = new MemoryImageSource(widthI, heightI, pix, 0, 401, null);
        source.setAnimated(false);
    }

    public void plot(double ad[])
    {
        holeU[line] = new double[ad.length];
        System.arraycopy(ad, 0, holeU[line], 0, ad.length);
        line++;
        System.arraycopy(ct.toColor(ad), 0, pix, (heightI - line) * 401, ad.length);
        source.newPixels(0, (heightI - line) + 1, widthI, 1);
        try
        {
            Thread.yield();
        }
        catch(Exception _ex) { }
    }

    public final double quotf(double d, double d1)
    {
        if(d == d1)
            return 0.0D;
        else
            return (f(d) - f(d1)) / (d - d1);
    }

    public void run()
    {
        System.out.println("AppletSize" + getSize().width + "x" + getSize().height);
        System.out.println(insets());
        if(anim != null)
            anim.stop();
        rho = getRho();
        red = getRed();
        green = getGreen();
        phases = (int)(4D / (red + green));
        for(int i = 0; i < 200; i++)
            u[i] = rho;

        u[200] = 1.0D;
        for(int j = 201; j < 401; j++)
            u[j] = 0.0D;

        computation1();
        sc.setImage(createImage(source));
        anim.setData(holeU);
        anim.start();
    }

    public synchronized void start()
    {
        if(runner != null)
            stop();
        System.out.println("start is called");
        sc.setImage(null);
        initCanvas();
        if(anim != null)
            anim.stop();
        runner = new Thread(this);
        runner.setPriority(1);
        runner.start();
    }

    public void stop()
    {
        System.out.println("stop is called");
        anim.stop();
        try
        {
            runner.stop();
        }
        catch(Exception _ex) { }
        runner = null;
    }

    public static final double h = 0.005D;
    public static final int N = 200;
    public static final double k = 0.005D;
    public static final double umax = 1D;
    public static final double l = 0D;
    public static final double quot = 1D;
    public static int phases = 3;
    public double rho;
    public double green;
    public double red;
    ColorTransformer ct;
    SolutionCanvas sc;
    Animation anim;
    Canvas diag;
    double u[];
    double holeU[][];
    int pix[];
    int heightI;
    int widthI;
    MemoryImageSource source;
    int line;
    Thread runner;
    ArgumentsPanel args;
    LegendPanel lp;
    //    SolutionObservable so;

}
