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

namespace XELF.Sphynx {

	class PuffGimmick : Gimmick {

		public override bool BodyRegistered {
			get { return base.BodyRegistered; }
			set {
				//base.BodyRegistered = value;
				foreach (var item in bodies) item.Registered = value;
				foreach (var item in springs) item.Registered = value;
			}
		}

		public override void Dispose() {
			foreach (var item in bodies) item.Dispose();
			foreach (var item in springs) item.Dispose();
			base.Dispose();
		}

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

			bodies.Add(CreateBody(Vector2.Zero));

			for (int i = 0; i < Corners; i++) {
				Vector2 point = Vector2.Zero;
				point.X = (float)Math.Sin(i * MathHelper.TwoPi / Corners);
				point.Y = (float)Math.Cos(i * MathHelper.TwoPi / Corners);
				bodies.Add(CreateBody(point));
			}

			Body = bodies[0];

			// 中心と周囲をつなぐバネ
			for (int i = 0; i < Corners; i++) {
				Vector2 point = Vector2.Zero;
				point.X = (float)Math.Sin(i * MathHelper.TwoPi / Corners);
				point.Y = (float)Math.Cos(i * MathHelper.TwoPi / Corners);
				var spring = new LinearSpring(Factories.Bodies.World);
				springs.Add(spring);
				spring.Body1 = Body;
				spring.Body2 = bodies[i + 1];
				spring.AttachPoint2 = point;
				spring.RestLength = Vector2.Distance(spring.Position1, spring.Position2);
				spring.SpringConstant = 200;
				spring.DampingConstant = 10;
			}
			// 周囲の隣同士の頂点をつなぐバネ
			for (int i = 0; i < Corners; i++) {
				int j = ((i + 1) % Corners);
				Vector2 point = Vector2.Zero;
				point.X = (float)Math.Sin(i * MathHelper.TwoPi / Corners);
				point.Y = (float)Math.Cos(i * MathHelper.TwoPi / Corners);
				Vector2 point2 = Vector2.Zero;
				point2.X = (float)Math.Sin(j * MathHelper.TwoPi / Corners);
				point2.Y = (float)Math.Cos(j * MathHelper.TwoPi / Corners);
				var spring = new LinearSpring(Factories.Bodies.World);
				springs.Add(spring);
				spring.Body1 = bodies[i + 1];
				spring.AttachPoint1 = point;
				spring.Body2 = bodies[j + 1];
				spring.AttachPoint2 = point2;
				spring.RestLength = Vector2.Distance(spring.Position1, spring.Position2);
				//spring.AttachPoint2 = spring.Body2.Position;
				spring.SpringConstant = 200;
				spring.DampingConstant = 10;
			}

			// 周囲の隣同士の頂点をつなぐバネ
			int step = (Corners / 3) + 1;
			for (int i = 0; i < Corners; i++) {
				var j = ((i + step) % Corners);
				var spring = new LinearSpring(Factories.Bodies.World);
				springs.Add(spring);
				spring.Body1 = bodies[i + 1];
				//spring.AttachPoint1 = spring.Body1.Position;
				spring.Body2 = bodies[j + 1];
				spring.RestLength = Vector2.Distance(spring.Position1, spring.Position2);
				//spring.AttachPoint2 = spring.Body2.Position;
				spring.SpringConstant = 200;
				spring.DampingConstant = 10;
			}

			for (int i = 0; i < bodies.Count; i++) {
				bodies[i].Position += position;
			}

			Visual = Factories.Visuals.Create(VisualKind.Puff);
			//UpdateMesh();
			restArea = Body.Geometry._.WorldVertices.GetArea();
		}

		float restArea;

		public override void Draw() {
			{
				int i = 0;
				var point = springs[i].Position1;
				var vertex = Visual.Vertices[i];
				vertex.Position.X = point.X;
				vertex.Position.Y = point.Y;
				vertex.Position.Z = Visual.Z;
				Visual.Vertices[i] = vertex;
			}
			for (int i = 1; i < bodies.Count; i++) {
				var point = springs[i - 1].Position2;
				var vertex = Visual.Vertices[i];
				vertex.Position.X = point.X;
				vertex.Position.Y = point.Y;
				vertex.Position.Z = Visual.Z;
				Visual.Vertices[i] = vertex;
			}
			Visual.Draw();
			//base.Draw();
		}

		public override Vector2 Position {
			get {
				return base.Position;
			}
			set {
				var translation = value - base.Position;

				for (int i = 0; i < bodies.Count; i++) {
					bodies[i].Position += translation;
				}

			}
		}

		readonly List<GimmickBody> bodies = new List<GimmickBody>(Corners + 1);
		readonly List<LinearSpring> springs = new List<LinearSpring>(Corners);

		public override void Update() {
			/*
			foreach (var body in bodies) {
				var position = body.Position;
				var force = position - Body.Position;
				force *= 5000;

				var area = body.Geometry._.WorldVertices.GetArea();

				force *= (restArea - area) / restArea;

				body.ApplyForceAtWorldPoint(ref position, ref force);
			}
			 * */
			base.Update();
		}

		public const int Corners = 12;

		GimmickBody CreateBody(Vector2 position) {
			var body = Factories.Bodies.Create(BodyKind.Puff, position, this);
			body.Geometry.CollisionCategories = CollisionCategories.C2;
			body.Geometry.CollidesWith = CollisionCategories.C5;
			return body;
		}

		public virtual void Move(Vector2 force) {
			if (force.X != 0 || force.Y != 0) {
				Vector2 point = Body.Position;
				Body.ApplyForceAtWorldPoint(ref point, ref force);
			}
		}
		public override void Save(XmlElement element) {
			base.Save(element);
		}
		public override void Load(XmlElement element) {
			base.Load(element);
		}
	}

}