package jp.ac.kyutech.mns.ist.play;

import java.awt.Color;
import java.awt.geom.Point2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import jp.ac.kyutech.mns.ist.Sheet;
import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.event.PBasicInputEventHandler;
import edu.umd.cs.piccolo.event.PDragSequenceEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolo.util.PBounds;
import edu.umd.cs.piccolo.util.PDimension;

/**
 * Cameraの下に表示される，タイムスライダ
 * @author miuramo
 *
 */
public class TimePlaySlider extends PPath implements Runnable {
	private static final long serialVersionUID = 636364263149924920L;

	PCamera camera;

	PPath knob;

	PPath exit;

	float min, max, value, preValue;

	float step = 1;

//	Note note;
	Sheet note;

	SSManager ssmanager;

	PPath pen;

	boolean playing, pausing = false;

	int playinterval = 30;

	Thread playmonthread;

	Color stateColor = Color.green;
	
	float exitbuttonsize = 20.0f; // キャンセルボタンのサイズ

	public TimePlaySlider(Sheet n, SSManager ssman, PCamera cam) {
		ssmanager = ssman;
		note = n;
		min = 0;
		max = ssmanager.end;
		value = 0;
		camera = cam;
		setPaint(Color.blue);
		setPathToRectangle(0, 0, 200, 10);
		setTransparency(0.5f);
		addAttribute("NoFlowMenu", true);

		knob = PPath.createEllipse(0, 0, 10, 10);
		knob.setPaint(Color.red);
		setTransparency(0.5f);
		addChild(knob);
		knob.addAttribute("NoFlowMenu", true);
		knob.addInputEventListener(new TimePlaySlider_KnobListener(knob, this, camera));
		addInputEventListener(new TimePlaySlider_OnSliderListener(this, camera));

		cam.addPropertyChangeListener(new TimePlaySlider_LayoutListener(this));

		exit = PPath.createRectangle(0, 0, exitbuttonsize, exitbuttonsize); // キャンセルボタンの外側はただの四角
		exit.moveTo(2, 2); // 内側の×を書いていく
		exit.lineTo(exitbuttonsize - 2, exitbuttonsize - 2);
		exit.moveTo(2, exitbuttonsize - 2);
		exit.lineTo(exitbuttonsize - 2, 2);
		exit.setTransparency(1.0f);
		// exit.setPaint(Color.white);
		exit.setStrokePaint(Color.black);
		addChild(exit);

		exit.addInputEventListener(new PBasicInputEventHandler() {
			public void mouseClicked(PInputEvent e) {
				dispose();
			}
		});

		pen = PPath.createPolyline(new float[] { 0, 20, 50, 40, 10, 0 },
				new float[] { 0, 10, 40, 50, 20, 0 });
		pen.setPaint(Color.blue);
		pen.setTransparency(0.7f);
		pen.rotate(-Math.PI / 2);
		pen.addInputEventListener(new PBasicInputEventHandler() {
			public void mouseClicked(PInputEvent e) {
				System.out.println("Pen click");
			}
		});

		cam.addChild(this);
		start(1);
	}

	/**
	 * スライダ自体を終了する．
	 * 
	 */
	public void dispose() {
		stop();
		pen.removeFromParent();
		setPlayPos(ssmanager.end, false);
		removeFromParent();
		System.gc();
	}

	// SnailViewer のサイズが変化したら，
	// TimePlaySlider_LayoutListener が発火
	// fitToBottom()
	public void fitToBottom() {
		// Boundsが変化したら！！！
		PBounds b = camera.getBoundsReference();
		resize((float) b.width);
		setOffset(10, b.height - getBounds().height - 2);
		updateXPos();

		exit.setOffset(0, -exitbuttonsize);

		// preBounds = b;
	}

	public void resize(float width) {
		setPathToRectangle(0, 0, width - 20, 10);
	}

	// ノブが動かされたら
	// SliderKnobListenr が発火，Knob位置が動かされる
	// boundaryCheck();で位置越え直す
	// updateValue();でValueを更新，checkValueChangeで表示位置を更新
	public void boundaryCheck() {
		Point2D p = knob.getOffset();
		if (p.getX() <= 0)
			knob.setOffset(0, 0);
		if (getWidth() - knob.getWidth() < p.getX())
			knob.setOffset(getWidth() - knob.getWidth(), 0);
	}

	public void setKnobPosition(double px) {
		knob.setOffset((float) px, 0);
		boundaryCheck();
	}

	public void setMinimum(int _min) {
		min = _min;
	}

	public void setManimum(int _max) {
		max = _max;
	}

	public float getValue() {
		return value;
	}

	// Valueがセットされたら
	// Valueを変更，ノブ位置を修正
	// checkValueChangeで表示位置を更新
	public void setValue(float p) {
		value = p;
		double xpos = (getWidth() * value) / (max - min);
		setKnobPosition(xpos);
		checkValueChange();
	}

	public void updateValue() { // knob to val
		double xpos = knob.getOffset().getX();
		double xwidth = (getWidth() - knob.getWidth());
		value = (int) ((max - min) * xpos / xwidth) + min;
		// System.out.println(value);
		checkValueChange(); // 変更されていたら，SSマネージャに通知
	}

	public void updateXPos() { // val to xpos
		setValue(value);
	}

	public void checkValueChange() {
		if (value != preValue) {
			setPlayPos(value, true);
		}
		preValue = value;
	}

	public void setPlayPos(float fps, boolean fromSlider) {
		ssmanager.setPlayPosition(fps);
		ssmanager.setPenOffset(pen);
		if (!fromSlider)
			setValue(fps);
		// note.ink.repaint();
		note.repaint();
	}

	public void run() {
		while (true) {
			if (pausing) {
				synchronized (this) {
					try {
						wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			setPlayPos(ssmanager.pos + (int) step, false);
			if (ssmanager.pos >= ssmanager.end - 1)
				break;
			if (ssmanager.pos <= 0)
				break;
			try {
				Thread.sleep(playinterval);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if (!playing)
				break;
		}
		stop();
	}

	public void start(float _step) {
		if (playing)
			return;
		note.addChild(pen);
		step = _step;
		if (step == 1 && ssmanager.pos == ssmanager.end)
			setPlayPos(0, false);
		if (step == -1 && ssmanager.pos == 0)
			setPlayPos(ssmanager.end - 1, false);
		if (playmonthread == null) {
			playmonthread = new Thread(this);
			playmonthread.start();
			playing = true;
			stateColor = Color.green;
			knob.setPaint(stateColor);
			// if (step == 1) playB.setText("pause"); else
			// playbackB.setText("pause");
		}
	}

	public void stop() {
		playing = false;
		if (playmonthread != null)
			playmonthread = null;
		stateColor = Color.red;
		knob.setPaint(stateColor);
	}

	public void pause() {
		pausing = true;
		knob.setPaint(Color.orange);
	}

	public synchronized void resume() {
		pausing = false;
		notify();
		knob.setPaint(stateColor);
	}

	public void pressOnBar(Point2D p, int button) {
		if (button == 1) {
			setKnobPosition(p.getX() - 10 - knob.getWidth() / 2);
			updateValue();
		} else if (button == 3) {
			Point2D p2 = knob.getOffset();
			if (p.getX() < p2.getX() + 10) {
				if (step > 0)
					step = 0;
				else
					step -= 1.0;
				// ノブの左
				if (step == 0)
					stop();
				else
					start(step);
			} else if (p2.getX() + 10 + knob.getWidth() < p.getX()) {
				// ノブの右
				if (step < 0)
					step = 0;
				else
					step += 1.0;
				if (step == 0)
					stop();
				else
					start(step);
			}
		}
		// System.out.println(p);
		// System.out.println(p2);
		// System.out.println(button);
	}
}

/**
 * タイムスライダが常にカメラの下に位置するためのPropertyChangeListener
 * @author miuramo
 *
 */
class TimePlaySlider_LayoutListener implements PropertyChangeListener {
	TimePlaySlider tpc;

	TimePlaySlider_LayoutListener(TimePlaySlider t) {
		tpc = t;
	}

	public void propertyChange(PropertyChangeEvent evt) {
		if (evt.getSource() == tpc.camera) {
			if (evt.getPropertyName().equals("bounds")) {
				tpc.fitToBottom();
			}
		}
	}
}

/**
 * スライダのノブ操作を処理するリスナ
 * @author miuramo
 *
 */
class TimePlaySlider_KnobListener extends PDragSequenceEventHandler {
	PPath target;

	TimePlaySlider controller;

	PCamera camera;

	// boolean noPress;
	TimePlaySlider_KnobListener(PPath t, TimePlaySlider cont, PCamera cam) {
		target = t;
		controller = cont;
		camera = cam;
	}

	public void startDrag(PInputEvent e) {
		super.startDrag(e);

		// System.out.println("Knob DragStart");
		controller.pause();
		// noPress = false;
		 e.setHandled(true);
	}

	public void drag(PInputEvent e) {
		super.drag(e);
		// Point2D camp = e.getPositionRelativeTo(camera);
		PDimension d = e.getCanvasDelta();
		// e.getTopCamera().localToView(d);
		d.height = 0;
		PDimension gDist = new PDimension();
		gDist.setSize(d);
		// target.getParent().globalToLocal(gDist);
		target.offset(gDist.getWidth(), gDist.getHeight());
		controller.boundaryCheck();
		controller.updateValue();
		// System.out.println("Knob Drag");
		 e.setHandled(true);
	}

	public void endDrag(PInputEvent e) {
		super.endDrag(e);
		controller.resume();
		// System.out.println("Knob DragEnd");
		 e.setHandled(true);
	}

	public void mouseEntered(PInputEvent e) {
		// noPress =true;
		// e.setHandled(true);
	}

	public void mouseExited(PInputEvent e) {
		// if (noPress) controller.resume();
		// e.setHandled(true);
	}

	public void mouseClicked(PInputEvent e) {
		if (e.getClickCount() == 2) {
			if (!controller.playing)
				controller.start(1);
			else
				controller.stop();
		}
	}
}

/**
 * スライダ上でのマウス操作イベントを処理するリスナ
 * @author miuramo
 *
 */
class TimePlaySlider_OnSliderListener extends PDragSequenceEventHandler {
	TimePlaySlider controller;

	PCamera camera;

	TimePlaySlider_OnSliderListener(TimePlaySlider cont, PCamera cam) {
		controller = cont;
		camera = cam;
	}

	public void mousePressed(PInputEvent e) {
		super.mousePressed(e);
		Point2D p = e.getPositionRelativeTo(camera);
		controller.pressOnBar(p, e.getButton());
	}

	public void drag(PInputEvent e) {
		super.drag(e);
		Point2D p = e.getPositionRelativeTo(camera);
		if (e.getButton() == 1)
			controller.pressOnBar(p, 1);
	}
}