using System;
using System.Xml;
#if JELLO_PHYSICS
using JelloPhysics;
#endif
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;

namespace XELF.Sphynx {

	enum GimmickKind {
		/// <summary>なし</summary>
		None,

		/// <summary>キャラクター</summary>
		Actor,
		/// <summary>キャラクター</summary>
		Puff,
		/// <summary>キャラクター</summary>
		Doll,
		/// <summary>目的地点</summary>
		StartWay,
		/// <summary>中継地点</summary>
		CheckWay,
		/// <summary>目的地点</summary>
		GoalWay,
		/// <summary>ボタン式スイッチ</summary>
		Button,
		/// <summary>キーを感知するスイッチ</summary>
		Lock,
		/// <summary>Lockに反応させるキー</summary>
		Key,
		/// <summary>放出</summary>
		Emitter,

		/// <summary>アンカー</summary>
		Anchor,
		/// <summary>回路の分岐点</summary>
		Junction,

		/// <summary>配線</summary>
		Lead,

		/// <summary>スライダー関節</summary>
		Slider,

		Wall,
#if false
		/// <summary>
		/// 回転関節
		/// </summary>
		Revolute,
#endif
		Size,
	};

	enum WayKind {
		None,
		Start,
		Check,
		Goal,
	}

	/// <summary>
	/// 各種ファクトリ
	/// </summary>
	class Factories {
		public readonly Stage Stage;
		public readonly GimmickFactory Gimmicks;
		public readonly BodyFactory Bodies;
		public readonly VisualFactory Visuals;

		public Factories(Stage stage, World world,
			GraphicsDevice graphicsDevice, BasicEffect effect, Texture2D texture, Camera camera) {
			Stage = stage;
			Gimmicks = new GimmickFactory(this);
			Bodies = new BodyFactory(this, world);
			Visuals = new VisualFactory(this, graphicsDevice, effect, texture, camera);
		}
	}

	/// <summary>
	/// ギミックのファクトリ
	/// </summary>
	class GimmickFactory {
		public readonly Factories Factories;

		public GimmickFactory(Factories factories) {
			Factories = factories;
		}

		public Gimmick Create(XmlElement element) {
			if (element == null)
				return null;
			var kind = (GimmickKind)Enum.Parse(typeof(GimmickKind), element.Name);
			var key = element.ValueAsInt32("Key");
			var value = Create(Vector2.Zero, kind, Unique.Create(key));
			value.Load(element);
			return value;
		}
		public Gimmick Create(Vector2 position, GimmickKind Kind, Unique unique) {
			switch (Kind) {
			case GimmickKind.Actor: return new ActorGimmick(Factories, position, unique);
			case GimmickKind.Puff: return new PuffGimmick(Factories, position, unique);
			case GimmickKind.Doll: return new DollGimmick(Factories, position, unique);
			case GimmickKind.StartWay: return new WayGimmick(Factories, position, unique, WayKind.Start);
			case GimmickKind.CheckWay: return new WayGimmick(Factories, position, unique, WayKind.Check);
			case GimmickKind.GoalWay: return new WayGimmick(Factories, position, unique, WayKind.Goal);
			case GimmickKind.Button: return new ButtonGimmick(Factories, position, unique);
			case GimmickKind.Lock: return new LockGimmick(Factories, position, unique);
			case GimmickKind.Key: return new KeyGimmick(Factories, position, unique);
			case GimmickKind.Emitter: return new EmitterGimmick(Factories, position, unique);
			case GimmickKind.Anchor: return new AnchorGimmick(Factories, position, unique, GimmickKind.Anchor);
			case GimmickKind.Junction: return new AnchorGimmick(Factories, position, unique, GimmickKind.Junction);
			case GimmickKind.Lead: return new LeadGimmick(Factories, position, unique);
			case GimmickKind.Slider: return new SliderGimmick(Factories, position, unique);
			case GimmickKind.Wall: return new WallGimmick(Factories, position, unique);
			}
			return null;
		}

		public ReferenceGimmick Refer(int key) {
			if (key == 0)
				return null;
			return new ReferenceGimmick(Factories, key);
		}
	}

	class ReferenceGimmick : Gimmick {
		public int Key { get; private set; }
		public ReferenceGimmick(Factories factories, int key)
			: base(GimmickKind.None, factories, null) {
			Key = key;
		}
		public override Gimmick CloneGimmick(bool upgrade) {
			throw new NotImplementedException();
		}

		static readonly Dictionary<int, Gimmick> dictionary = new Dictionary<int, Gimmick>();

		public static void CreateDictionary(List<Gimmick> gimmicks) {
			dictionary.Clear();
			foreach (var gimmick in gimmicks) {
				dictionary.Add(gimmick.Unique.Key, gimmick);
			}
		}

		public static Gimmick Resolve(Gimmick gimmick) {
			var reference = gimmick as ReferenceGimmick;
			if (reference == null)
				return gimmick;
			return dictionary[reference.Key];
		}
	}

	class Unique : IDisposable {
		public Unique this[bool upgrade] {
			get {
				if (upgrade) return this;
				return Create();
			}
		}

		public static void Clear() {
			Pool.Instance.Clear();
		}

		public override string ToString() {
			return string.Format("{{Key={0}, Tag={1}}}", Key, Tag);
		}
		class Pool {
			public static readonly Pool Instance = new Pool();
			protected Pool() { }

			public void Clear() {
				foreach (var unique in usedKeys.Values) {
					keys.Add(unique.Key, unique);
				}
				usedKeys.Clear();
			}

			readonly Dictionary<int, Unique> keys = new Dictionary<int, Unique>();
			readonly Dictionary<int, Unique> usedKeys = new Dictionary<int, Unique>();

			int keyCounter;

			public Unique Pop() {
				Unique unique;
				if (keys.Count > 0) {
					unique = null;
					foreach (var pop in keys.Values) {
						unique = pop;
						break;
					}
					keys.Remove(unique.Key);
				} else {
					do {
						++keyCounter;
					} while (usedKeys.ContainsKey(keyCounter));
					unique = new Unique(keyCounter);
				}
				usedKeys.Add(unique.Key, unique);
				return unique;
			}
			public void Push(Unique unique) {
				keys.Add(unique.Key, unique);
				usedKeys.Remove(unique.Key);
			}
			public Unique Register(int key) {
				Unique unique;
				if (keys.ContainsKey(key)) {
					unique = keys[key];
					keys.Remove(key);
				} else {
					unique = new Unique(key);
				}
				usedKeys.Add(unique.Key, unique);
				return unique;
			}
		}
		public int Key { get; private set; }

		public void Update(int key) {
			Key = key;
		}
		protected Unique(int key) {
			Key = key;
		}

		public static Unique Create() {
			return Pool.Instance.Pop();
		}
		public static Unique Create(int key) {
			if (key < 0) return Pool.Instance.Pop();
			return Pool.Instance.Register(key);
		}

		public virtual void Dispose() {
			Pool.Instance.Push(this);
		}
		public object Tag {
			get;
			set;
		}
	}

	/// <summary>
	/// ギミック
	/// </summary>
	abstract class Gimmick : IDisposable, ILocator2 {
		bool isDisposed;

		/// <summary>
		/// 現在のステージ中にあるバーションのギミックを得る。
		/// </summary>
		public Gimmick CurrentVersion {
			get {
				return Factories.Stage.Resolve(this);
			}
		}
		/// <summary>
		/// 参照するギミックを現在のバージョンに参照しなおす。
		/// </summary>
		public virtual void Resolve() {
		}

		public bool IsDisposed {
			get { return isDisposed; }
		}

		public virtual bool Registered {
			get {
				return Factories.Stage.Gimmicks.Contains(this);
			}
			set {
				if (value) {
					BodyRegistered = true;
					Factories.Stage.Gimmicks.Add(this);
				} else {
					BodyRegistered = false;
					Factories.Stage.Gimmicks.Remove(this);
				}
			}
		}

		public Color GuideColor = Settings.DefaultGuideColor;

		public GimmickKind Kind { get; protected set; }
		public abstract Gimmick CloneGimmick(bool upgrade);

		public readonly Unique Unique;

		/// <summary>
		/// 複製を作る。
		/// </summary>
		/// <param name="upgrade">trueの場合は、同じキーを持ち、falseの場合は異なるキーを持つ。</param>
		/// <returns></returns>
		public Gimmick Clone(bool upgrade) {
			//			if (Key < 0)
			//				throw new ObjectDisposedException(this.GetType().Name);
			var value = CloneGimmick(upgrade);
			//value.Unique = Unique;
			if (value.Body == null) value.Body = Body.Clone();
			//Body.CopyTo(value.Body);
			value.Body.Tag = value;
			if (Visual != null && value.Visual == null) value.Visual = Visual.Clone();
			return value;
		}

		public event EventHandler Disposed;
		public readonly Factories Factories;
		public Visual Visual;

		public int Duration = -1;
		public int Age;

		public virtual bool Enabled {
			get { return Body.Enabled; }
			set { Body.Enabled = value; }
		}
		public virtual bool BodyRegistered {
			get { return Body.Registered; }
			set { Body.Registered = value; }
		}

		public virtual void Dispose() {
			isDisposed = true;
			if (Body != null) {
				BodyRegistered = false;
			}
			if (Disposed != null) {
				Disposed(this, EventArgs.Empty);
			}
		}
		public Gimmick(GimmickKind kind, Factories factories, Unique unique) {
			Kind = kind;
			Factories = factories;
			Unique = unique;
		}

		GimmickBody body;

		public GimmickBody Body {
			get { return body; }
			protected set {
				if (body != value) {
					body = value; OnBodyChanged();
				}
			}
		}

		protected virtual void OnBodyChanged() {
		}

		public bool Alive {
			get { return Duration < 0 || Age < Duration; }
		}

		protected void SetPosition(GimmickBody body, Vector2 position) {
			/*
			int count = body.Shape.Vertices.Count;
			Vector2 translation = position - Position;
			for (int i = 0; i < count; i++) {
				body.getPointMass(i).Position += translation;
			}
			 * */
			//body.SetPositionRotation(position, 0, Vector2.One);
			if (body != null)
				body.Position = position;
		}
		public virtual float Rotation {
			get { return Body._.Rotation; }
			set {
				if (Body != null)
					Body._.Rotation = value;
			}
		}
		public virtual Vector2 Position {
			get { return Body.Position; }
			set {
				SetPosition(Body, value);
			}
		}

		public virtual void Update() {
			if (Duration > 0 && Age < Duration) {
				Age++;
			}
		}

		/// <summary>
		/// ボディの頂点をビジュアルの頂点に適用します。
		/// </summary>
		/// <param name="body"></param>
		/// <param name="visual"></param>
		protected void UpdateVertices(Body body, Visual visual) {
			if (visual != null) {
				int count = Math.Min(body.WorldVertices.Count, visual.Vertices.Length);
				for (int i = 0; i < count; i++) {
					Vector2 p = body.WorldVertices[i];
					visual.Vertices[i].Position.X = p.X;
					visual.Vertices[i].Position.Y = p.Y;
					visual.Vertices[i].Position.Z = visual.Z;
				}
			}
		}

		/// <summary>
		/// デザイン時の描画
		/// </summary>
		/// <param name="graphics"></param>
		public virtual void Draw(VectorGraphics graphics) {
			if (Body.IsStatic)
				graphics.DrawIcon(IconKind.Fixed, Body.Position, new Color(255, 255, 255, 128), 0.25f);
		}
		public virtual void Draw() {
			if (Visual != null) {
				UpdateVertices(Body, Visual);
				Visual.Draw();
			}
		}

		public virtual void Save(XmlElement element) {
			element.Write("Key", Unique.Key);
			element.Write("Age", Age);
			element.Write("Duration", Duration);
			element.Write("Position", Position);
			element.Write("Rotation", Rotation);
			element.Write("IsStatic", body.IsStatic);
		}
		public virtual void Load(XmlElement element) {
			Unique.Update(element.ValueAsInt32("Key"));
			Age = element.ValueAsInt32("Age");
			Duration = element.ValueAsInt32("Duration");
			Position = element.ValueAsVector2("Position");
			Rotation = element.ValueAsSingle("Rotation");
			body.IsStatic = element.ValueAsBoolean("IsStatic");
		}

		public void AppendChildTo(XmlElement parent) {
			var _gimmick = parent.AppendChild(Kind.ToString());
			Save(_gimmick);
		}

		/// <summary>
		/// 幾何形状に基づいて、描画メッシュとそのテクスチャマッピングを更新します。
		/// </summary>
		protected void UpdateMesh() {
			UpdateMesh(Body, Visual);
		}
		protected static void UpdateMesh(Body body, Visual visual) {
			Geometry geometry = body.Geometry;

			var shape = geometry._.LocalVertices;

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

	class WallGimmick : Gimmick {
		public override Gimmick CloneGimmick(bool upgrade) {
			var value = new WallGimmick(Factories, Position, Unique[upgrade], this);
			return value;
		}
		public override string ToString() {
			return string.Format("Wall");
		}
		public WallGimmick(Factories factories, Vector2 position, Unique unique, WallGimmick source)
			: base(GimmickKind.Wall, factories, unique) {
			Body = source.Body.Clone();
			Visual = source.Visual.Clone();
		}

		public WallGimmick(Factories factories, Vector2 position, Unique unique)
			: base(GimmickKind.Wall, factories, unique) {
			Body = factories.Bodies.Create(BodyKind.Wall, position, this);
			Visual = factories.Visuals.Create(VisualKind.Wall);
			UpdateMesh();
		}
	}

	class WayGimmick : Gimmick {
		public readonly WayKind WayKind;

		public override Gimmick CloneGimmick(bool upgrade) {
			return new WayGimmick(Factories, Position, Unique[upgrade], WayKind);
		}

		public static BodyKind BodyKindOf(WayKind Kind) {
			switch (Kind) {
			case WayKind.Start: return BodyKind.StartWay;
			case WayKind.Check: return BodyKind.CheckWay;
			case WayKind.Goal: return BodyKind.GoalWay;
			}
			return BodyKind.None;
		}
		public static VisualKind VisualKindOf(WayKind Kind) {
			switch (Kind) {
			case WayKind.Start: return VisualKind.StartWay;
			case WayKind.Check: return VisualKind.CheckWay;
			case WayKind.Goal: return VisualKind.GoalWay;
			}
			return VisualKind.None;
		}
		public static GimmickKind GimmickKindOf(WayKind Kind) {
			switch (Kind) {
			case WayKind.Start: return GimmickKind.StartWay;
			case WayKind.Check: return GimmickKind.CheckWay;
			case WayKind.Goal: return GimmickKind.GoalWay;
			}
			return GimmickKind.None;
		}
		public override string ToString() {
			return string.Format("{0}", WayKind);
		}
		public WayGimmick(Factories factories, Vector2 position, Unique unique, WayKind WayKind)
			: base(GimmickKindOf(WayKind), factories, unique) {
			this.WayKind = WayKind;
			Body = factories.Bodies.Create(BodyKindOf(WayKind), position, this);
			Visual = factories.Visuals.Create(VisualKindOf(WayKind));

			//Body.Geometry._.IsSensor = true;// このギミック全体がセンサー

			Body.Geometry.CollisionCategories = CollisionCategories.C1;
			Body.Geometry.CollidesWith = CollisionCategories.C1;

			Reset();
		}

		public virtual void Reset() {
			Life = 4;
		}

		public int Life;

		void UpdateGoal() {
			var actor = Factories.Stage.Actor;
			if (actor != null) {
				bool hit = Body.Geometry._.Collide(actor.Body.Geometry._);
				if (hit) {
					Factories.Stage.FinishGame();
				}
			}
		}

		public override void Update() {
			//Body._.Enabled = false;
			switch (WayKind) {
			case WayKind.Goal: UpdateGoal(); break;
			}

			/*
			var bodies = Body.World.CollideAllBodies(Position);
			foreach (var body in bodies) {
				var actor = body.Tag as ActorGimmick;
				if (actor != null)
					Factories.Stage.FinishGame();
			}
			 * */
			base.Update();
		}
		public override void Save(XmlElement element) {
			base.Save(element);
			//element.Write("Kind", WayKind);
		}
		public override void Load(XmlElement element) {
			base.Load(element);
			//WayKind = element.Value<WayKind>("Kind");
		}
	}

	abstract class SwitchGimmick : Gimmick {
		public SwitchGimmick(GimmickKind kind, Factories factories, Unique unique)
			: base(kind, factories, unique) {
			Anchors = new List<AnchorGimmick>();
		}
		public SwitchGimmick(GimmickKind kind, Factories factories, Unique unique, List<AnchorGimmick> anchors)
			: base(kind, factories, unique) {
			Anchors = anchors;
		}

		/// <summary>
		/// スイッチONのときにtrue
		/// </summary>
		public Operator Output = new Operand();
		public readonly List<AnchorGimmick> Anchors;

		public override void Resolve() {
			for (int i = 0; i < Anchors.Count; i++) {
				Anchors[i] = (AnchorGimmick)Anchors[i].CurrentVersion;
			}
			base.Resolve();
		}
		public override Vector2 Position {
			get {
				return base.Position;
			}
			set {
				if (base.Position != value) {
					var d = value - base.Position;
					foreach (var anchor in Anchors) {
						anchor.Position += d;
					}
					base.Position = value;
				}

			}
		}
	}

	class DetectorGimmick : SwitchGimmick {
		public override Gimmick CloneGimmick(bool upgrade) {
			return new DetectorGimmick(Kind, Factories, Unique[upgrade]);
		}
		public DetectorGimmick(GimmickKind kind, Factories factories, Unique unique)
			: base(kind, factories, unique) {
		}
	}

	class ButtonGimmick : SwitchGimmick {
		GimmickBody Inner;
		Visual InnerVisual;
		readonly List<LinearSpring> springs = new List<LinearSpring>();
		const float ActiveRadius = 0.25f;

		protected override void OnBodyChanged() {
			foreach (var item in springs) {
				item.Body1 = Body;
				item.Body2 = Inner;
			}
		}
		public override string ToString() {
			return string.Format("Button{{Active={0}, {1}}}", Output.Active, Position);
		}
		public override Gimmick CloneGimmick(bool upgrade) {
			var value = new ButtonGimmick(Factories, Position, Unique[upgrade], this)
			{
				Rotation = Rotation,
			};
			//value.Initialize();
			return value;
		}
		public override bool BodyRegistered {
			set {
				base.BodyRegistered = value;
				Inner.Registered = value;
				foreach (var item in springs) item.Registered = value;
			}
		}

		void Initialize(GimmickBody outer, Visual outerVisual, GimmickBody inner, Visual innerVisual) {
			Inner = inner;
			Inner.Tag = this;
			InnerVisual = innerVisual;
			Body = outer;
			Body.Tag = this;
			Visual = outerVisual;

			UpdateMesh();
			UpdateMesh(Inner, InnerVisual);

			Vector2 d = new Vector2(1, 1.0f);
			var length = d.Length();
			springs.Add(new LinearSpring(Factories.Bodies.World)
			{
				Body1 = Body,
				AttachPoint1 = new Vector2(-0.5f, -0.25f),
				Body2 = Inner,
				AttachPoint2 = new Vector2(+0.5f, +0.25f),
				SpringConstant = 500,
				DampingConstant = 10,
				RestLength = length,
			});
			springs.Add(new LinearSpring(Factories.Bodies.World)
			{
				Body1 = Body,
				AttachPoint1 = new Vector2(+0.5f, -0.25f),
				Body2 = Inner,
				AttachPoint2 = new Vector2(-0.5f, +0.25f),
				SpringConstant = 500,
				DampingConstant = 10,
				RestLength = length,
			});
		}

		public ButtonGimmick(Factories factories, Vector2 position, Unique unique, ButtonGimmick source)
			: base(GimmickKind.Button, factories, unique) {
			Initialize(source.Body.Clone(), source.Visual.Clone(),
				source.Inner.Clone(), source.InnerVisual.Clone());
		}
		public ButtonGimmick(Factories factories, Vector2 position, Unique unique)
			: base(GimmickKind.Button, factories, unique) {

			var Inner = factories.Bodies.Create(BodyKind.ButtonInner, position + new Vector2(0, 0.5f), this);
			Inner.Geometry.CollisionCategories = CollisionCategories.C2;
			Inner.Geometry.CollidesWith = CollisionCategories.C2;
			Inner.Mass = 0.5f;
			var Body = factories.Bodies.Create(BodyKind.ButtonOuter, position, this);
			Body.Geometry.CollisionCategories = CollisionCategories.C2;
			Body.Geometry.CollidesWith = CollisionCategories.C2;
			var InnerVisual = factories.Visuals.Create(VisualKind.ButtonInner);
			var Visual = factories.Visuals.Create(VisualKind.ButtonOuter);

			Initialize(Body, Visual, Inner, InnerVisual);
		}
		public override Vector2 Position {
			get { return base.Position; }
			set {
				base.Position = value;
				SetPosition(Inner, value + new Vector2(0, 0.5f));
			}
		}
		public override float Rotation {
			get {
				return base.Rotation;
			}
			set {
				base.Rotation = value;
				Inner.Rotation = value;
			}
		}
		public override void Draw() {
			UpdateVertices(Inner, InnerVisual);
			InnerVisual.Draw();
			base.Draw();
		}
		public override void Update() {
			Output.Active = Vector2.DistanceSquared(Inner.Position, Body.Position) <= ActiveRadius * ActiveRadius;

			// assert
			foreach (var item in springs) {
				if (item.Body1 != Body || item.Body2 != Inner) {
					throw new Exception();
				}
			}
			base.Update();
		}
		public override void Dispose() {
			Inner.Registered = false;
			BodyRegistered = false;//test
			Inner.Dispose();
			Body.Dispose();
			foreach (var item in springs) item.Dispose();
			base.Dispose();
		}
	}

	class LockGimmick : SwitchGimmick {
		public override void Draw(VectorGraphics graphics) {
			graphics.DrawEllipse(Body.Position, Radius, Color.Red);
			base.Draw(graphics);
		}

		public override string ToString() {
			return string.Format("Lock(R={0}, Active={1}, {2})", Radius, Output.Active, Position);
		}
		public override Gimmick CloneGimmick(bool upgrade) {
			return new LockGimmick(Factories, Position, Unique[upgrade])
			{
				hit = this.hit,
				Radius = this.Radius,
			};
		}

		public LockGimmick(Factories factories, Vector2 position, Unique unique)
			: base(GimmickKind.Lock, factories, unique) {
			Body = factories.Bodies.Create(BodyKind.Lock, position, this);
			Body.Geometry.CollisionCategories = CollisionCategories.C3;
			Body.Geometry.CollidesWith = CollisionCategories.C3;
			Visual = factories.Visuals.Create(VisualKind.Lock);
		}

		int hit;

		public float Radius = 4;

		public override void Update() {
			int count = Factories.Bodies.World.BodyCount;

			hit = 0;

			float rr = Radius * Radius;

			for (int i = 0; i < count; i++) {
				var body = Factories.Bodies.World.GetBody(i);
				if (body != null) {
					KeyGimmick item = body.Tag as KeyGimmick;
					if (item != null) {
						if (Vector2.DistanceSquared(item.Position, Position) <= rr) {
							hit++;
						}
					}
				}
			}
			Output.Active = hit > 0;

			base.Update();
		}

		public override void Load(XmlElement element) {
			Radius = element.ValueAsSingle("Radius");
			base.Load(element);
		}
		public override void Save(XmlElement element) {
			element.Write("Radius", Radius);
			base.Save(element);
		}
	}

	class KeyGimmick : Gimmick {
		public override Gimmick CloneGimmick(bool upgrade) {
			return new KeyGimmick(Factories, Position, Unique[upgrade]);
		}
		public KeyGimmick(Factories factories, Vector2 position, Unique unique)
			: base(GimmickKind.Key, factories, unique) {
			Body = factories.Bodies.Create(BodyKind.Key, position, this);
			Body.Geometry.CollisionCategories = CollisionCategories.C4;
			Body.Geometry.CollidesWith = CollisionCategories.C4;
			Visual = factories.Visuals.Create(VisualKind.Key);
		}
		public override string ToString() {
			return string.Format("Key({0})", Position);
		}
	}

	class ActorGimmick : Gimmick {
		public override string ToString() {
			return string.Format("Actor({0}/{1}[deg])", Position, Rotation.ToDegrees());
		}
		public override Gimmick CloneGimmick(bool upgrade) {
			return new ActorGimmick(Factories, Position, Unique[upgrade]);
		}
		protected ActorGimmick(GimmickKind kind, Factories factories, Vector2 position, Unique unique)
			: base(kind, factories, unique) {
		}
		public ActorGimmick(Factories factories, Vector2 position, Unique unique)
			: base(GimmickKind.Actor, factories, unique) {
			Body = factories.Bodies.Create(BodyKind.Actor, position, this);
			Body.Geometry.CollisionCategories = CollisionCategories.C2;
			Body.Geometry.CollidesWith = CollisionCategories.C2;
			//Body._.LinearDragCoefficient = 0.1f;
			//Body._.IsQuadraticDragEnabled = true;
			Visual = factories.Visuals.Create(VisualKind.Actor);
			UpdateMesh();
		}

		public virtual void Move(Vector2 force) {
			if (force.X != 0 || force.Y != 0) {
				Vector2 point = Body.Position;
				Body.ApplyForceAtWorldPoint(ref point, ref force);
			}
			/*
			for (int i = 0; i < Body.Shape.Vertices.Count; i++) {
				Body.getPointMass(i).Velocity += velocity * 10;
			}
			*/
		}
		public override void Save(XmlElement element) {
			base.Save(element);
		}
		public override void Load(XmlElement element) {
			base.Load(element);
		}
	}

	class EmitterGimmick : Gimmick {
		int time;
		int span = 60;
		Gimmick emanation;
		readonly List<Gimmick> children = new List<Gimmick>();

		public override string ToString() {
			return string.Format("Emitter(Time={0}, Span={1}, Emanation={2})", time, span, emanation);
		}
		public override Gimmick CloneGimmick(bool upgrade) {
			return new EmitterGimmick(Factories, Position, Unique[upgrade])
			{
				time = this.time,
				span = this.span,
				emanation = emanation != null ? this.emanation.Clone(upgrade) : null,
			};
		}
		public EmitterGimmick(Factories factories, Vector2 position, Unique unique)
			: base(GimmickKind.Emitter, factories, unique) {

			Body = factories.Bodies.Create(BodyKind.Emitter, position, this);
			Body.Geometry.CollisionCategories = CollisionCategories.C1;
			Body.Geometry.CollidesWith = CollisionCategories.C1;

			Visual = factories.Visuals.Create(VisualKind.Emitter);
		}

		public override void Update() {
			if (++time == span) {
				time = 0;
				if (emanation != null) {
					var child = emanation.Clone(false);
					child.Registered = true;
					children.Add(child);
					child.Disposed += new EventHandler(child_Disposed);
				}
			}

			base.Update();
		}

		void child_Disposed(object sender, EventArgs e) {
			children.Remove((Gimmick)sender);
		}
		public override void Load(XmlElement element) {
			time = element.Attributes["Time"].ValueAsInt32();
			span = element.Attributes["Span"].ValueAsInt32();
			emanation = Factories.Gimmicks.Create(element["Emanation"]);
			base.Load(element);
		}
		public override void Save(XmlElement element) {
			element.AppendAttribute("Time", time.ToString());
			element.AppendAttribute("Span", span.ToString());
			if (emanation != null) emanation.AppendChildTo(element);
			base.Save(element);
		}
	}

}
