using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

using EVE.EVEThreading;
using EVE.EVEPhysics;

using EVE.EVECameras;

namespace EVE.EVELogic
{
    public class EVEDefaultUpdater : EVEUpdateManager
    {
        private EVEPhysicsEngine physicsEngine;
        Random rand;

        public EVEDefaultUpdater(EVEDoubleBuffer doubleBuffer, EVEGame game)
            : base(doubleBuffer, game)
        {
            rand = new Random();
            physicsEngine = new EVEPhysicsEngine();
        }

        public void updatePhysics(GameTime gameTime)
        {
            foreach (EVEUnit unit in gameLogicObjects.Values)
            {
                if (physicsEngine.updateUnitPhysics(unit, gameTime))
                {

                    //unit.position += Vector3.Multiply(new Vector3((float)rand.NextDouble() * 2.0f - 1.0f, (float)rand.NextDouble() * 2.0f - 1.0f, (float)rand.NextDouble() * 2.0f - 1.0f),(float)Math.Sin((double)gameTime.TotalGameTime.Milliseconds*0.0001f));

                    Matrix newWorldMatrix = unit.rotation * Matrix.CreateTranslation(unit.position);

                    EVEMessage msg = new EVEMessage();
                    msg.ID = unit.uID;
                    msg.MessageType = EVEMessageType.UpdateWorldMatrix;
                    msg.WorldMatrix = newWorldMatrix;
                    messageBuffer.Add(msg);

                    //Console.Out.WriteLine(gameTime.);
                }
            }
        }

        public override void Update(GameTime gameTime)
        {
            if (EVEGlobalManager.currentUID() != 0)
            {
                messageBuffer.Clear();

                HandleAvatarInput(0);

                updatePhysics(gameTime);

                UpdateAvatarCamera(0);

                base.Update(gameTime);
            }
        }

        public override void addUnit(EVEUnit unit)
        {
            physicsEngine.registerPhysicalObject(unit);
            base.addUnit(unit);
        }

        public override void addUnit(int uID, EVEUnit unit)
        {
            physicsEngine.registerPhysicalObject(unit);
            base.addUnit(uID, unit);
        }


        // how quickly the sphere can turn from side to side
        const float CameraTurnSpeed = .1f;
        float cameraFacingDirection;
        readonly Vector3 CameraPositionOffset = new Vector3(0, 10, 15);
        readonly Vector3 CameraTargetOffset = new Vector3(0, 5, 0);

        private void UpdateAvatarCamera(int uID)
        {
            Matrix cameraFacingMatrix = Matrix.CreateRotationY(cameraFacingDirection);
            Vector3 positionOffset = Vector3.Transform(CameraPositionOffset,
                cameraFacingMatrix);
            Vector3 targetOffset = Vector3.Transform(CameraTargetOffset,
                cameraFacingMatrix);

            Vector3 cameraPosition = gameLogicObjects[uID].position + positionOffset;

            Vector3 cameraTarget = gameLogicObjects[0].position + targetOffset;

            EVEMessage msg = new EVEMessage();
            msg.MessageType = EVEMessageType.UpdateCameraView;
            msg.CameraViewMatrix = Matrix.CreateLookAt(cameraPosition,
                                              cameraTarget,
                                              Vector3.Up);

            messageBuffer.Messages.Add(msg);
        }

        GamePadState lastGamePadState;
        private void HandleAvatarInput(int uID)
        {
            GamePadState currentGamePadState = GamePad.GetState(PlayerIndex.One);
            EVEUnit playerBall = gameLogicObjects[uID];

            float turnAmount = -currentGamePadState.ThumbSticks.Left.X;
            // clamp the turn amount between -1 and 1, and then use the finished
            // value to turn the sphere.
            turnAmount = MathHelper.Clamp(turnAmount, -1, 1);
            cameraFacingDirection += turnAmount * CameraTurnSpeed;

            Matrix cameraFacingMatrix = Matrix.CreateRotationY(cameraFacingDirection);

            if ((currentGamePadState.Buttons.A == ButtonState.Pressed))
            {
                Vector3 accel = Vector3.Transform(new Vector3(0, 0, -20), cameraFacingMatrix);
                playerBall.acceleration = accel;
            }
            if (((currentGamePadState.Buttons.Y == ButtonState.Pressed) &&
                (lastGamePadState.Buttons.Y == ButtonState.Released)))
            {
                Vector3 v = Vector3.Transform(new Vector3(0, 0, -50), cameraFacingMatrix);
                playerBall.velocity = v;
            }

            if (((currentGamePadState.Buttons.X == ButtonState.Pressed) &&
                (lastGamePadState.Buttons.X == ButtonState.Released)))
            {
                foreach (EVEUnit sd in gameLogicObjects.Values)
                {
                    if (sd == playerBall)
                        continue;

                    Vector3 randomVel = GetRandomVector3();
                    randomVel.Y = 0;
                    randomVel -= new Vector3(0.5f, 0.0f, 0.5f);
                    sd.velocity = 100 * randomVel;

                }
            }

            
            if (((currentGamePadState.Buttons.B == ButtonState.Pressed) &&
                (lastGamePadState.Buttons.B == ButtonState.Released)))
            {

                int aID = EVEGlobalManager.getUID();
                EVEUnit gd = new EVEUnit(aID);
                addUnit(aID, gd);
                gd.velocity = Vector3.Transform(new Vector3(0, 0, -50), cameraFacingMatrix);
                gd.position = playerBall.position;

                EVEMessage msg = new EVEMessage();
                msg.MessageType = EVEMessageType.CreateNewRenderData;
                msg.ID = aID;
                msg.Position = playerBall.position;
                msg.Color = GetRandomVector3();

                messageBuffer.Add(msg);
            }

            lastGamePadState = currentGamePadState;
        }

        protected Vector3 GetRandomVector3()
        {
            return new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble());
        }

    }
}
