
/****************************************************************************/
/*Copyright (c) 2011, Florent DEVILLE.                                      */
/*All rights reserved.                                                      */
/*                                                                          */
/*Redistribution and use in source and binary forms, with or without        */
/*modification, are permitted provided that the following conditions        */
/*are met:                                                                  */
/*                                                                          */
/* - Redistributions of source code must retain the above copyright         */
/*notice, this list of conditions and the following disclaimer.             */
/* - Redistributions in binary form must reproduce the above                */
/*copyright notice, this list of conditions and the following               */
/*disclaimer in the documentation and/or other materials provided           */
/*with the distribution.                                                    */
/* - The names of its contributors cannot be used to endorse or promote     */
/*products derived from this software without specific prior written        */
/*permission.                                                               */
/* - The source code cannot be used for commercial purposes without         */
/*its contributors' permission.                                             */
/*                                                                          */
/*THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       */
/*"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         */
/*LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS         */
/*FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE            */
/*COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,       */
/*INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,      */
/*BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;          */
/*LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER          */
/*CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT        */
/*LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN         */
/*ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           */
/*POSSIBILITY OF SUCH DAMAGE.                                               */
/****************************************************************************/

using GE.Visualisation;
using GE.Physics.Shapes;
using GE.Physics;
using GE.Manager;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace GE.World.Entities
{
    /// <summary>
    /// Hard Hat enemy
    /// </summary>
    class HardHatEntity : EnemyEntity
    {
        enum eState
        {
            eStateNone,
            eStateFly,
            eStateShoot,
            eStateCount
        }

        /// <summary>
        /// Hard hat's inner state
        /// </summary>
        eState _innerState;

        /// <summary>
        /// Texture's id
        /// </summary>
        int _iIdTexture;

        /// <summary>
        /// Sprite's id of the hard hat opened
        /// </summary>
        int _iIdSpriteOpened;

        /// <summary>
        /// Sprite's id of the hard hat closed
        /// </summary>
        int _iIdSpriteClosed;

        /// <summary>
        /// Bullet sprites
        /// </summary>
        int _iIdSpriteBullet;

        /// <summary>
        /// Moving speed of the entity
        /// </summary>
        Vector2 _v2Speed;

        /// <summary>
        /// Flag set to true when the state changed
        /// </summary>
        bool _bPreState;

        /// <summary>
        /// Id of the explosion animation
        /// </summary>
        int _iIdAnimationExplosion;

        /// <summary>
        /// The position offset to display the explosion
        /// </summary>
        Vector2 _v2AnimationExplosionOffsetPosition;

        struct structStateFly
        {
            public const int STATE_FLY_LIFE_TIME = 1000;
            public int _iStateFlyBirthTime;

            public void reset()
            {
                _iStateFlyBirthTime = TimeClock.Clock.instance.millisecs;
            }
        }

        struct structStateShoot
        {
            public const int STATE_SHOOT_LIFE_TIME = 1000;
            public const int STATE_SHOOT_HALF_LIFE = STATE_SHOOT_LIFE_TIME /2;
            public int _iStateShootBirthTime;
            public bool _bShoot;

            public void reset()
            {
                _iStateShootBirthTime = TimeClock.Clock.instance.millisecs;
                _bShoot = false;
            }
        }

        /// <summary>
        /// Data used in the fly state
        /// </summary>
        structStateFly _dataStateFly;

        /// <summary>
        /// Data used in the shoot state
        /// </summary>
        structStateShoot _dataStateShoot;

        /// <summary>
        /// Physic shape
        /// </summary>
        //DynamicShapeRectangle _shape;

        /// <summary>
        /// Position to spawn the bullets
        /// </summary>
        Vector2 _v2BulletSpawnOffset;

        /// <summary>
        /// Direction the enemy is facing
        /// </summary>
        int _iDirection;

        /// <summary>
        /// entity birth time
        /// </summary>
        int _iBirthTime;

        /// <summary>
        /// Entity lifetime
        /// </summary>
        int _iLifeTime;

        #region Properties
#if !GAME
        public static string EDITOR_TILESET { get { return "enemysheet.xml"; } }
        public static string EDITOR_SPRITE { get { return "hard_hat_opened"; } }
#endif
        public Vector2 Speed { set { _v2Speed = value; } }
        public int Direction { set { _iDirection = value; } }
        public int LifeTime { set { _iLifeTime = value; } }

        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        public HardHatEntity()
            :base()
        {
            _innerState = eState.eStateNone;
            _iIdTexture = -1;
            _iIdSpriteOpened = -1;
            _iIdSpriteClosed = -1;
            _iIdSpriteBullet = -1;
            _v2Speed = Vector2.Zero;
            _bPreState = true;
            _shape = Physics.Physics.Instance.createDynamicRectangle(0, 0, Vector2.Zero, this);
            _shape._iGroup = (int)ePhysicGroup.ePhysicEnemy;
            _shape._bCollisionEnable = false;
            _dataStateFly = new structStateFly();
            _dataStateShoot = new structStateShoot();
            _iDirection = -1;
            _iIdAnimationExplosion = -1;
            _v2AnimationExplosionOffsetPosition = Vector2.Zero;
        }

        /// <summary>
        /// Initialise the entity. Load all the data which are not loaded during the level loading.
        /// </summary>
        public override void init()
        {
            _iIdTexture = Visu.Instance.loadTilset("enemysheet.xml");
            _iIdSpriteOpened = Visu.Instance.getSpriteId(_iIdTexture, "hard_hat_opened");
            _iIdSpriteClosed = Visu.Instance.getSpriteId(_iIdTexture, "hard_hat_closed");
            _iIdAnimationExplosion = Visu.Instance.getAnimationID("Little_Explosion");
            _iIdSpriteBullet = Visu.Instance.getSpriteId(_iIdTexture, "hard_hat_bullet");

            int iWidth = Visu.Instance.getSpriteWidth(_iIdTexture, _iIdSpriteClosed);
            int iHeight = Visu.Instance.getSpriteHeight(_iIdTexture, _iIdSpriteClosed);
            _shape.resize(iWidth, iHeight);

            _v2AnimationExplosionOffsetPosition = new Vector2(iWidth / 2, iHeight / 2);

            iWidth = Visu.Instance.getSpriteWidth(_iIdTexture, _iIdSpriteOpened);
            iHeight = Visu.Instance.getSpriteHeight(_iIdTexture, _iIdSpriteOpened);
            _v2BulletSpawnOffset = new Vector2(iWidth / 2, iHeight / 2);
         
            base.init();
        }

        /// <summary>
        /// Activator
        /// </summary>
        public override void activate()
        {
            _iBirthTime = TimeClock.Clock.instance.millisecs;
        
            _shape._bCollisionEnable = true;
            _bActive = true;
            _innerState = eState.eStateFly;

            
        }

        /// <summary>
        /// Update
        /// </summary>
        public override void update()
        {
            if (TimeClock.Clock.instance.millisecs > _iBirthTime + _iLifeTime)
                die();

            switch (_innerState)
            {
                case eState.eStateFly:
                    updateStateFly();
                    break;
                case eState.eStateShoot:
                    updateStateShoot();
                    break;
            }

            //check collision
            CollisionResult res = Physics.Physics.Instance.checkFirstRegisteredCollisionEx(_shape, (int)ePhysicGroup.ePhysicPlayer);
            if (res != null)
            {
                res.Entity.hurt(_iDamages);
                die();
            }
        }

        /// <summary>
        /// Update the fly state
        /// </summary>
        private void updateStateFly()
        {
            if (_bPreState)
            {
                _bPreState = false;
                _dataStateFly.reset();
            }

            //check if time elapsed
            if (TimeClock.Clock.instance.millisecs >= _dataStateFly._iStateFlyBirthTime + structStateFly.STATE_FLY_LIFE_TIME)
            {
                _bPreState = true;
                _innerState = eState.eStateShoot;
                return;
            }

            //update position
            _v2Position += _iDirection * _v2Speed;
            _shape._v2position = _v2Position;

        }

        /// <summary>
        /// Update the shoot state
        /// </summary>
        private void updateStateShoot()
        {
            if (_bPreState)
            {
                _bPreState = false;
                _dataStateShoot.reset();
            }

            if (_dataStateShoot._bShoot && TimeClock.Clock.instance.millisecs >= _dataStateShoot._iStateShootBirthTime + structStateShoot.STATE_SHOOT_LIFE_TIME)
            {
                _bPreState = true;
                _innerState = eState.eStateFly;
                return;
            }
            else if (_dataStateShoot._bShoot)
            {
                return;
            }
            else if (TimeClock.Clock.instance.millisecs >= _dataStateShoot._iStateShootBirthTime + structStateShoot.STATE_SHOOT_HALF_LIFE)
            {
                const int bulletSpeed = 4;
                Vector2 spawnPosition = Position + _v2BulletSpawnOffset;
                int lifetime = 1000;

                int distance = 20;
                _dataStateShoot._bShoot = true;
                Vector2 direction = new Vector2(0, -1);
                Vector2 bulletPosition = spawnPosition + direction * distance;
                BulletManager.Instance.activate(_iIdTexture, _iIdSpriteBullet, lifetime, 1, direction * bulletSpeed, bulletPosition, eSide.eSideEnemy);

                direction = new Vector2(1, -1); direction.Normalize();
                bulletPosition = spawnPosition + direction * distance;
                BulletManager.Instance.activate(_iIdTexture, _iIdSpriteBullet, lifetime, 1, direction * bulletSpeed, bulletPosition, eSide.eSideEnemy);

                direction = new Vector2(1, 0);
                bulletPosition = spawnPosition + direction * distance;
                BulletManager.Instance.activate(_iIdTexture, _iIdSpriteBullet, lifetime, 1, direction * bulletSpeed, bulletPosition, eSide.eSideEnemy);

                direction = new Vector2(1, 1); direction.Normalize();
                bulletPosition = spawnPosition + direction * distance;
                BulletManager.Instance.activate(_iIdTexture, _iIdSpriteBullet, lifetime, 1, direction * bulletSpeed, bulletPosition, eSide.eSideEnemy);

                direction = new Vector2(0, 1);
                bulletPosition = spawnPosition + direction * distance;
                BulletManager.Instance.activate(_iIdTexture, _iIdSpriteBullet, lifetime, 1, direction * bulletSpeed, bulletPosition, eSide.eSideEnemy);

                direction = new Vector2(-1, 1); direction.Normalize();
                bulletPosition = spawnPosition + direction * distance;
                BulletManager.Instance.activate(_iIdTexture, _iIdSpriteBullet, lifetime, 1, direction * bulletSpeed, bulletPosition, eSide.eSideEnemy);

                direction = new Vector2(-1, 0);
                bulletPosition = spawnPosition + direction * distance;
                BulletManager.Instance.activate(_iIdTexture, _iIdSpriteBullet, lifetime, 1, direction * bulletSpeed, bulletPosition, eSide.eSideEnemy);

                direction = new Vector2(-1, -1); direction.Normalize();
                bulletPosition = spawnPosition + direction * distance;
                BulletManager.Instance.activate(_iIdTexture, _iIdSpriteBullet, lifetime, 1, direction * bulletSpeed, bulletPosition, eSide.eSideEnemy);
            }
        }

        public override void render()
        {
            SpriteEffects flip = SpriteEffects.None;
            if (_iDirection > 0)
                flip = SpriteEffects.FlipHorizontally;

            switch (_innerState)
            {
                case eState.eStateFly:
                    Visu.Instance.displaySprite(_iIdTexture, _iIdSpriteClosed, ScreenPosition, flip);
                    break;
                case eState.eStateShoot:
                    Visu.Instance.displaySprite(_iIdTexture, _iIdSpriteOpened, ScreenPosition, flip);
                    break;
            }
        }

        public override void die()
        {
            _shape._bCollisionEnable = false;
            _bActive = false;
            Manager.ExplosionManager.Instance.activate(_iIdAnimationExplosion, Position + _v2AnimationExplosionOffsetPosition);
        }

        public override void hurt(int damages)
        {
            if (_innerState == eState.eStateFly) return;

            HP -= damages;
            if (HP <= 0) die();
            //die();
        }
    }
}
