//---------------------------------------------------------------------------------------------------------------------------------------------------
// <copyright file="Bullet.cs" company="DarkWynter Studios">
//     Copyright (C)2007 DarkWynter Studios.  All rights reserved.
// </copyright>
//---------------------------------------------------------------------------------------------------------------------------------------------------
// {Contact : darkwynter.com for licensing information
//---------------------------------------------------------------------------------------------------------------------------------------------------

namespace DarkWynter.Game.GameObjects
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.Content;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Storage;
    using System.Diagnostics;
    using System.Xml;
    #endregion

    using DarkWynter.Engine.GameObjects;
    using DarkWynter.Engine.Globals;
    using DarkWynter.Engine.Physics;
    using DarkWynter.Engine.ObjectLib;
    using GameObjects;
    using DarkWynter.Stream;
    using DarkWynter.Engine.Init;

    /// <summary>
    /// A Bullet
    /// </summary>
    public class Bullet : GameObject
    {
        #region Properties
        /// <summary>
        /// Timer used to identify when to recycle the object
        /// </summary>
        public Stopwatch dissipationTimer = new Stopwatch();

        /// <summary>
        /// Particle-type Enumeration
        /// </summary>
        public Enums_Engine.BulletType bulletType;

        //private ParticleSounds pSounds = new ParticleSounds();

        /// <summary>
        /// A counter for the number of collisions that have occured
        /// </summary>
        public int hasCollided = -1;
        /// <summary>
        /// Stopwatch for recycling the object after it has collided
        /// </summary>
        public Stopwatch hasCollidedTimer = new Stopwatch();

        /// <summary>
        /// ID of the player who fired this bullet
        /// </summary>
        public int ownerID = -1;
        /// <summary>
        /// Boolean check to see if the player was hit by their own bullet
        /// </summary>
        public bool hurtOwner = false;
        private Stopwatch hurtOwnerTimer = new Stopwatch();

        private float waveRadius = 0.0f;
        #endregion

        #region Construction Methods
        /// <summary>
        /// Constructor
        /// </summary>
        public Bullet(Load gameObjectLoader)
            : base(gameObjectLoader)
        {
            mass.objectType = Enums_Engine.ObjectType.PARTICLE;
            mass.gameObjectPointer = this;

            mass.objectHeight = mass.boundingVolume.radius;

            mass.isMoving = true;
            mass.energy = 1.0f;
            waveRadius = 1.0f;
            hasCollided = -1;
            hurtOwner = false;
            hurtOwnerTimer = Stopwatch.StartNew();

            collisionWithPlayerResponse = Enums_Engine.CollisionResponses.HEALTHLOSS;

            switch (bulletType)
            {
                case Enums_Engine.BulletType.Earth:
                    mass.mass = Statics_Engine.ParticleSettings.Earth.EARTH_MASS;
                    mass.COR = Statics_Engine.ParticleSettings.Earth.EARTH_COLD_COR;
                    mass.dynamicFrictionCoefficient = Statics_Engine.ParticleSettings.Earth.EARTH_COLD_FRICTION;
                    break;
                case Enums_Engine.BulletType.Water:
                    mass.mass = Statics_Engine.ParticleSettings.Water.WATER_MASS;
                    mass.COR = Statics_Engine.ParticleSettings.Water.WATER_COLD_COR;
                    mass.dynamicFrictionCoefficient = Statics_Engine.ParticleSettings.Water.WATER_COLD_FRICTION;
                    break;
                case Enums_Engine.BulletType.Air:
                    mass.mass = Statics_Engine.ParticleSettings.Wind.WIND_MASS;
                    mass.COR = Statics_Engine.ParticleSettings.Wind.WIND_COLD_COR;
                    mass.dynamicFrictionCoefficient = Statics_Engine.ParticleSettings.Wind.WIND_COLD_FRICTION;
                    break;
            }
        }

        /// <summary>
        /// Load bullet information from XML
        /// </summary>
        /// <param name="node">The bullet's XML node</param>
        /// <param name="objectLibrary">ObjectLibrary</param>
        /// <returns>True if load was successful, else false</returns>
        public override bool Load(ObjectLibrary objectLibrary)
        {
            // Set up Model Manager
            bool success = LoadModel(load.node);

            // Return unsuccessfully if can't load user supplied XML data
            if (!success) { return false; }

            draw.drawMethod = Enums_Stream.DrawMethod.BasicGameObject_Draw;
            drawBoundingVolume = draw;
            draw.technique = "BasicLightingShaderMain";
            drawBoundingVolume.technique = "ShieldShaderMain";

            // Start Type properties
            objectModelName = load.node.Attributes["name"].Value;

            drawBoundingVolume.textureList.Add(Statics_Engine.SystemSettings.content.Load<Texture2D>("Content/_textures/whitesquare"));
            drawBoundingVolume.textureList.Add(Statics_Engine.SystemSettings.content.Load<Texture2D>("Content/_textures/Wind"));

            mass.scale = new Vector3((float.Parse(load.node.Attributes["maxScale"].Value)));
            mass.mass = float.Parse(load.node.Attributes["mass"].Value);
            mass.isMoving = false;

            isCollectable = bool.Parse(load.node.Attributes["isCollectable"].Value);
            isKey = bool.Parse(load.node.Attributes["isKey"].Value);

            if (isKey)
            {
                collisionWithPlayerResponse = Enums_Engine.CollisionResponses.HEALTHBONUS;
            }

            return true;
        }

        #endregion

        #region Update Methods
        /// <summary>
        /// Update
        /// </summary>
        /// <param name="objectLibrary">ObjectLibrary</param>
        public override void Update(ref ObjectLibrary objectLibrary)
        {
            return;

            mass.Update();
            mass.boundingVolume.UpdateSphere(mass.currentPosition);

            //Update COR based on energy level
            //mass.COR = Statics.ParticleSettings.Wind.WIND_COLD_COR - ((mass.energy / 2.0f) + 0.5f) * (Statics.ParticleSettings.Wind.WIND_COLD_COR - Statics.ParticleSettings.Wind.WIND_HOT_COR);
            //mass.dynamicFrictionCoefficient = Statics.ParticleSettings.Wind.WIND_COLD_FRICTION - ((mass.energy / 2.0f) + 0.5f) * (Statics.ParticleSettings.Wind.WIND_COLD_FRICTION - Statics.ParticleSettings.Wind.WIND_HOT_FRICTION);
            mass.Rotate(0.0f, 0.2f);

            // Send properties and control signal to Shaders
            Vector4 gpu_particleType = new Vector4(0);
            if (this.bulletType == Enums_Engine.BulletType.Earth) { gpu_particleType.Y = 0.0f; }      // Earth = x0xx
            if (this.bulletType == Enums_Engine.BulletType.Water) { gpu_particleType.Y = 1.0f; }      // Water = x1xx
            if (this.bulletType == Enums_Engine.BulletType.Air) { gpu_particleType.Y = 2.0f; }        // Gas   = x2xx
            gpu_particleType.Z = (mass.energy / 2.0f) + 0.5f;                                // Fire  = xxNx
            gpu_particleType.W = waveRadius;

            // Instancing buffers
            //dynamicObjectValues = new VertexFogBinormalTangent();
            //dynamicObjectValues.Fog = new Vector4(mass.scale.Y, gpu_particleType.Y, gpu_particleType.Z, gpu_particleType.W);          // Store the scale
            //dynamicObjectValues.Binormal = new Vector4(mass.currentRotation.X,
            //                                    mass.currentRotation.Y,
            //                                    mass.currentRotation.Z,
            //                                    mass.currentRotation.W);            // Store the quaternion rotation
            //dynamicObjectValues.Tangent = new Vector4(mass.currentPosition, 0.0f);         // Store the translation
        }
        #endregion

        #region Draw Methods
        /// <summary>
        /// Draw the particle object
        /// </summary>
        public override Draw Draw()
        {
            // Calculate ObjectSpace(Rotation) and WorldSpace(Translation) Transformation Matrix
            draw.matrix = draw.initialTransform *
                          Matrix.CreateScale(mass.scale) *
                          Matrix.CreateFromQuaternion(mass.currentRotation) *
                          Matrix.CreateTranslation(mass.currentPosition);

            return draw;
        }

        /// <summary>
        /// Function for debugging to compare the size of the bullet with it's bounding volume
        /// </summary>
        public void DrawBoundingSphere()
        {
            drawBoundingVolume.matrix = drawBoundingVolume.initialTransform *
                                        Matrix.CreateScale(mass.boundingVolume.radius) *
                                        Matrix.CreateFromQuaternion(mass.currentRotation) *
                                        Matrix.CreateTranslation(mass.currentPosition);

            float[] color = { 1, 1, 1, 1 };
            ShaderParameters.DrawFX.materialDiffuse.SetValue(color);

            drawBoundingVolume.DoDraw();
        }
        #endregion

        #region CollisionResponse Methods
        /// <summary>
        /// Add Particle specific responses to an object to object collision involving this and collidedObject
        /// </summary>
        /// <param name="collidedObject">Object colliding with this object</param>
        /// <param name="resultantForce">The resultant force of collision</param>
        /// <param name="objectLibrary">ObjectLibrary</param>
        /// <returns>True = this object needs to be recycled</returns>
        public override bool ObjectCollisionResponse(GameObject collidedObject, Vector3 resultantForce, ObjectLibrary objectLibrary)
        {
            mass.AddForce(resultantForce);

            if (collidedObject.mass.objectType == Enums_Engine.ObjectType.PLAYER)
            {
                // Add Player specific responses to the collided player


                // Remove this object
                return true;
            }

            return false;
        }
        /// <summary>
        /// Add Particle specific responses to an object to terrain collision involving this
        /// </summary>
        /// <param name="terrain">Terrain</param>
        public override void TerrainCollisionResponse(Terrain terrain)
        {
            base.GetObjectHeight(terrain);

            // Add Gravity if terrain and player are touching
            if (heightDifference < 0.0f)
            {
                mass.AddForce(mass.mass * Statics_Engine.GameSettings.accelDueToGravity);
            }
            else if (heightDifference > 0.0f)
            {
                // Call base first
                base.TerrainCollisionResponse(terrain);

                if (combinedNormalForce.Y < 0.0f)
                {
                    mass.AddForce(-combinedNormalForce);
                }
                else
                {
                    mass.AddForce(-velocityNormalComponent);
                }

                mass.AddForce(heightDifference * surfaceNormal * terrain.particleSurfaceTension * mass.COR);

                if (combinedTangentialForce.Length() >= 0.5f)
                {
                    mass.AddForce(mass.mass * -gravityTangentialComponent);

                    mass.AddFriction(mass.dynamicFrictionCoefficient + terrain.mass.dynamicFrictionCoefficient,
                                     (mass.mass * gravityNormalComponent.Length()) + totalForceNormalComponent.Length(),
                                     combinedTangentialForce);
                }
                else
                {
                    mass.AddForce(mass.mass * -velocityTangentialComponent / Statics_Engine.SystemSettings.dt);
                    mass.AddForce(-totalForceTangentialComponent);
                }
            }
        }
        #endregion
    }
}