using eyecm.PhysX;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using XELF.Sphynx;

namespace XELF.PhysX.ClothSample {

	public partial class ClothGame2 : Microsoft.Xna.Framework.Game {
		readonly GraphicsDeviceManager graphics;
		SpriteBatch spriteBatch;

		readonly PhysicsWorld gameScene = new PhysicsWorld();
		readonly Visual torsoVisual = new Visual();
		readonly Visual clothVisual = new Visual();
		/// <summary>
		/// 服
		/// </summary>
		Cloth cloth;
		/// <summary>
		/// 簡易形状の胴体
		/// </summary>
		Actor torso;
		VectorGraphics vectorGraphics;
		DebugRenderer renderer;
		Camera camera = new Camera();

		/// <summary>
		/// 胴体の回転
		/// </summary>
		float torsoRotation = 0;

		/// <summary>
		/// torsoの面の描画
		/// </summary>
		bool torsoFaceVisible = true;
		/// <summary>
		/// clothの面の描画
		/// </summary>
		bool clothFaceVisible = true;

		public ClothGame2() {
			graphics = new GraphicsDeviceManager(this);
			graphics.PreferMultiSampling = true;
			Content.RootDirectory = "Content";
		}

		void InitializeScene() {
			var actorDesc = new ActorDesc
			{
				Density = 0.01f,
				Body = new BodyDesc { },
			};
			actorDesc.Shapes.Add(
				new CapsuleShapeDesc(7, 150)
				{
					LocalPosition = new Vector3(0, 80, 0),
					ShapeFlags = ShapeFlags.Visualization,
				});
			actorDesc.Shapes.Add(
				new CapsuleShapeDesc(11.2f, 10)
				{
					LocalPosition = new Vector3(0, 131, 1.5f),
					ShapeFlags = ShapeFlags.Visualization,
				});
			actorDesc.Shapes.Add(
				new CapsuleShapeDesc(12.5f, 8.2f)
				{
					LocalPose = Matrix.CreateRotationZ(MathHelper.PiOver2) *
						Matrix.CreateTranslation(0, 100, 0),
					ShapeFlags = ShapeFlags.Visualization,
				});
			actorDesc.Shapes.Add(
				new CapsuleShapeDesc(6.5f, 64)
				{
					LocalPose = Matrix.CreateRotationZ(MathHelper.PiOver2) *
						Matrix.CreateTranslation(0, 140, 0),
					ShapeFlags = ShapeFlags.Visualization,
				});
			actorDesc.Shapes.Add(
				new CapsuleShapeDesc(4, 64)
				{
					LocalPose = Matrix.CreateRotationZ(MathHelper.PiOver2) *
						Matrix.CreateTranslation(0, 2, 0),
					ShapeFlags = ShapeFlags.Visualization,
				});
			actorDesc.Shapes.Add(
				new CapsuleShapeDesc(4, 64)
				{
					LocalPose = Matrix.CreateRotationX(MathHelper.PiOver2) *
						Matrix.CreateTranslation(0, 2, 0),
					ShapeFlags = ShapeFlags.Visualization,
				});
			torso = gameScene.Scene.CreateActor(actorDesc);
			// 強制的な運動ができるようにする。
			torso.BodyFlags.Kinematic = true;
		}

		protected override void LoadContent() {
			spriteBatch = new SpriteBatch(GraphicsDevice);
			vectorGraphics = new VectorGraphics(GraphicsDevice);
			vectorGraphics.Camera = camera;
			renderer = new DebugRenderer(this, vectorGraphics, gameScene.Scene);
			Components.Add(renderer);

			torsoVisual.Model = Content.Load<Model>("Torso");
			clothVisual.Model = Content.Load<Model>("Shirt");
			torsoVisual.Mesh = Mesh.FromModelMesh(torsoVisual.Model.Meshes[0]);
			clothVisual.Mesh = Mesh.FromModelMesh(clothVisual.Model.Meshes[0]);
			cloth = gameScene.CreateClothFromMesh(clothVisual.Mesh);
			cloth.CollisionResponseCoefficient = 1;

			InitializeScene();
		}

		/// <summary>
		/// ユーザー入力に応答する。
		/// </summary>
		partial void HandleInput();

		protected override void Update(GameTime gameTime) {
			HandleInput();

			cloth.WindAcceleration = new Vector3(10, (float)gameTime.TotalGameTime.TotalSeconds % 10, 10);

			// 強制的な運動
			torso.MoveGlobalPose(Matrix.CreateRotationY(torsoRotation));

			gameScene.Update(1.0f / 60);

			var world = Matrix.Identity;
			var view = Matrix.CreateLookAt(new Vector3(0, 150, 200), new Vector3(10, 100, 0), Vector3.Up);
			var projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
						GraphicsDevice.Viewport.AspectRatio, 1, 10000);

			camera.World = world;
			camera.View = view;
			camera.Projection = projection;

			// 剛体は姿勢を使う。
			if (torso != null) torsoVisual.World = torso.GlobalPose * Matrix.CreateTranslation(0, 18, 0);
			else torsoVisual.World = Matrix.CreateRotationY(torsoRotation) * Matrix.CreateTranslation(0, 80, 0);
			torsoVisual.View = view;
			torsoVisual.Projection = projection;

			// 布は計算された頂点の位置の変化を使う。
			clothVisual.World = world;
			clothVisual.View = view;
			clothVisual.Projection = projection;

			base.Update(gameTime);
		}

		protected override void UnloadContent() {
			gameScene.Dispose();
			base.UnloadContent();
		}

		protected override void Draw(GameTime gameTime) {
			GraphicsDevice.Clear(Color.DarkBlue);
			GraphicsDevice.RenderState.CullMode = CullMode.None;

			if (clothFaceVisible) {
				foreach (BasicEffect effect in clothVisual.Model.Meshes[0].Effects) {
					effect.PreferPerPixelLighting = true;
					effect.EnableDefaultLighting();
				}
				clothVisual.Draw();
			}

			if (torsoFaceVisible) {
				foreach (BasicEffect effect in torsoVisual.Model.Meshes[0].Effects) {
					effect.PreferPerPixelLighting = true;
					effect.EnableDefaultLighting();
				}
				torsoVisual.DrawModel();
			}
			base.Draw(gameTime);
		}
	}
}

namespace XELF.PhysX.ClothSample {
	public partial class ClothGame2 {
		KeyboardState previousKeyboarState;
		KeyboardState currentKeyboarState;

		public bool IsKeyPress(Keys key) {
			return currentKeyboarState.IsKeyDown(key) && previousKeyboarState.IsKeyUp(key);
		}
		public bool IsKeyDown(Keys key) {
			return currentKeyboarState.IsKeyDown(key);
		}

		partial void HandleInput() {
			previousKeyboarState = currentKeyboarState;
			currentKeyboarState = Keyboard.GetState();

			if (IsKeyPress(Keys.C)) {
				clothFaceVisible = !clothFaceVisible;
			}
			if (IsKeyPress(Keys.T)) {
				torsoFaceVisible = !torsoFaceVisible;
			}
			if (IsKeyPress(Keys.W)) {
				renderer.Visible = !renderer.Visible;
			}
			if (IsKeyDown(Keys.A)) {
				torsoRotation += 0.025f;
			}
			if (IsKeyDown(Keys.D)) {
				torsoRotation -= 0.025f;
			}

			if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
				this.Exit();
		}
	}

}
