using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Jitter;
using Jitter.LinearMath;
using Jitter.Collision;
using Jitter.Dynamics;

namespace KinPortal {
	public class Scene : DrawableGameComponent {
		private Camera m_camera = null;
		public Camera Camera {
			get {
				return m_camera;
			}
			set {
				if(m_camera != null) {
					Game.Components.Remove(m_camera);
				}
				m_camera = value;
				Game.Components.Add(m_camera);
			}
		}

		public List<DrawableGameComponent> Components {get; private set;}
		public World PhysicWorld {get; private set;}
		public PortalManager PortalManager {get; private set;}

		public Scene(KinPortal.Game game, Camera camera)
				: base(game) {
			Camera = camera;
			Components = new List<DrawableGameComponent>();
			PhysicWorld = new World(new CollisionSystemSAP());
			PhysicWorld.Gravity = new JVector(0, 0, -9.81f);
			PortalManager = new PortalManager(this);
			PhysicWorld.CollisionSystem.CollisionDetected += this.CollisionEvent;
			UpdateOrder = 0;
		}

		public void AddComponent(DrawableGameComponent obj) {
			Components.Add(obj);
			Game.Components.Add(obj);
			if(obj is DynamicObject) {
				PhysicWorld.AddBody(((DynamicObject) obj).PhysicModel);
			}
		}

		public void RemoveComponent(DrawableGameComponent obj) {
			Components.Remove(obj);
			Game.Components.Remove(obj);
			if(obj is DynamicObject) {
				PhysicWorld.RemoveBody(((DynamicObject) obj).PhysicModel);
			}
		}

		private bool RaycastCallback(RigidBody body, JVector normal, float fraction) {
			return true;
		}

		public override void Update(GameTime gameTime) {
			float phyStep = gameTime.ElapsedGameTime.Milliseconds / 1000.0f;
			PhysicWorld.Step(phyStep, true);

			KinPortal.Game game = (KinPortal.Game) Game;

			JVector ray;
			float fraction;
			JVector normal;
			HandsProjection leftHand = new HandsProjection();
			HandsProjection rightHand = new HandsProjection();
			JVector camPos = Geometry.GetJitterVector(Camera.Pos);
			RigidBody leftBody = null, rightBody = null;

			/* Left Hand projection */
			ray = Geometry.GetJitterVector(Camera.Unproject(game.HandsTracker.LeftHand.Pos.X, game.HandsTracker.LeftHand.Pos.Y));
			ray = JVector.Normalize(ray) * 100.0f;

			if(PhysicWorld.CollisionSystem.Raycast(camPos, ray, RaycastCallback, out leftBody, out normal, out fraction)) {
				leftHand.HitPoint = Geometry.GetXnaVector(camPos + fraction * ray);
				leftHand.HitNormal = Geometry.GetXnaVector(JVector.Normalize(normal));
				leftHand.IsProjected = true;		
			} else {
				leftHand.IsProjected = false;	
			}

			/* Right Hand projection */
			ray = Geometry.GetJitterVector(Camera.Unproject(game.HandsTracker.RightHand.Pos.X, game.HandsTracker.RightHand.Pos.Y));
			ray = JVector.Normalize(ray) * 100.0f;

			if(PhysicWorld.CollisionSystem.Raycast(camPos, ray, RaycastCallback, out rightBody, out normal, out fraction)) {
				rightHand.HitPoint = Geometry.GetXnaVector(camPos + fraction * ray);
				rightHand.HitNormal = Geometry.GetXnaVector(JVector.Normalize(normal));
				rightHand.IsProjected = true;
			} else {
				rightHand.IsProjected = false;
			}

			DynamicObject leftObj = null, rightObj = null;

			foreach(DrawableGameComponent obj in Components) {
				if(obj is DynamicObject) {
					DynamicObject dobj = (DynamicObject) obj;
					if(dobj.PhysicModel == leftBody) {
						leftObj = dobj;
					}
					if(dobj.PhysicModel == rightBody) {
						rightObj = dobj;
					}
				}
			}

			if(leftObj == rightObj && leftObj != null) {
				leftObj.Target(leftHand, rightHand);
			} else {
				if(leftObj != null) {
					leftObj.Target(leftHand, new HandsProjection(false, Vector3.Zero, Vector3.Zero));
				}
				if(rightObj != null) {
					rightObj.Target(new HandsProjection(false, Vector3.Zero, Vector3.Zero), rightHand);
				}
			}
			
			base.Update(gameTime);
		}

		public void CollisionEvent(RigidBody body1, RigidBody body2, JVector point1, JVector point2, JVector normal, float penetration) {
			DynamicObject dobj1 = null;
			DynamicObject dobj2 = null;
			
			foreach(DrawableGameComponent obj in Components) {
				if(obj is DynamicObject) {
					DynamicObject dobj = (DynamicObject) obj;
					if(dobj.PhysicModel == body1) {
						dobj1 = dobj;
					}
					if(dobj.PhysicModel == body2) {
						dobj2 = dobj;
					}
				}
			}

			if(dobj1 == null || dobj2 == null) {
				return;
			}

			if(dobj1 is InterruptorObject) {
				((InterruptorObject) dobj1).Collision(dobj2);
			}
			if(dobj2 is InterruptorObject) {
				((InterruptorObject) dobj2).Collision(dobj1);
			}
		}

		public override void Draw(GameTime gameTime) {
			foreach(DrawableGameComponent obj in Components) {
				obj.Draw(gameTime);
			}
			base.Draw(gameTime);
		}
	}
}
