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

namespace XELF.Sphynx {

	class CameraComponent : DrawableGameComponent {
		public ActiveCamera Camera { get; private set; }

		public CameraComponent(Game game)
			: base(game) {
		}
		protected override void LoadContent() {
			Camera = new ActiveCamera(GraphicsDevice);
			base.LoadContent();
		}
		public override void Update(GameTime gameTime) {
			Camera.Update();
			base.Update(gameTime);
		}
		public override void Draw(GameTime gameTime) {
			base.Draw(gameTime);
		}
	}

	class SphynxGame : Microsoft.Xna.Framework.Game {
		readonly GraphicsDeviceManager graphics;
		SpriteBatch spriteBatch;
		BasicEffect effect;
		readonly World world;
		readonly CameraComponent camera;
		readonly VectorGraphicsComponent vectorGraphics;
		readonly PointerDisplay pointer;
		readonly Desktop desktop;
		readonly MouseInput input = new MouseInput();
		readonly KeyboardInput keyboardInput = new KeyboardInput();
		readonly ScoreDisplay scoreDisplay;
		Stage stage;
		StageEditor stageEditor;
		StageSelector stageSelector;

		public SphynxGame() {
			graphics = new GraphicsDeviceManager(this);
			graphics.PreferredBackBufferWidth = 1280;
			graphics.PreferredBackBufferHeight = 720;
			graphics.PreferMultiSampling = true;
			Content.RootDirectory = "Content";

			world = new World();
			world.Gravity = new Vector2(0, -9.8f);
			world.AutoAddBody = false;

			Components.Add(camera = new CameraComponent(this));
			vectorGraphics = new VectorGraphicsComponent(this);
			pointer = new PointerDisplay(this, vectorGraphics, input, camera);

			Components.Add(vectorGraphics);
			Components.Add(desktop = new Desktop(vectorGraphics, pointer, keyboardInput, input));
			Components.Add(scoreDisplay = new ScoreDisplay(this));
			Components.Add(stageSelector = new StageSelector(this));
			Components.Add(pointer);
		}

		CheckButton playButton;
		Button undoButton;
		Button redoButton;

		protected override void LoadContent() {
			spriteBatch = new SpriteBatch(GraphicsDevice);
			effect = new BasicEffect(GraphicsDevice, null)
			{
				VertexColorEnabled = true,
				LightingEnabled = false,
			};
			var texture = Content.Load<Texture2D>("Gimmick");

			desktop.Clips.Add(new Clip(texture, new Rectangle(0x80 * 0, 0x80 * 4, 0x80, 0x80)));
			desktop.Clips.Add(new Clip(texture, new Rectangle(0x80 * 1, 0x80 * 4, 0x80, 0x80)));
			desktop.Clips.Add(new Clip(texture, new Rectangle(0x80 * 2, 0x80 * 4, 0x80, 0x80)));
			desktop.Clips.Add(new Clip(texture, new Rectangle(0x80 * 3, 0x80 * 4, 0x80, 0x80)));


			world.SetBounds(new Vector2(-20, -20), new Vector2(20, 20));

			vectorGraphics.Batch.IconTexture = Content.Load<Texture2D>("Gimmick");
			/*
			var shape = new ClosedShape(new List<Vector2>() {
				new Vector2(1, 2), new Vector2(5, 5), new Vector2(4, 9), new Vector2(3, 8), new Vector2(1, 5), });

			var body = new GimmickBody(world, shape, 1, 1, 0.1f, Vector2.Zero, 0, Vector2.One);
			world.addBody(body);
			body.addInternalSpring(0, 2, 1, 0.1f);
			body.addInternalSpring(1, 4, 1, 0.1f);
			*/

			/*
			var ground = new ClosedShape(new List<Vector2>() {
				new Vector2(-100, 100),
				new Vector2(-20, 100),
				new Vector2(-20, -5), new Vector2(20, -20),
				new Vector2(100, 100),
				new Vector2(100, -100),
				new Vector2(20, -30), new Vector2(-20, -30),
			});
			ground.Subdivide(10);
			//world.addBody(new Body(world, ground, float.PositiveInfinity, Vector2.Zero, 0, Vector2.One, false));
			var body = new Body(world, ground, float.PositiveInfinity);

			world.AddBody(body);
			body._.LinearDragCoefficient = 100;
			body._.RotationalDragCoefficient = 100;
			body._.QuadraticDragCoefficient = 100;
			body._.IsQuadraticDragEnabled = true;
			body.Geometry.FrictionCoefficient = 1;
			*/

			var font = Content.Load<SpriteFont>("Default");
			var bigFont = Content.Load<SpriteFont>("BigFont");
			stage = new Stage(GraphicsDevice, bigFont, world, effect, texture, camera.Camera, keyboardInput);
			scoreDisplay.Status = stage.Status;
			stageEditor = new StageEditor(vectorGraphics.Batch, input, keyboardInput,
				GraphicsDevice, font, texture, effect, stage, stageSelector, desktop);
			stage.ModeChanged += new System.EventHandler(stage_ModeChanged);

			playButton = desktop.CreateCheckButton(ClipKind.Pause, ClipKind.Play);
			playButton.Location = new Vector2(GraphicsDevice.Viewport.TitleSafeArea.Left + 32, GraphicsDevice.Viewport.TitleSafeArea.Bottom - 128 - 32);
			playButton.Click += new System.EventHandler(PlayButtonClick);
			stage.IsPlayChanged += new System.EventHandler(stage_IsPlayChanged);
			undoButton = desktop.CreateButton(ClipKind.Undo);
			undoButton.Click += new System.EventHandler(undoButton_Click);
			undoButton.Location = new Vector2(GraphicsDevice.Viewport.TitleSafeArea.Left + 32 + 0x80, GraphicsDevice.Viewport.TitleSafeArea.Bottom - 128 - 32);
			redoButton = desktop.CreateButton(ClipKind.Redo);
			redoButton.Click += new System.EventHandler(redoButton_Click);
			redoButton.Location = new Vector2(GraphicsDevice.Viewport.TitleSafeArea.Left + 32 + 0x100, GraphicsDevice.Viewport.TitleSafeArea.Bottom - 128 - 32);
		}

		void stage_ModeChanged(object sender, System.EventArgs e) {
			switch (stage.Mode) {
			case StageMode.Editor:
				desktop.Enabled = true;
				desktop.Visible = true;
				break;
			case StageMode.Game:
				desktop.Enabled = false;
				desktop.Visible = false;
				break;
			}
		}

		void undoButton_Click(object sender, System.EventArgs e) {
			stageEditor.Undo();
		}
		void redoButton_Click(object sender, System.EventArgs e) {
			stageEditor.Redo();
		}

		void stage_IsPlayChanged(object sender, System.EventArgs e) {
			playButton.Checked = stage.IsPlay;
		}

		void PlayButtonClick(object sender, System.EventArgs e) {
			stage.IsPlay = !stage.IsPlay;
		}

		protected override void UnloadContent() {
		}

		protected override void Update(GameTime gameTime) {
			input.Update();

			base.Update(gameTime);

			stage.Update(gameTime);
			stageEditor.Update();

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

		}

		protected override void Draw(GameTime gameTime) {
			GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer,
				Color.Gray, 1.0f, 0);

			stage.Draw();
			stageEditor.Draw();

			effect.View = camera.Camera.View;
			effect.Projection = camera.Camera.Projection;
			effect.World = camera.Camera.World;

			effect.TextureEnabled = false;

			GraphicsDevice.RenderState.CullMode = CullMode.None;

			if (stage.DebugDraw) {
				world.DrawAllBodies(vectorGraphics.Batch, effect, true);
				world.DrawAllSprings(vectorGraphics.Batch, effect, true);
				world.DrawPointVelocities(vectorGraphics.Batch, effect);
			}

			base.Draw(gameTime);
		}

	}
}
