/*
 * Author: Hannu Kankaanpää
 * Email: hkankaan@cc.hut.fi
 */

import springsystem.*;

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;

/**
 * A test applet for the spring system
 */
public class SpringApplet extends Applet implements MouseListener, MouseMotionListener, KeyListener, Runnable {
	private int width, height;
	private Image backbuffer;
	private Graphics graphics;
	private SpringManager springManager;
	private Vector2d mousePosition = new Vector2d(0, 0);
	private SpringNode draggedNode;
	private SpringNode newDraggedNode;
	private boolean drawInternals = false;

	public void init() {
		initGraphics();
		initSprings();
		addMouseListener(this);
		addMouseMotionListener(this);
		addKeyListener(this);
		new Thread(this).start();
	}

	private void initSprings() {
		springManager = new SpringManager(width, height);

		byte[][] data = {{0, 0, 1, 1, 1, 0, 0},
		                 {1, 1, 1, 1, 1, 1, 1},
		                 {1, 1, 1, 1, 1, 1, 1},
		                 {1, 1, 1, 1, 1, 1, 1},
		                 {0, 0, 1, 1, 1, 0, 0},
		                 {0, 0, 1, 1, 1, 0, 0}};

		springManager.createStructureFromData(data, 30, 30, 50, 80, 1);

		byte[][] data2 = {{0, 0, 2, 2},
		                  {0, 0, 1, 1},
		                  {0, 0, 1, 1},
		                  {0, 0, 1, 1},
		                  {0, 0, 1, 1},
		                  {0, 0, 1, 1},
		                  {1, 1, 1, 1},
		                  {1, 1, 1, 1}};

		springManager.createStructureFromData(data2, 50, 60, 320, 0, 1);

		byte[][] data3 = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}};
		springManager.createStructureFromData(data3, 20, 20, 5, 400, 1);

		springManager.createCircle(200, 400, 60, 12, 0.5);
	}

	private void initGraphics() {
		width = getSize().width;
		height = getSize().height;
		backbuffer = createImage(width, height);
		graphics = backbuffer.getGraphics();
		clearScreen();
	}

	private void clearScreen() {
		graphics.setColor(Color.black);
		graphics.fillRect(0, 0, width, height);
	}

	public void run() {
		long oldTime = System.currentTimeMillis();
		while (true) {
			double dt = (double) (System.currentTimeMillis() - oldTime) / 1000.0;
			if (dt > 0.05) dt = 0.05;
			oldTime = System.currentTimeMillis();
			setDraggedNode(newDraggedNode);
			calculatePhysics(dt);
			drawScene();

			try {
				Thread.sleep(20);
			} catch (InterruptedException e) {
			}
		}
	}

	private void calculatePhysics(double dt) {
		if (draggedNode != null)
			addForceToDraggedNode();

		int ticksPerFrame = 8;
		for (int i = 0; i < ticksPerFrame; i++) {
			if (i % 2 == 0)
				springManager.checkCollisions();
			springManager.moveSystem(dt / ticksPerFrame);
		}
	}

	private void drawScene() {
		clearScreen();
		drawSprings();
		if (draggedNode != null)
			drawDraggedNode();
		repaint();
	}

	private void addForceToDraggedNode() {
		//force = (mousePosition - draggedNode.position) * constant
		Vector2d force = (Vector2d) mousePosition.clone();
		force.substract(draggedNode.getPosition());
		force.multiplyBy(50);
		draggedNode.setExternalForce(force);
	}

	private void drawDraggedNode() {
		graphics.setColor(Color.white);
		graphics.drawArc((int) draggedNode.getPosition().x - 8, (int) draggedNode.getPosition().y - 8,
		                 16, 16, 0, 360);
	}

	private void drawSprings() {
		if (drawInternals) {
			graphics.setColor(Color.cyan);
			drawSpringLines(springManager.getSprings());
		}
		graphics.setColor(Color.white);
		drawSpringLines(springManager.getEdgeSprings());
	}

	private void drawSpringLines(Vector springs) {
		for (int i = 0; i < springs.size(); i++) {
			Spring spring = (Spring) springs.elementAt(i);
			Vector2d pos1 = spring.getStartPosition();
			Vector2d pos2 = spring.getEndPosition();
			graphics.drawLine((int) pos1.x, (int) pos1.y,
			                  (int) pos2.x, (int) pos2.y);
		}
	}

	public void mouseDragged(MouseEvent e) {
		mousePosition.x = e.getX();
		mousePosition.y = e.getY();
	}

	public void mouseMoved(MouseEvent e) {
	}

	public void mouseClicked(MouseEvent e) {
	}

	public void mousePressed(MouseEvent e) {
		mousePosition.x = e.getX();
		mousePosition.y = e.getY();
		newDraggedNode = springManager.findClosestSpringNode(mousePosition);
	}

	public void mouseReleased(MouseEvent e) {
		newDraggedNode = null;
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseExited(MouseEvent e) {
	}

	public void update(Graphics g) {
		g.drawImage(backbuffer, 0, 0, this);
	}

	public void paint(Graphics g) {
		update(g);
	}

	public void keyTyped(KeyEvent e) {
	}

	public void keyPressed(KeyEvent e) {
		switch (e.getKeyChar()) {
			case '1':
				drawInternals = true;
				break;
			case '2':
				drawInternals = false;
				break;
		}
	}

	public void keyReleased(KeyEvent e) {
	}

	public void setDraggedNode(SpringNode draggedNode) {
		if (this.draggedNode != null) {
			this.draggedNode.setExternalForce(new Vector2d(0, 0));
		}
		this.draggedNode = draggedNode;
	}
}

