using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using ShackRPG.HelperClasses;
using TileEngine;

namespace ShackRPG.Enemies
{
    public abstract class BaseEnemy
    {
        int iAttackDamageDelt = 0;
        
        #region System Variables
        Camera camera;
        EnemyAnimation animation;
        #endregion

        #region Animation Variables / Timers
        /// <summary>
        /// Houses the text information until the timers for their
        /// animation has expired
        /// </summary>
        int[] iDamageTaken = new int[100];
        int[] iHealthRecovered = new int[100];

        /// <summary>
        /// Health text animation timers
        /// </summary>
        float[] fHealthTextTimer = new float[100];
        const float fHealthTextTimerReset = 2.0f;

        /// <summary>
        /// Health text animation timers
        /// </summary>
        float[] fDamageTextTimer = new float[100];
        const float fDamageTextTimerReset = 2.0f;

        /// <summary>
        /// Attack timer for time between attacks
        /// </summary>
        float fAttackTimer = 0.0f;
        float fAttackTimerReset = 0.75f;

        /// <summary>
        /// Spell timer time inbetween spells
        /// </summary>
        float fSpellTimer = 0.0f;
        float fSpellTimerReset = 3.0f;

        /// <summary>
        /// Animation timer for time inbetween action commands
        /// </summary>
        float fActionTimer = 0.0f;
        float fActionTimerReset = 0.75f;

        /// <summary>
        /// Bool values used to determine the last direction
        /// player was moving
        /// </summary>
        bool bMoveDown = true, bMoveLeft, bMoveUp, bMoveRight;

        #endregion

        #region Character Specific Variables
        /// <summary>
        /// Strings for the Characters name and title
        /// </summary>
        string sName;               //Characters Name
        string sTitle;              //Characters Title

        /// <summary>
        /// Players stats
        /// </summary>
        int iHealth,            //Current Health
            iHealthMax,         //Max Health for Level
            iMana,              //current mana
            iManaMax,           //max mana for level
            iStrength,          //Current Strength
            iStrengthBase,      //STR not including buffs or +STR items
            iAttackPower,       //current ATK
            iAttackPowerBase,   //ATK not including buffs or +ATK items
            iArmor,             //current Armor
            iArmorBase,         //Armor not including buffs or +Armor items
            iGold,              //bonus gold given when killed
            iLevel,             //Current Level
            iExpBonus;          //bonus exp given when killed

        /// <summary>
        /// Damage and Defend Mods Current formula is
        /// Random int between current level, and level * damage mod
        /// </summary>
        int iAttackDamageMod,
            iDefendDamageMod;

        /// <summary>
        /// Walking speed
        /// </summary>
        float fSpeed = 3.2f;
        float fSpeedReset = 3.2f;

        #endregion

        #region System Variables

        /// <summary>
        /// Path location to the character files
        /// </summary>
        const string CharFilesPath = "/Enemies/";

        /// <summary>
        /// Shows the current sprite to be rendered to screen
        /// </summary>
        Rectangle rCurSpriteToRender;

        /// <summary>
        /// Sprite sheet housing all sprites used to animate
        /// the player object
        /// </summary>
        Texture2D tSpriteSheet;

        /// <summary>
        /// Rectangle used to draw sprite on screen
        /// </summary>
        Rectangle rSpriteRect = new Rectangle(0,0, 0, 0);

        /// <summary>
        /// Profile Picture Rectangles
        /// </summary>
        Rectangle
            rMugShotSource = new Rectangle(0, 0, 40, 40);

        /// <summary>
        /// Players location on the field of play
        /// </summary>
        Vector2 vCurLocation, vPrevLocation;

        #endregion 

        #region Battle Variables
        /// <summary>
        /// List of available Battle Commands
        /// </summary>
        public enum BattleCommands
        {
            Fight,
            Magic,
            Item,
            Defend,
            None,
        }

        /// <summary>
        /// The current command selected by Party Member (Default: None)
        /// </summary>
        BattleCommands 
            curCommand = BattleCommands.None, 
            prevCommand;

        /// <summary>
        /// Current battle status of the character
        /// </summary>
        public BattleStatus 
            curBattleStatus, 
            prevBattleStatus;

        /// <summary>
        /// List of current available battle status
        /// </summary>
        public enum BattleStatus
        {
            Normal,
            Hasted,
            Slowed,
            Poisoned,
            Stopped,
            Silenced,
            Dead,
        }
        #endregion

        #region Properties
        /// <summary>
        /// Returns the bounding box for the sprite
        /// </summary>
        public Rectangle SpriteBoundingBox
        {
            get
            {
                return new Rectangle(
                    (int)Center.X,
                    (int)Center.Y,
                    rSpriteRect.Width / 3,
                    rSpriteRect.Height / 3);
            }
        }

        /// <summary>
        /// Gets and sets the spritesheet associated with
        /// this character
        /// </summary>
        public Texture2D SpriteSheet
        {
            get { return tSpriteSheet; }
            set { tSpriteSheet = value; }
        }

        /// <summary>
        /// The center point of the sprite
        /// </summary>
        public Vector2 Center
        {
            get
            {
                return CurrentLocation + new Vector2(rSpriteRect.Width / 2, rSpriteRect.Height / 2);
            }
        }

        /// <summary>
        /// gets / sets the current width of the sprite
        /// </summary>
        public int SpriteWidth
        {
            get { return rSpriteRect.Width; }
            set { rSpriteRect.Width = value; }
        }

        /// <summary>
        /// gets / sets the current Height of the sprite
        /// </summary>
        public int SpriteHeight
        {
            get { return rSpriteRect.Height; }
            set { rSpriteRect.Height = value; }
        }

        /// <summary>
        /// Sprites current location on the map
        /// </summary>
        public Vector2 CurrentLocation
        {
            get { return vCurLocation; }
            set { vCurLocation = value; }
        }

        /// <summary>
        /// Sprites previous location on the map
        /// </summary>
        public Vector2 PreviousLocation
        {
            get { return vPrevLocation; }
            set { vPrevLocation = vCurLocation; }
        }

        /// <summary>
        /// The last direction the sprite was facing
        /// </summary>
        public string LastDirection
        {
            get
            {
                string direction = "";

                if (bMoveDown == true)
                    direction = "down";
                if (bMoveLeft == true)
                    direction = "left";
                if (bMoveUp == true)
                    direction = "up";
                if (bMoveRight == true)
                    direction = "right";

                return direction;
            }
        }

        /// <summary>
        /// Characters current health
        /// </summary>
        public int Health
        {
            get { return iHealth; }
            set
            {
                iHealth = value;
                if (iHealth <= 0)
                    iHealth = 0;
                if (iHealth >= iHealthMax)
                    iHealth = iHealthMax;
            }
        }

        /// <summary>
        /// Characters max health for its level
        /// </summary>
        public int MaxHealth
        {
            get { return iHealthMax; }
        }

        /// <summary>
        /// Characters level
        /// </summary>
        public int Level
        {
            get { return iLevel; }
            set
            {
                iLevel = value;

                if (iLevel <= 1)
                    iLevel = 1;
            }//end set
        }

        /// <summary>
        /// Characters Exp Bonus given in addition
        /// to the normal Exp
        /// </summary>
        public int Exp
        {
            get { return iExpBonus; }
        }

        /// <summary>
        /// Characters attack damage modifier
        /// </summary>
        public int AttackDamageMod
        {
            get { return iAttackDamageMod; }
        }

        /// <summary>
        /// Characters defense damage modifier
        /// </summary>
        public int DefendDamageMod
        {
            get { return iDefendDamageMod; }
        }

        /// <summary>
        /// Characters armor value
        /// </summary>
        public int Armor
        {
            get { return iArmor; }
            set
            {
                iArmor = value;

                if (iArmor <= 0)
                    iArmor = 0;
            }
        }

        #endregion

        #region Constructor / Initialize Methods
        public BaseEnemy()
        {
        }

        /// <summary>
        /// Sets up the starting character stats
        /// </summary>
        public abstract void SetUpCharacterStats();
        

        /// <summary>
        /// Initializes the character
        /// </summary>
        public void Initialize()
        {
            animation = new EnemyAnimation();
        }

        #endregion

        #region Methods

            #region General Methods
        /// <summary>
        /// Updates all game logic associated with the character
        /// </summary>
        /// <param name="gameTime"></param>
        public abstract void Update(GameTime gameTime);

        /// <summary>
        /// General update logic that all enemies will need to execute
        /// </summary>
        public void Update()
        {
            //Updates the Sprite rectangle to be the same as the players location
            rSpriteRect.X = (int)CurrentLocation.X;
            rSpriteRect.Y = (int)CurrentLocation.Y;
            rSpriteRect.Width = (int)animation.CurrentSprite.Width * 2;
            rSpriteRect.Height = (int)animation.CurrentSprite.Height * 2;

            PreviousLocation = CurrentLocation;
        }

        /// <summary>
        /// Updates all player timers
        /// </summary>
        /// <param name="gameTime">Game Timer</param>
        public void UpdateTimers(GameTime gameTime)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            fAttackTimer -= elapsed;
            fSpellTimer -= elapsed;
            fActionTimer -= elapsed;
        }

        /// <summary>
        /// Checks to see if the player has pressed a movement key
        /// and if so, move the character in the direction intended
        /// </summary>
        /// <param name="gameTime">Game Timer</param>
        public void MoveCharacter(GameTime gameTime, string direction)
        {
            bool 
                Up = false,
                Down = false,
                Left = false,
                Right = false;

            if (direction == "up")
            {
                vCurLocation.Y -= fSpeed;
                bMoveDown = false;
                bMoveLeft = false;
                bMoveUp = true;
                bMoveRight = false;
            }
            else if (direction == "down")
            {
                vCurLocation.Y += fSpeed;
                bMoveDown = true;
                bMoveLeft = false;
                bMoveUp = false;
                bMoveRight = false;
            }
            else if (direction == "left")
            {
                vCurLocation.X -= fSpeed;
                bMoveDown = false;
                bMoveLeft = true;
                bMoveUp = false;
                bMoveRight = false;
            }
            else if (direction == "right")
            {
                vCurLocation.X += fSpeed;
                bMoveDown = false;
                bMoveLeft = false;
                bMoveUp = false;
                bMoveRight = true;
            }

            CheckForCollisions(fSpeed);

            if (PreviousLocation == CurrentLocation)
                animation.StartStandingAnimation(LastDirection);
            else
                animation.UpdateWalkingAnimation(gameTime, LastDirection);
        }

        /// <summary>
        /// Checks for collisions
        /// </summary>
        /// <param name="fTravelSpeed"></param>
        private void CheckForCollisions(float speed)
        {
            Rectangle[,] CollisionLayer = (Rectangle[,])TileEngineV2.rUnwalkable.Clone();
            int height = CollisionLayer.GetLength(0);
            int width = CollisionLayer.GetLength(1);

            Point spriteCell = TileEngineV2.ConvertPositionToCell(Center);

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if (bMoveDown && SpriteBoundingBox.Intersects(CollisionLayer[y, x]))
                    {
                        vCurLocation.Y -= speed;
                    }
                    if (bMoveLeft && SpriteBoundingBox.Intersects(CollisionLayer[y, x]))
                    {
                        vCurLocation.X += speed;
                    }
                    if (bMoveUp && SpriteBoundingBox.Intersects(CollisionLayer[y, x]))
                    {
                        vCurLocation.Y += speed;
                    }
                    if (bMoveRight && SpriteBoundingBox.Intersects(CollisionLayer[y, x]))
                    {
                        vCurLocation.X -= speed;
                    }
                }
            }
        }

        // <summary>
        /// Adds health to the characters current health value
        /// </summary>
        /// <param name="healed">Amount of health to be added</param>
        public void AddHealth(int healed)
        {
            int AddHealth = (int)MathHelper.Max(0, healed);
            Health += AddHealth;

            if (Health >= MaxHealth)
                Health = MaxHealth;

            if (curBattleStatus == BattleStatus.Dead)       //if character was dead and health is added
                curBattleStatus = BattleStatus.Normal;          //they are now alive

            AddScrollingText(0, AddHealth);
        }//end AddHealth

        /// <summary>
        /// Takes physical damage and deals that damage to the character
        /// </summary>
        /// <param name="damage">Amount of physical damage to be taken</param>
        public abstract void TakePhysicalDamage(int damage);

            ////Random Modifier to Defense. This adds a slight random element to the damage received
            ////currently iDamageMod = 5
            //int iBlockedDamage = RandomHelper.GetRandomInt(Level, Level * DefendDamageMod);
            //int iDamageDone = damage - (Armor + iBlockedDamage);  //Determines final Damage Done to player

            //DamageCharacter(iDamageDone);


        /// <summary>
        /// Takes magical damage and deals that damage to the character
        /// </summary>
        /// <param name="damage">Amount of magical damage to be taken</param>
        public abstract void TakeMagicalDamage(int damage);

        /// <summary>
        /// Ajusts the characters health based on the damage received
        /// </summary>
        /// <param name="damage">Amount of damage taken</param>
        private void DamageCharacter(int damage)
        {
            int dmg = (int)MathHelper.Max(0, damage);   //takes the max value so damage can not be 0 or -
            iHealth -= dmg;                             //ajusts the players health

            if (iHealth <= 0)
                Died();

            AddScrollingText(dmg, 0);

        }//end DamagePlayer(damage,healed)

        /// <summary>
        /// Places the character in a Dead state
        /// </summary>
        public void Died()
        {
            curBattleStatus = BattleStatus.Dead;

            //give player exp

            iHealth = 0;
        }

        /// <summary>
        /// Adds scrolling text to the text animation
        /// </summary>
        /// <param name="damage">Damage Text</param>
        /// <param name="healed">Health Text</param>
        /// <param name="manaGained">Mana Gained text</param>
        /// <param name="manaGained">Mana Lost Text</param>
        /// <param name="exp">Exp Text</param>
        /// <param name="gold">Gold Text</param>
        private void AddScrollingText(int damage, int healed)
        {
            #region Damage Text
            if (damage != 0)
            {
                bool added = false;
                //initiates the array that houses the damage taken this round
                for (int i = 0; i < 100; i++)
                {
                    if (!added)
                    {
                        if (iDamageTaken[i] == 0)       //if the current block is empty
                        {
                            iDamageTaken[i] = damage;   //add the dmg to the current block
                            added = true;               //set bool to true, so dmg is ONLY added to current block
                        }
                    }//end !added
                }//end loop
            }//end Damage
            #endregion

            #region Health Text
            if (healed != 0)
            {
                bool added = false;     //ensures the exp text animation only adds one instance of text

                //initiates the exp array for text animation
                for (int i = 0; i < 100; i++)
                {
                    if (!added)
                    {
                        if (iHealthRecovered[i] == 0)       //if current block is empty
                        {
                            iHealthRecovered[i] = healed;   //add value to the current block
                            added = true;                   //set bool to true to ensure only 1 block gets updated
                        }//end if
                    }//end !added
                }//end loop
            }
            #endregion
        }

        /// <summary>
        /// Loads character from file
        /// </summary>
        /// <param name="fileName">Character to load</param>
        public static void LoadCharacter(string fileName)
        {
            using (StreamReader reader = new StreamReader(CharFilesPath + fileName + ".char"))
            {
                while (!reader.EndOfStream)
                {
                    string line = reader.ReadLine();

                    if (string.IsNullOrEmpty(line))
                        continue;

                    //if line contains [something]
                    //else if contains [something else]
                    //else if reading [something]
                }
            }//end using streamreader
        }//end load character

        /// <summary>
        /// Saves character to file
        /// </summary>
        /// <param name="charName">Characters Name</param>
        public static void SaveCharacter(string charName)
        {
            using (StreamWriter writer = new StreamWriter(CharFilesPath + charName + ".char"))
            {
                //write char to file
            }
        }//end save character
            #endregion

            #region Battle Methods
        /// <summary>
        /// Starts attack animation and determines damage delt
        /// </summary>
        /// <param name="gameTime">Game Timer</param>
        /// <returns>Damage Delt in the Attack</returns>
        public abstract int Attack(GameTime gameTime);

        /// <summary>
        /// Puts the character in a Defending state; halting their
        /// movement but raising their armor.
        /// </summary>
        public void Defend()
        {
            animation.Defending(LastDirection);

            iArmor += iLevel * 25;      //sets the ajusted "blocking" armor value
            fSpeed = 0;                 //halts character movement while defending
        }
            #endregion

        public void TestCastSpell()
        {
            if (fSpellTimer <= 0)
            {
                animation.StartSpellAnimation(LastDirection);

                fActionTimer = fActionTimerReset;
                fSpellTimer = fSpellTimerReset;
            }
        }
        #endregion

        #region Draw Methods
        /// <summary>
        /// Draws the character to screen
        /// </summary>
        /// <param name="batch">SpriteBatch</param>
        /// <param name="font">SpriteFont</param>
        /// <param name="gameTime">Game Timer</param>
        public void Draw(SpriteBatch batch, SpriteFont font, GameTime gameTime)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;   //elapsed time for timers

            if (curBattleStatus == BattleStatus.Dead)
                batch.Draw(tSpriteSheet, rSpriteRect, animation.Dead, Color.White);
            else 
                batch.Draw(tSpriteSheet, rSpriteRect, animation.CurrentSprite, Color.White);


            DrawTextAnimations(batch, gameTime, font);
        }

        /// <summary>
        /// Draws the enemy in small form (40 by 40) for easy editor manipulation
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="font"></param>
        public void DrawInEditor(SpriteBatch batch, SpriteFont font)
        {
            batch.Draw(tSpriteSheet,
                new Rectangle(rSpriteRect.X, rSpriteRect.Y, 40, 40),
                animation.CurrentSprite,
                Color.White);
        }

        /// <summary>
        /// Draws the text animations above the characters head
        /// </summary>
        /// <param name="batch">SpriteBatch</param>
        /// <param name="gameTime">Game Timer</param>
        /// <param name="font">SpriteFont</param>
        private void DrawTextAnimations(SpriteBatch batch, GameTime gameTime, SpriteFont font)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            for (int i = 0; i < 100; i++)
            {

                #region Damage Taken Text
                if (iDamageTaken[i] != 0)                //if the current field is not empty
                {
                    fDamageTextTimer[i] -= elapsed;     //update the current fields timer
                    batch.DrawString(font,
                        "-" + iDamageTaken[i],
                        new Vector2(CurrentLocation.X - 35, (CurrentLocation.Y - rSpriteRect.Height + 20) - (20 * fDamageTextTimer[i])),
                        Color.Gold);
                }

                //if the current fields timer reaches the cap
                if (fDamageTextTimer[i] <= 0)
                {
                    iDamageTaken[i] = 0;                            //clear the field
                    fDamageTextTimer[i] = fDamageTextTimerReset;    //stop the timer
                }
                #endregion

                #region Health Recovered Text
                if (iHealthRecovered[i] != 0)           //if the current field is not empty
                {
                    fHealthTextTimer[i] -= elapsed;     //update the current fields timer
                    batch.DrawString(font,
                        "+" + iHealthRecovered[i],
                        new Vector2(CurrentLocation.X - 35, (CurrentLocation.Y - rSpriteRect.Height + 20) - (20 * fHealthTextTimer[i])),
                        Color.Gold);
                }

                if (fHealthTextTimer[i] <= 0) //if current fields timer reaches cap
                {
                    iHealthRecovered[i] = 0;                        //clear the current field
                    fHealthTextTimer[i] = fHealthTextTimerReset;    //reset the current fields timer
                }
                #endregion

            }//end loop
        }
        
        #endregion

    }//end abstract class
}//end namespace
