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;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using GroundImporter;
using System.IO;
using System.Reflection;

namespace Junk_XNA
{

    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class mapppingSamplerGame : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Model gldModel;
        Texture2D tex;
        bool useTextureFlg = false;
        HeightMapSetter hMap;
        String modelName = "stage1";

        Model wpModel;

        List<Vector3> addedWp = new List<Vector3>();
        //List<Vector3> addedWpCol = new List<Vector3>();

        List<int> addedWpCol = new List<int>();

        /// <summary>現在のカメラのポジション</summary>
        Vector3 nowCamPos = Vector3.Zero;

        /// <summary>推しっぱなしを防ぐため、1フレーム前の値を保持 </summary>
        GamePadState befState;

        /// <summary>
        /// 描画用フォント1　通常用
        /// </summary>
        public static SpriteFont font_14 = null;

        public enum colID : int
        {
            A = 0,
            B,
            X,
            Y,
            maxval,
        }

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

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            nowCamPos.Y = 3000;
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            gldModel = Content.Load<Model>(modelName);
            hMap = (HeightMapSetter)gldModel.Tag;
            try
            {
                tex = Content.Load<Texture2D>(modelName + "_uv");
                useTextureFlg = true;
            }
            catch
            {
                useTextureFlg = false;
            }
            font_14 = Content.Load<SpriteFont>("fontM");

            wpModel = Content.Load<Model>("waypoint");
            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }


        /// <summary>
        /// Runの直前？直後？に行われる
        /// </summary>
        protected override void BeginRun()
        {
            graphics.PreferredBackBufferWidth = 640;
            graphics.PreferredBackBufferHeight = 640;
            //ここで定義したウィンドウを実際の表示に当てはめてるのかと思う･･･
            graphics.ApplyChanges();
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            //　Backが推されたら、最後に追加したポイントを削除する
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed
                        && befState.Buttons.Back == ButtonState.Released
                )
            {
                //this.Exit();
                if (addedWp.Count > 0)
                {
                    addedWp.RemoveAt(addedWp.Count - 1);
                    addedWpCol.RemoveAt(addedWpCol.Count - 1);
                }
            }

            //え？スティックかい？まずいだろスティックは・・・　
            Vector2 st_L, st_R;

            float fact = 10;

            fact += fact * GamePad.GetState(PlayerIndex.One).Triggers.Right;

            st_L = Vector2.Multiply(GamePad.GetState(PlayerIndex.One).ThumbSticks.Left, fact);
            st_R = Vector2.Multiply(GamePad.GetState(PlayerIndex.One).ThumbSticks.Right, fact);

            nowCamPos.X = nowCamPos.X - st_L.X;
            nowCamPos.Z = nowCamPos.Z + st_L.Y;
            nowCamPos.Y = nowCamPos.Y + st_R.Y;

            Vector3 nowHeightPoint = new Vector3(nowCamPos.X, hMap.GetLandHeight(ref nowCamPos), nowCamPos.Z);
    
            //ボタンが押されたら、ポイントを追加する。各ボタンの色に対応した色で表示する。
            if (GamePad.GetState(PlayerIndex.One).Buttons.X == ButtonState.Pressed
                                      && befState.Buttons.X == ButtonState.Released)
            {
                addedWp.Add(nowHeightPoint);
                addedWpCol.Add((int)colID.X);
            }

            if (GamePad.GetState(PlayerIndex.One).Buttons.Y == ButtonState.Pressed
                                      && befState.Buttons.Y == ButtonState.Released)
            {
                addedWp.Add(nowHeightPoint);
                addedWpCol.Add((int)colID.Y);
            }

            if (GamePad.GetState(PlayerIndex.One).Buttons.B == ButtonState.Pressed
                                      && befState.Buttons.B == ButtonState.Released)
            {
                addedWp.Add(nowHeightPoint);
                addedWpCol.Add((int)colID.B);
            }

            if (GamePad.GetState(PlayerIndex.One).Buttons.A == ButtonState.Pressed
                                      && befState.Buttons.A == ButtonState.Released)
            {
                addedWp.Add(nowHeightPoint);
                addedWpCol.Add((int)colID.A);
            }


            if (GamePad.GetState(PlayerIndex.One).Buttons.Start == ButtonState.Pressed
                                      && befState.Buttons.Start == ButtonState.Released)
            {
                MakeFile();
            }

            befState = GamePad.GetState(PlayerIndex.One);
            base.Update(gameTime);
        }



        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            GraphicsDevice.RenderState.DepthBufferEnable = true;
            GraphicsDevice.RenderState.DepthBufferWriteEnable = true;
            GraphicsDevice.RenderState.AlphaBlendEnable = true;
            GraphicsDevice.RenderState.AlphaTestEnable = false;
            // TODO: Add your drawing code here

            Vector3 viewingPos = new Vector3(nowCamPos.X, 0, nowCamPos.Z);

            Matrix view = Matrix.CreateLookAt(nowCamPos, viewingPos, Vector3.UnitZ);
            Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.Pi / 4, (float)64 / (float)64, 5, 50000);

            foreach (ModelMesh mesh in this.gldModel.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.DiffuseColor = Vector3.One;
                    if (useTextureFlg)
                    {
                        effect.Texture = tex;
                    }
                    effect.TextureEnabled = useTextureFlg;
                    effect.World = Matrix.Identity;

                    // ビューマトリックス設定
                    effect.View = view;

                    // プロジェクションマトリックスめ設定
                    effect.Projection = projection;
                }
                mesh.Draw();
            }

            float nowHeight = hMap.GetLandHeight(ref nowCamPos);

            //現在の高さをあらわすポイントの描画
            foreach (ModelMesh mesh in this.wpModel.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.DiffuseColor = Vector3.One;
                    effect.EmissiveColor = Vector3.One;
                    //effect.Texture = tex;
                    effect.TextureEnabled = false;
                    effect.World = Matrix.Identity * Matrix.CreateTranslation(new Vector3(nowCamPos.X, nowHeight, nowCamPos.Z));

                    // ビューマトリックス設定
                    effect.View = view;

                    // プロジェクションマトリックスめ設定
                    effect.Projection = projection;
                }
                mesh.Draw();
            }

            //追加したWpModelの描画
            int i = 0;
            foreach (Vector3 v in addedWp)
            {
                foreach (ModelMesh mesh in this.wpModel.Meshes)
                {
                    foreach (BasicEffect effect in mesh.Effects)
                    {
                        effect.EnableDefaultLighting();
                        effect.DiffuseColor = colIDtoV3(addedWpCol[i]);
                        effect.EmissiveColor = colIDtoV3(addedWpCol[i]);
                        //effect.Texture = tex;
                        effect.TextureEnabled = false;
                        effect.World = Matrix.Identity * Matrix.CreateTranslation(v);

                        // ビューマトリックス設定
                        effect.View = view;

                        // プロジェクションマトリックス設定
                        effect.Projection = projection;
                    }
                    mesh.Draw();
                }
                i++;
            }

            //文字描画 日本語対応なんてしてない
            spriteBatch.Begin();
            {
                spriteBatch.DrawString(font_14, "nowPos_X:" + nowCamPos.X.ToString(), new Vector2(10, 10), Color.White);
                spriteBatch.DrawString(font_14, "nowPos_Z:" + nowCamPos.Z.ToString(), new Vector2(10, 30), Color.White);

                spriteBatch.DrawString(font_14, "nowPos_Y/Height:" + nowHeight, new Vector2(10, 60), Color.White);
            }
            spriteBatch.End();

            base.Draw(gameTime);
        }

        /// <summary>
        /// ColIDに対応した色のVector3を返す
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        private Vector3 colIDtoV3(int id)
        {
            Vector3 refV = Vector3.One;

            switch (id)
            {
                case (int)colID.A:
                    refV = new Vector3(0, 1, 0); break;
                case (int)colID.B:
                    refV = new Vector3(1, 0, 0); break;
                case (int)colID.X:
                    refV = new Vector3(0, 0, 1); break;
                case (int)colID.Y:
                    refV = new Vector3(1, 1, 0); break;

            }

            return refV;
        }

        /// <summary>
        /// 保存したポイントを出力する
        /// </summary>
        private void MakeFile()
        {
                // ファイルにテキストを書き出し。
            //今のところめんどくさいので、固定パス。
            try
            {
                using (StreamWriter w = new StreamWriter(@"C:\puttexdPoint.txt"))
                {

                    Type type = typeof(colID);
                    FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Static);

                    for (int col = 0; col < (int)colID.maxval; col++)
                    {
                        w.WriteLine("PuttedPoint ColorID = " + fields[col].Name);
                        w.WriteLine("{");
                        for (int i = 0; i < addedWp.Count; i++)
                        {
                            if (addedWpCol[i] == col)
                            {
                                w.WriteLine("   X:" + addedWp[i].X + ";  Y:" + addedWp[i].Y + ";  Z:" + addedWp[i].Z + ";");
                            }
                        }
                        w.WriteLine("}");
                    }
                }
            }
            catch
            {
                //エラー。後は知らん。
            }
        }

    }
}
