using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace CameraTest
{
	/// <summary>
	/// 이것은 사용자 게임의 주 형식입니다.
	/// </summary>
	public class Game1 : Microsoft.Xna.Framework.Game
	{
		GraphicsDeviceManager graphics;
		SpriteBatch spriteBatch;

		Camera camera = new Camera ();
		Matrix projection;

		Model model, skybox;
		Heightmap map;

		public Game1 ()
		{
			graphics = new GraphicsDeviceManager ( this );
			Content.RootDirectory = "Content";
		}

		/// <summary>
		/// 게임이 실행되기 전에 처리해야 할 모든 초기화를 실시할 수 있습니다.
		/// 게임은 여기서 필요한 서비스를 질의(쿼리)할 수 있으며 비그래픽
		/// 관련 콘텐츠 또한 로드할 수 있습니다. base.Initialize를 호출하면 모든 구성 요소를 열거함과 동시에
		/// 모두 초기화합니다.
		/// </summary>
		protected override void Initialize ()
		{
			// TODO: 여기에 사용자의 초기화 논리 추가

			float aspectRatio = ( float ) GraphicsDevice.Viewport.Width / GraphicsDevice.Viewport.Height;
			projection = Matrix.CreatePerspectiveFieldOfView ( MathHelper.PiOver4, aspectRatio, 0.0001f, 1000.0f );

			base.Initialize ();
		}

		/// <summary>
		/// LoadContent는 매 게임별로 한 번씩 호출되며 이 메서드에서
		/// 모든 사용자 콘텐츠를 로드합니다.
		/// </summary>
		protected override void LoadContent ()
		{
			// 텍스쳐를 출력하는 데 사용할 수 있는 새 SpriteBatch를 생성하십시오.
			spriteBatch = new SpriteBatch ( GraphicsDevice );

			// TODO: 여기서 this.Content를 이용해서 사용자 콘텐츠를 로드
			model = Content.Load<Model> ( "item_star" );
			skybox = Content.Load<Model> ( "skybox" );

			map = new Heightmap ( GraphicsDevice, Content.Load<Texture2D> ( "heightmap" ) );
		}

		/// <summary>
		/// UnloadContent는 매 게임별로 한 번씩 호출되며 이 메서드에서
		/// 모든 사용자 콘텐츠를 언로드합니다.
		/// </summary>
		protected override void UnloadContent ()
		{
			// TODO: 여기서 모든 비ContentManager 콘텐츠를 언로드
			
		}

		/// <summary>
		/// 게임이 레벨 업데이트, 충돌 감지, 입력 감지 및 오디오 재생과
		/// 같은 게임 논리 코드를 실행할 수 있습니다.
		/// </summary>
		/// <param name="gameTime">타이밍 값의 스냅샷을 제공합니다.</param>
		protected override void Update ( GameTime gameTime )
		{
			// 게임이 종료할 수 있게 함
			if ( GamePad.GetState ( PlayerIndex.One ).Buttons.Back == ButtonState.Pressed )
				this.Exit ();

			camera.Yaw -= GamePad.GetState ( PlayerIndex.One ).ThumbSticks.Right.X / 30;
			camera.Pitch += GamePad.GetState ( PlayerIndex.One ).ThumbSticks.Right.Y / 30;
			if ( GamePad.GetState ( PlayerIndex.One ).DPad.Up == ButtonState.Pressed || Keyboard.GetState ().IsKeyDown ( Keys.W ) )
				camera.Walk ( 0.7f );
			if ( GamePad.GetState ( PlayerIndex.One ).DPad.Down == ButtonState.Pressed || Keyboard.GetState ().IsKeyDown ( Keys.S ) )
				camera.Walk ( -0.7f );
			if ( GamePad.GetState ( PlayerIndex.One ).DPad.Left == ButtonState.Pressed || Keyboard.GetState ().IsKeyDown ( Keys.A ) )
				camera.Strafe ( -0.7f );
			if ( GamePad.GetState ( PlayerIndex.One ).DPad.Right == ButtonState.Pressed || Keyboard.GetState ().IsKeyDown ( Keys.D ) )
				camera.Strafe ( 0.7f );

			// TODO: 여기에 업데이트 논리 추가

			base.Update ( gameTime );
		}

		/// <summary>
		/// 게임이 스스로 갱신해야 할 때 이 메서드를 호출합니다.
		/// </summary>
		/// <param name="gameTime">타이밍 값의 스냅샷을 제공합니다.</param>
		protected override void Draw ( GameTime gameTime )
		{
			GraphicsDevice.Clear ( Color.CornflowerBlue );

			foreach ( ModelMesh m in skybox.Meshes )
			{
				foreach ( BasicEffect e in m.Effects )
				{
					e.View = camera.Matrix;
					e.World = Matrix.CreateScale ( 3 ) * Matrix.CreateTranslation ( 0, -50, 20 );
					e.Projection = projection;
				}

				m.Draw ();
			}

			foreach ( ModelMesh m in model.Meshes )
			{
				foreach ( BasicEffect e in m.Effects )
				{
					e.View = camera.Matrix;
					e.World = Matrix.CreateTranslation ( 70, 70, 70 );
					e.Projection = projection;
				}

				m.Draw ();
			}

			BasicEffect effect = new BasicEffect ( GraphicsDevice );
			effect.View = camera.Matrix;
			effect.World = Matrix.CreateScale ( 0.2f );
			effect.Projection = projection;
			foreach(EffectTechnique et in effect.Techniques)
			{	
				foreach(EffectPass ep in et.Passes)
					ep.Apply();
			}
			map.Draw ();

			base.Draw ( gameTime );
		}
	}
}
