
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace XELF.Sphynx {

	interface Tool {
		bool Enabled { get; set; }
		void Update();
	}

	class DefaultTool : Tool {
		readonly StageEditor editor;
		public DefaultTool(StageEditor editor) {
			this.editor = editor;
		}
		public bool Enabled { get; set; }

		public void Update() {
			editor.HandleBasicToolInput();
		}
		public override string ToString() {
			return "Default Tool";
		}
	}

	class CircuitTool : Tool {
		readonly StageEditor editor;
		public bool Enabled { get; set; }
		public override string ToString() {
			return "Circuit Tool";
		}
		public CircuitTool(StageEditor editor) {
			this.editor = editor;
		}
		public void Update() {
			//TODO:
		}
	}

	class MoveTool : Tool {
		readonly StageEditor editor;
		public bool Enabled { get; set; }
		public override string ToString() {
			return "Move Tool";
		}
		public MoveTool(StageEditor editor) {
			this.editor = editor;
		}

		void UpdateTargetAnchor() {
			var bodies = editor.Stage.Factories.Bodies.World.CollideAllBodies(
				editor.WorldPosition2From(editor.Input.Location));
			var target = bodies.Find(delegate(Body body) {
				return (body.Geometry.CollisionCategories &
					(CollisionCategories.C1
					| CollisionCategories.C2
					| CollisionCategories.C3
					| CollisionCategories.C4)) != 0;
			});
			var anchor = editor.SelectedItem as AnchorGimmick;

			var targetGimmick = (target == null) ? null : target.Tag as Gimmick;
			{//if (anchor.Target != targetGimmick) {
				editor.SelectedItem.Position = editor.WorldPosition2From(editor.Input.Location);
				anchor.SetAnchor(targetGimmick, anchor.Position);
			}

		}
		void UpdateTargetJunction() {
			var bodies = editor.Stage.Factories.Bodies.World.CollideAllBodies(
				editor.WorldPosition2From(editor.Input.Location));
			var target = bodies.Find(delegate(Body body) {
				return (body.Geometry.CollisionCategories &
					(CollisionCategories.C1
					| CollisionCategories.C2
					| CollisionCategories.C3
					| CollisionCategories.C4
					| CollisionCategories.C5)) != 0;
			});
			var anchor = editor.SelectedItem as AnchorGimmick;

			var targetGimmick = (target == null) ? null : target.Tag as Gimmick;
			{//if (anchor.Target != targetGimmick) {
				editor.SelectedItem.Position = editor.WorldPosition2From(editor.Input.Location);
				anchor.SetAnchor(targetGimmick, anchor.Position);
			}

		}

		public void Update() {
			if (editor.SelectedItem == null) {
				editor.BeginSelect();
			} else {
				editor.UpdateHoveredItem();
				if (editor.Keyboard.IsPress(Keys.Escape)) {
					editor.SelectedItem = null;
				} else if (editor.Keyboard.IsPress(Keys.Space)) {
					switch (editor.SelectedItem.Kind) {
					case GimmickKind.Anchor:
						UpdateTargetAnchor(); break;
					case GimmickKind.Junction:
						UpdateTargetJunction(); break;
					}
					editor.RecordEntire();
					editor.SelectedItem = null;
					//editor.World.Update(-1e-5f);
				} else {
					editor.SelectedItem.Position = editor.WorldPosition2From(editor.Input.Location);
					editor.HandleBasic0ToolInput();
					editor.HandleSceneToolInput();
				}
			}

		}
	}

	class SelectTool : Tool {
		readonly StageEditor editor;
		public bool Enabled { get; set; }

		public SelectTool(StageEditor editor) {
			this.editor = editor;
		}
		public void Update() {
			editor.UpdateHoveredItem();
			Gimmick gimmick = editor.Pick(editor.Input.Location);
			if (editor.Keyboard.IsPress(Keys.Escape)) {
				editor.EndSelect(null);
			} else if (editor.Keyboard.IsPress(Keys.Space)) {
				editor.EndSelect(gimmick);
			} else {
				editor.HandleBasic0ToolInput();
				editor.HandleSceneToolInput();
			}
		}
		public override string ToString() {
			return "Select Tool";
		}
	}

	/// <summary>
	/// 形状編集
	/// </summary>
	class ShapeTool : Tool {
		readonly StageEditor editor;
		readonly KeyboardInput keyboard;
		readonly MouseInput input;

		public bool Enabled { get; set; }
		public Geometry Geometry { get; set; }

		public int Index;

		public override string ToString() {
			return "Shape Tool";
		}
		public ShapeTool(StageEditor editor, KeyboardInput keyboard, MouseInput input) {
			this.editor = editor;
			this.keyboard = keyboard;
			this.input = input;
		}

		public void Update() {
			if (editor.SelectedItem == null) {
				editor.BeginSelect();
			} else {
				if (keyboard.IsPress(Keys.Space)) {
					Click(input.Location);
				} else if (keyboard.IsPress(Keys.T)) {
					if (++editor.SelectedItem.Visual.Mapping >= Mapping.Max)
						editor.SelectedItem.Visual.Mapping = Mapping.Wrap;
					editor.RecordEntire();
					UpdateMesh(editor.SelectedItem);
				} else if (keyboard.IsPress(Keys.Z)) {
					RemovePoint(input.Location);
				} else if (keyboard.IsPress(Keys.Escape)) {
					editor.PutSelectedItem();
					editor.SelectedItem = null;
				} else {
					editor.HandleBasic0ToolInput();
				}
			}
		}

		public void Click(Vector2 point) {
			EditPoint(point);
		}
		void EditPoint(Vector2 location) {
			if (MovePoint(location))
				return;
			if (AddPoint(location))
				return;
		}

		bool AddPoint(Vector2 location) {
			var gimmick = editor.SelectedItem;
			Body body = gimmick.Body;
			Geometry geometry = body.Geometry;

			var shape = geometry._.LocalVertices;
			Vector2 localPoint;
			var position = editor.WorldPosition2From(location);
			geometry._.TransformToLocalCoordinates(ref position, out localPoint);
			int index = shape.NearestEdgeMidIndexOf(localPoint);

			if (index < 0)
				shape.Add(localPoint);
			else
				shape.Insert(shape.NextIndex(index), localPoint);
			//geometry._.SetVertices(shape);
			body.RebuildGeometry(new ClosedShape(shape));
			editor.RecordEntire();

			UpdateMesh(gimmick);

			return true;
		}

		void UpdateMesh(Gimmick gimmick) {
			Body body = gimmick.Body;
			Geometry geometry = body.Geometry;

			var shape = geometry._.LocalVertices;

			var mesh = gimmick.Visual.BeginEdit();
			mesh.BuildFrom(shape);
			gimmick.Visual.EndEdit();
		}

		bool MovePoint(Vector2 location) {
			var gimmick = editor.SelectedItem;
			Body body = gimmick.Body;
			Geometry geometry = body.Geometry;

			var shape = geometry._.LocalVertices;
			Vector2 localPoint;
			var position = editor.WorldPosition2From(location);
			geometry._.TransformToLocalCoordinates(ref position, out localPoint);
			float distanceSquared;
			int index = shape.NearestIndexOf(localPoint, out distanceSquared);
			if (distanceSquared <= 1) {
				shape[index] = localPoint;
				body.RebuildGeometry(new ClosedShape(shape));
				editor.RecordEntire();
				UpdateMesh(gimmick);
				return true;
			}
			return false;
		}
		bool RemovePoint(Vector2 location) {
			var gimmick = editor.SelectedItem;
			Body body = gimmick.Body;
			Geometry geometry = body.Geometry;
			var shape = geometry._.LocalVertices;
			if (shape.Count <= 3)
				return false;
			Vector2 localPoint;
			var position = editor.WorldPosition2From(location);
			geometry._.TransformToLocalCoordinates(ref position, out localPoint);
			float distanceSquared;
			int index = shape.NearestIndexOf(localPoint, out distanceSquared);
			if (distanceSquared <= 1) {
				shape.RemoveAt(index);
				body.RebuildGeometry(new ClosedShape(shape));
				editor.RecordEntire();
				UpdateMesh(gimmick);
				return true;
			}
			return false;
		}
	}

	enum ToolKind {
		GimmickAdd,
		Selector,
	}

}
