/*
 * (c) 2015 CS448J Lecture, Stanford University.
 */

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;

/**
 * Simulation of a double pendulum with simple forward Euler integration.
 * 
* @author Dominik L. Michels <michels@cs.stanford.edu>
 */
public class DoublePendulum extends Applet implements ActionListener, Runnable {

    double theta1S = 1.6, dtheta1S = 0;
    double theta2S = 1.8, dtheta2S = 0;
    double theta1, dtheta1, ddtheta1;
    double theta2, dtheta2, ddtheta2;
    double theta1a, dtheta1a, ddtheta1a, theta2a, dtheta2a, ddtheta2a;
    double x1, y1, x2, y2;
    int x1c, y1c, x2c, y2c;
    double dt = 0.005, t = -dt;
    double g = 9.81;
    double l1, l2, l;
    double m1, m2, m;

    public void dataUpdate() {
        t = t + dt;

        ddtheta1 = (-1 / (l1 * (m - Math.pow(Math.cos(theta1 - theta2), 2)))) * (g * (m * Math.sin(theta1) - Math.sin(theta2) * Math.cos(theta1 - theta2)) + Math.sin(theta1 - theta2) * (l1 * Math.pow(dtheta1, 2) * Math.cos(theta1 - theta2) + l2 * Math.pow(dtheta2, 2)));

        ddtheta2 = -l * ddtheta1 * Math.cos(theta1 - theta2) + l * Math.pow(dtheta1, 2) * Math.sin(theta1 - theta2) - (g / l2) * Math.sin(theta2);

        dtheta1a = dtheta1;
        dtheta2a = dtheta2;
        theta1a = theta1;
        theta2a = theta2;

        dtheta1 = dtheta1a + ddtheta1 * dt;
        dtheta2 = dtheta2a + ddtheta2 * dt;
        theta1 = theta1a + dtheta1 * dt;
        theta2 = theta2a + dtheta2 * dt;

        x1 = l1 * Math.sin(theta1);
        y1 = l1 * Math.cos(theta1);

        x2 = x1 + l2 * Math.sin(theta2);
        y2 = y1 + l2 * Math.cos(theta2);

        x1c = (int) (100 * x1) + 450;
        y1c = (int) (100 * y1) + 300;

        x2c = (int) (100 * x2) + 450;
        y2c = (int) (100 * y2) + 300;
    }

    public void paint(Graphics g) {
        g.setColor(Color.black);

        text("t = " + String.valueOf(Math.round(t * 10.) / 10.) + "s", 20, 20, 12, g);

        g.fillOval(446, 296, 8, 8);
        g.drawLine(450, 300, x1c, y1c);
        g.drawLine(x1c, y1c, x2c, y2c);

        g.setColor(Color.red);
        g.fillOval(x1c - (int) (12 * m1), y1c - (int) (12 * m1),
                (int) (24 * m1), (int) (24 * m1));

        g.setColor(Color.blue);
        g.fillOval(x2c - (int) (12 * m2), y2c - (int) (12 * m2),
                (int) (24 * m2), (int) (24 * m2));
    }

    void text(String txt, int x, int y, int s, Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        Font f = new Font("Sans Serif", Font.LAYOUT_LEFT_TO_RIGHT, s);
        FontMetrics fm = getFontMetrics(f);
        LineMetrics lm = fm.getLineMetrics(txt, g);
        g2d.setFont(f);
        g2d.drawString(txt, x, y);
    }
    private Image bufferingImage;
    private Graphics bufferingGraphics;

    public void update(Graphics g) {
        int width = 900, height = 600;
        if (bufferingImage == null) {
            bufferingImage = createImage(width, height);
            bufferingGraphics = bufferingImage.getGraphics();
        }
        bufferingGraphics.setColor(getBackground());
        bufferingGraphics.fillRect(0, 0, width, height);
        paint(bufferingGraphics);
        g.drawImage(bufferingImage, 0, 0, this);
    }
    Thread runner;
    boolean started = false;

    /**
     * Runner-Start.
     *
     */
    public void startSim() {
        runner = new Thread(this);
        runner.start();
    }

    public void pauseSim() {
        runner.stop();
    }

    public void stopSim() {
        try {
            runner.stop();
        } catch (Exception oops) {
        }
        loadInput();
        t = -dt;
        dataUpdate();
        repaint();
    }

    public void run() {
        while (true) {
            repaint();
            dataUpdate();
            try {
                Thread.sleep((int) (dt * 900));
            } catch (InterruptedException e) {
            }
        }

    }

    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == btnSP) {
            if (started) {
                pauseSim();
            } else {
                startSim();
            }
            started = !started;
        } else if (e.getSource() == btnST) {
            stopSim();
            if (started) {
                started = !started;
            }
        }
    }
    Button btnSP, btnST;
    TextField txtM1 = new TextField("1.0");
    TextField txtM2 = new TextField("1.0");
    TextField txtL1 = new TextField("1.0");
    TextField txtL2 = new TextField("1.0");
    TextField txtTheta1 = new TextField("1.6");
    TextField txtTheta2 = new TextField("1.8");
    TextField txtDTheta1 = new TextField("0.0");
    TextField txtDTheta2 = new TextField("0.0");

    public void init() {

        setLayout(new BorderLayout());
        Panel myPanel = new Panel();
        myPanel.setLayout(new GridLayout(2, 9));

        Label laM1 = new Label("m1 =");
        Label laM2 = new Label("m2 =");
        Label laL1 = new Label("l1 =");
        Label laL2 = new Label("l2 =");
        Label laTheta1 = new Label("θ1(0) =");
        Label laTheta2 = new Label("θ2(0) =");
        Label laDTheta1 = new Label("dθ1(0) =");
        Label laDTheta2 = new Label("dθ2(0) =");

        btnSP = new Button("Run/Stop");
        btnST = new Button("Initialize");

        add(myPanel, BorderLayout.SOUTH);

        myPanel.add(laM1);
        myPanel.add(txtM1);
        myPanel.add(laL1);
        myPanel.add(txtL1);
        myPanel.add(laTheta1);
        myPanel.add(txtTheta1);
        myPanel.add(laDTheta1);
        myPanel.add(txtDTheta1);
        myPanel.add(btnSP);
        myPanel.add(laM2);
        myPanel.add(txtM2);
        myPanel.add(laL2);
        myPanel.add(txtL2);
        myPanel.add(laTheta2);
        myPanel.add(txtTheta2);
        myPanel.add(laDTheta2);
        myPanel.add(txtDTheta2);
        myPanel.add(btnST);

        btnSP.addActionListener(this);
        btnST.addActionListener(this);

        loadInput();
        dataUpdate();
    }

    public void loadInput() {
        m1 = new Double(txtM1.getText());
        m2 = new Double(txtM2.getText());
        l1 = new Double(txtL1.getText());
        l2 = new Double(txtL2.getText());
        theta1 = new Double(txtTheta1.getText());
        theta2 = new Double(txtTheta2.getText());
        dtheta1 = new Double(txtDTheta1.getText());
        dtheta2 = new Double(txtDTheta2.getText());
        l = l1 / l2;
        m = (m1 + m2) / m2;
    }
}