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 MikuMikuDance.XNA;
using BulletX.LinerMath;
using MikuMikuDance.XNA.Model;
using BulletX;
using BulletX.BulletCollision.NarrowPhaseCollision;
using BulletX.BulletCollision.CollisionDispatch;
using MikuMikuDance.XNA.Motion;

namespace BulletTest
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        MikuMikuDanceXNA mmdx;
        DefaultDebugDraw debugDraw;
        MMDModel model;
        MMDMotion motion;
#if WINDOWS
        Profiler testProfiler = new Profiler();
#endif
        VertexDeclaration vdec = null;
        VertexPositionColor[] vsurface = null;
        BasicEffect beffect = null;

        PhysicsObj obj;
        List<PhysicsObj> physicobj = new List<PhysicsObj>();
        public Game1()
        {
            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()
        {
            MikuMikuDanceXNA.CheckDevice(graphics, true);
            // TODO: Add your initialization logic here
            mmdx = new MikuMikuDanceXNA(this);
            
            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);

            vdec = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements);
            beffect = new BasicEffect(GraphicsDevice, null);
            beffect.VertexColorEnabled = true;
            vsurface = new VertexPositionColor[3];
            vsurface[0] = new VertexPositionColor(new Vector3(20.0f, 0f, 0f), Color.Green);
            vsurface[1] = new VertexPositionColor(new Vector3(-20.0f, 0f, 30f), Color.Green);
            vsurface[2] = new VertexPositionColor(new Vector3(-20.0f, 0f, -30f), Color.Green);

            model = mmdx.LoadModel("Miku", this, Matrix.Identity);
            motion = mmdx.LoadMotion("TrueMyHeart");
            model.Player.SetMotion(0, motion, true);
            //obj = new BoxObj(this, mmdx.Camera, Content.Load<Model>("Crate"), new btVector3(1, 1, 1), new btTransform(btMatrix3x3.Identity,new btVector3( 5, 50, 0)), mmdx.Physic);
            //obj = new SphereObj(this, mmdx.Camera, Content.Load<Model>("sphere"), new btVector3(1, 1, 1), new btTransform(btMatrix3x3.Identity, new btVector3(0, 10, 0)), mmdx.Physic);
            //obj = new CapsuleObj(this, mmdx.Camera, Content.Load<Model>("capsule"), new Vector3(1, 1, 2), new btTransform(btMatrix3x3.Identity, new btVector3(0, 10, 0)), mmdx.Physic);
            //this.Components.Add(obj);
            //obj.ShowPosition = true;
            
            debugDraw = new DefaultDebugDraw(GraphicsDevice, mmdx.Physic);
#if WINDOWS
            CollisionWorld.Profiler = testProfiler;
#endif
            //ManifoldResult.gContactAddedCallback += new ContactAddedCallback(ManifoldResult_gContactAddedCallback);
            //PersistentManifold.gContactProcessedCallback += new ContactProcessedCallback(ProcessedCallback);
            //obj.PhysicsBody.CollisionFlags |= CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK;
            //mmdx.Ground.CollisionFlags |= CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK;

            //mmdx.Camera.CameraPos = new Vector3(mmdx.Camera.CameraPos.X, mmdx.Camera.CameraPos.Y, -mmdx.Camera.CameraPos.Z);
        }

        /*void ManifoldResult_gContactAddedCallback(ManifoldPoint cp, CollisionObject colObj0, int partId0, int index0, CollisionObject colObj1, int partId1, int index1)
        {
            string str0 = colObj0.UserData != null ? ((int)colObj0.UserData).ToString() : "";
            string str1 = colObj1.UserData != null ? ((int)colObj1.UserData).ToString() : "";
            System.Diagnostics.Debug.Assert(colObj0.CollisionShape.ShapeType != BulletX.BulletCollision.BroadphaseCollision.BroadphaseNativeTypes.CAPSULE_SHAPE_PROXYTYPE
                && colObj1.CollisionShape.ShapeType != BulletX.BulletCollision.BroadphaseCollision.BroadphaseNativeTypes.CAPSULE_SHAPE_PROXYTYPE);
            System.Diagnostics.Debug.WriteLine("CA:" + colObj0.CollisionShape.Name + str0 + "&" + colObj1.CollisionShape.Name + str0);

        }
        void ProcessedCallback(ManifoldPoint cp, CollisionObject body0, CollisionObject body1)
        {
            string str0 = body0.UserData != null ? ((int)body0.UserData).ToString() : "";
            string str1 = body1.UserData != null ? ((int)body1.UserData).ToString() : "";

            System.Diagnostics.Debug.WriteLine("P:"+body0.CollisionShape.Name + str0 + "&" + body1.CollisionShape.Name + str0);

        }*/
        /// <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
        }

        bool mouseFlag = false;
        /// <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)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            Vector3 rayTo = getRayTo(Mouse.GetState().X, Mouse.GetState().Y);
            // TODO: Add your update logic here
            if (Mouse.GetState().LeftButton == ButtonState.Pressed && !mouseFlag)
            {
                shootBox(rayTo);
                mouseFlag = true;
            }
            if (Mouse.GetState().LeftButton == ButtonState.Released)
                mouseFlag = false;
            if ((Keyboard.GetState().IsKeyDown(Keys.Enter) || GamePad.GetState(PlayerIndex.One).Buttons.A == ButtonState.Pressed) && !model.Player.IsPlay(0))
            {
                model.Player.Start(0, false);
            }
            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);
            
            // TODO: Add your drawing code here
            GraphicsDevice.VertexDeclaration = vdec;
            beffect.View = mmdx.Camera.GetViewMatrix();
            beffect.Projection = mmdx.Camera.GetProjectionMatrix(GraphicsDevice);
            // パスの数だけ繰り替えし描画 (といっても BasicEffect は通常１回)
            beffect.Begin();
            for (int pindex = 0; pindex < beffect.Techniques[0].Passes.Count; pindex++)
            {
                EffectPass pass = beffect.Techniques[0].Passes[pindex];
                // パスの開始
                pass.Begin();

                // ポリゴンを描画する
                GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, vsurface, 0, 1);
                // パスの終了
                pass.End();
            }

            // エフェクトの使用を終了する
            beffect.End();
            debugDraw.View = mmdx.Camera.GetViewMatrix();
            debugDraw.Proj = mmdx.Camera.GetProjectionMatrix(GraphicsDevice);
            debugDraw.Draw(GraphicsDevice);
            base.Draw(gameTime);
        }

        private void shootBox(Vector3 destination)
        {

            //float mass = 1f;
            btTransform startTransform = btTransform.Identity;
            Vector3 camPos = mmdx.Camera.CameraPos;
            startTransform.Origin = new btVector3(camPos.X, camPos.Y, camPos.Z);
            //CollisionShape boxShape = new SphereShape(1);

            /*PhysicsObj obj = new BoxObj(this, mmdx.Camera, Content.Load<Model>("Crate"),
                 new btVector3(1, 1, 1),
                 startTransform, mmdx.Physic);*/
            PhysicsObj obj = new SphereObj(this, mmdx.Camera, Content.Load<Model>("Sphere"),
                 new btVector3(1, 1, 1),
                 startTransform, mmdx.Physic);
            //PhysicsObj obj = new CapsuleObj(this, mmdx.Camera, Content.Load<Model>("capsule"), new Vector3(1, 1, 2), startTransform, mmdx.Physic);
            
            Components.Add(obj);
            physicobj.Add(obj);
            obj.PhysicsBody.CollisionFlags |= CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK;
            
            /*BulletX.CollisionShape boxShape = new BulletX.BoxShape(Vector3.One);
            RigidBody body = CreateRigidBody(mass, startTransform, boxShape);*/

            btVector3 linVel = new btVector3(destination.X - camPos.X, destination.Y - camPos.Y, destination.Z - camPos.Z);
            linVel.normalize();
            linVel *= 50f;

            //body.getWorldTransform().setOrigin(camPos);
            //body.getWorldTransform().setRotation(btQuaternion(0, 0, 0, 1));
            obj.PhysicsBody.LinearVelocity = linVel;
            obj.PhysicsBody.AngularVelocity = btVector3.Zero;


        }
        private Vector3 getRayTo(int x, int y)
        {
            Vector3 nearSource = new Vector3(x, y, 0);
            Vector3 farSource = new Vector3(x, y, 1);

            Matrix world = Matrix.CreateTranslation(0, 0, 0);

            Vector3 nearPoint = GraphicsDevice.Viewport.Unproject(nearSource, mmdx.Camera.GetProjectionMatrix(GraphicsDevice), mmdx.Camera.GetViewMatrix(), world);
            Vector3 farPoint = GraphicsDevice.Viewport.Unproject(farSource, mmdx.Camera.GetProjectionMatrix(GraphicsDevice), mmdx.Camera.GetViewMatrix(), world);

            Vector3 direction = farPoint - nearPoint;
            //direction.Normalize();
            return direction;
        }
    }
}
