using System;
using System.Xml;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;

namespace XELF.Sphynx {

	class DollGimmick : ActorGimmick {
		protected readonly List<GimmickBody> Bodies = new List<GimmickBody>();
		protected readonly List<Visual> Visuals = new List<Visual>();
		readonly List<DollJoint> Joints = new List<DollJoint>();
		readonly List<RevoluteJoint> GrabJoints = new List<RevoluteJoint>();

		public override string ToString() {
			return string.Format("Doll({0}/{1}[deg])", Position, Rotation.ToDegrees());
		}
		public override Gimmick CloneGimmick(bool upgrade) {
			return new DollGimmick(Factories, Position, Unique[upgrade]);
		}
		public DollGimmick(Factories factories, Vector2 position, Unique unique)
			: base(GimmickKind.Doll, factories, position, unique) {

			/*
			if (true) {
				Body = factories.Bodies.Create(BodyKind.Actor, position, this);
				Body.Geometry.CollisionCategories = CollisionCategories.C5;
				Body.Geometry.CollidesWith = CollisionCategories.C2;
			}
			*/

			for (int i = 0; i < DollSettings.Parts.Length; i++) {
				var body = factories.Bodies.Create(BodyKind.Head + i, DollSettings.Parts[i].Position, this);
				body.Geometry.CollisionCategories = CollisionCategories.C5;
				body.Geometry.CollidesWith = CollisionCategories.C2;
				Bodies.Add(body);
				var visual = factories.Visuals.Create(VisualKind.Head + i);
				Visuals.Add(visual);
				SortedVisuals.Add(visual);
			}

			for (int i = 0; i < DollSettings.Joints.Length; i++) {
				var source = DollSettings.Joints[i];
				var joint = new DollJoint(Factories.Bodies.World);
				joint.Body1 = GetBody(source.Left);
				joint.Body2 = GetBody(source.Right);
				// アンカーの位置は、初期位置が基準になっているので、ボディを移動する前に設定する。
				joint.Anchor = source.Position;
				joint.SpringConstant = source.SpringConstant;
				joint.DampingConstant = source.DampingConstant;
				Joints.Add(joint);
			}

			// 指定位置に移動する。
			for (int i = 0; i < DollSettings.Parts.Length; i++) {
				Bodies[i].Position += position;
			}
			SortedVisuals.Sort(CompareDrawOrder);

			GrabJoints.Add(new RevoluteJoint(Factories.Bodies.World));
			GrabJoints.Add(new RevoluteJoint(Factories.Bodies.World));
			GrabJoints[0].Body1 = GetBody(BodyKind.HandL);
			GrabJoints[1].Body1 = GetBody(BodyKind.HandR);
			GrabJoints[0].Body2 = GetBody(BodyKind.HandL);
			GrabJoints[1].Body2 = GetBody(BodyKind.HandR);
			GrabJoints[0].Enabled = false;
			GrabJoints[1].Enabled = false;

			Factories.Bodies.World._.Add(GrabJoints[0]._);
			Factories.Bodies.World._.Add(GrabJoints[1]._);

			Body = Bodies[4];

			GetJoint(DollJointKind.UArmL).TargetAngle = MathHelper.Pi * +0.5f;
			GetJoint(DollJointKind.UArmR).TargetAngle = MathHelper.Pi * -0.5f;

			GetBody(BodyKind.HandL).Geometry._.OnCollision = new FarseerGames.FarseerPhysics.Collisions.CollisionEventHandler(OnHandLCollision);
			GetBody(BodyKind.HandR).Geometry._.OnCollision = new FarseerGames.FarseerPhysics.Collisions.CollisionEventHandler(OnHandRCollision);
			GetBody(BodyKind.FootL).Geometry._.OnCollision = new FarseerGames.FarseerPhysics.Collisions.CollisionEventHandler(OnCollision);
			GetBody(BodyKind.FootR).Geometry._.OnCollision = new FarseerGames.FarseerPhysics.Collisions.CollisionEventHandler(OnCollision);
		}

		public bool OnHandLCollision(FarseerGames.FarseerPhysics.Collisions.Geom geometry1,
			FarseerGames.FarseerPhysics.Collisions.Geom geometry2,
			FarseerGames.FarseerPhysics.Collisions.ContactList contactList) {
			canGrab = true;
			var geometry = (geometry1.Body == GrabJoints[0].Body1._) ? geometry2 : geometry1;
			grabBodyL = geometry.Body.Tag as Body;
			grabPositionL = contactList[0].Position;
			return true;
		}
		public bool OnHandRCollision(FarseerGames.FarseerPhysics.Collisions.Geom geometry1,
			FarseerGames.FarseerPhysics.Collisions.Geom geometry2,
			FarseerGames.FarseerPhysics.Collisions.ContactList contactList) {
			return true;
		}
		public bool OnCollision(FarseerGames.FarseerPhysics.Collisions.Geom geometry1,
			FarseerGames.FarseerPhysics.Collisions.Geom geometry2,
			FarseerGames.FarseerPhysics.Collisions.ContactList contactList) {

			landed = true;

			return true;
		}

		static int CompareDrawOrder(Visual a, Visual b) {
			return Math.Sign(b.Z - a.Z);
		}

		public override Vector2 Position {
			get {
				return base.Position;
			}
			set {
				var translation = value - base.Position;
				//base.Position = value;
				foreach (var body in Bodies) {
					if (body == base.Body) {

					}
					body.Position += translation;
				}
			}
		}

		public virtual float MoveRotation {
			get { return Bodies[2].Rotation; }
			set { Bodies[2].Rotation = value; }
		}
		public virtual void Rotate(float rotation) {
			Bodies[2]._.AngularVelocity += rotation;
		}

		public override bool BodyRegistered {
			get { return base.BodyRegistered; }
			set {
				//base.BodyRegistered = value;

				foreach (var body in Bodies) {
					body.Registered = value;
					/*
					if (value) {
						Factories.Bodies.World.AddBody(body);
					} else {
						Factories.Bodies.World.RemoveBody(body);
					}
					*/
				}

				foreach (var joint in Joints) {
					joint.Registered = value;
				}
				foreach (var joint in GrabJoints) {
					if (value) {
						Factories.Bodies.World._.Add(joint._);
					} else {
						Factories.Bodies.World._.Remove(joint._);
					}
				}
			}
		}

		public override void Save(XmlElement element) {
			base.Save(element);
		}
		public override void Load(XmlElement element) {
			base.Load(element);
		}

		int time;
		bool previousLanded;
		bool landed;
		bool previousCanGrab;
		bool canGrab;
		Vector2 grabPositionL;

		public override bool Enabled {
			get { return base.Enabled; }
			set {
				base.Enabled = value;
				foreach (var body in Bodies) {
					body.Enabled = value;
				}
			}
		}

		Body grabBodyL;
		Body grabBodyR;

		public override void Update() {
			++time;
			previousLanded = landed;
			previousCanGrab = canGrab;
			landed = false;
			canGrab = false;
			base.Update();
		}
		public override void Draw(VectorGraphics graphics) {
			base.Draw(graphics);
		}

		readonly List<Visual> SortedVisuals = new List<Visual>();

		public override void Draw() {
			int i = 0;
			foreach (var visual in Visuals) {
				UpdateVertices(Bodies[i], visual);
				i++;
			}
			foreach (var visual in SortedVisuals) {
				visual.Draw();
			}
			base.Draw();
		}

		public override void Move(Vector2 force) {
			if (force.X != 0 || force.Y != 0) {
				Vector2 point = Bodies[2].Position;
				Vector2 f = force * 10;
				Bodies[2].ApplyForceAtWorldPoint(ref point, ref f);

				Stand(GetBody(BodyKind.Chest));
				Stand(GetBody(BodyKind.ULegL));
				Stand(GetBody(BodyKind.ULegR));
				Walk(force);
			}
		}

		void Stand(GimmickBody body) {
			var rotation = body.Rotation;
			var d = rotation;
			if (rotation < -MathHelper.Pi || rotation > +MathHelper.Pi) {
				d = d - MathHelper.TwoPi;
			}
			d = MathHelper.Clamp(d, -0.8f, +0.8f);
			//rotation -= d;
			//rotation = MathHelper.WrapAngle(rotation);
			//doll.MoveRotation = rotation;

			body._.AngularVelocity += d * -50;

		}

		void ApplyForce(BodyKind kind, Vector2 force) {
			var item = GetBody(kind);
			var position = item.Position;
			item.ApplyForceAtWorldPoint(ref position, ref force);
		}

		void Walk(Vector2 force) {
			var r = (float)Math.Sin(time * (Math.PI * 2 / 60.0 * 2)) * 20;

			ApplyForce(BodyKind.HandL, force * (r * -0.5f));
			ApplyForce(BodyKind.HandR, force * (r * 0.5f));
			ApplyForce(BodyKind.FootL, force * r);
			ApplyForce(BodyKind.FootR, force * -r);
			ApplyForce(BodyKind.LLegL, force * -r);
			ApplyForce(BodyKind.LLegR, force * r);
		}

		public GimmickBody GetBody(BodyKind kind) {
			return Bodies[(int)(kind - BodyKind.Head)];
		}
		public DollJoint GetJoint(DollJointKind kind) {
			return Joints[(int)kind];
		}

		public bool CanJump {
			get { return previousLanded; }
		}
		public void Jump() {
			ApplyForce(BodyKind.Hip, new Vector2(0, 20000));
		}
		internal void Crouch(bool enabled) {
			if (enabled) {
				//ApplyForce(BodyKind.Hip, new Vector2(0, -1000));
				Stand(GetBody(BodyKind.Chest));
				//Stand(GetBody(BodyKind.ULegL));
				//Stand(GetBody(BodyKind.ULegR));
				//ApplyForce(BodyKind.LLegL, new Vector2(0, +5000));
				//ApplyForce(BodyKind.LLegR, new Vector2(0, -5000));
				//ApplyForce(BodyKind.FootL, new Vector2(0, -5000));
				//ApplyForce(BodyKind.FootR, new Vector2(0, +5000));
				GetJoint(DollJointKind.ULegL).TargetAngle = MathHelper.Pi * -0.25f;
				GetJoint(DollJointKind.ULegR).TargetAngle = MathHelper.Pi * +0.25f;
				GetJoint(DollJointKind.LLegL).TargetAngle = MathHelper.Pi * +0.50f;
				GetJoint(DollJointKind.LLegR).TargetAngle = MathHelper.Pi * -0.50f;
			} else {
				GetJoint(DollJointKind.ULegL).TargetAngle = MathHelper.Pi * -0.00f;
				GetJoint(DollJointKind.ULegR).TargetAngle = MathHelper.Pi * +0.00f;
				GetJoint(DollJointKind.LLegL).TargetAngle = MathHelper.Pi * +0.00f;
				GetJoint(DollJointKind.LLegR).TargetAngle = MathHelper.Pi * -0.00f;
			}
		}

		bool IsGrabbing {
			get { return GrabJoints[0].Enabled; }
		}

		public virtual void Grab(bool enabled) {
			if (enabled && !IsGrabbing && previousCanGrab) {
				GrabJoints[0].Enabled = true;
				GrabJoints[0].Body2 = grabBodyL;
				//GrabJoints[0].Anchor = grabPositionL;
				GrabJoints[0].Anchor = GrabJoints[0].Body1.Position;
			} else if (!enabled && IsGrabbing) {
				GrabJoints[0].Enabled = false;
			}
			//GrabJoints[1].Enabled = enabled & canGrab;
			if (enabled) {
				GetJoint(DollJointKind.UArmL).TargetAngle = MathHelper.Pi * -0.5f;
				GetJoint(DollJointKind.UArmR).TargetAngle = MathHelper.Pi * +0.5f;
			} else {
				GetJoint(DollJointKind.UArmL).TargetAngle = MathHelper.Pi * +0.35f;
				GetJoint(DollJointKind.UArmR).TargetAngle = MathHelper.Pi * -0.35f;
			}
		}
	}

}
