using UnityEngine;

using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

[System.Serializable]
public class CharacterInfo
{
    public const int EXP_FOR_LEVEL_GAIN = 100;
    
    public string m_name;

    public int m_pendingXP; // Doesn't need to be serialized. Just being lazy...
    public int m_pendingGold; // Doesn't need to be serialized. Just being lazy...
    public int m_enemyIconIndex; // Doesn't need to be serialized. Just being lazy...

    public int m_battleNumber;
    public int m_gold;
    public int m_maxGoldEver;   //< I use this to fill up the shop correctly
    public int m_experience;
    public int m_maxHp;
    public int m_maxMp;
    public int m_currentHp;
    public int m_currentMp;

    public int m_attack;
    public int m_defense;

    public int m_level;     // Ignored for enemies

    public int m_move;      // Squares can move in a turn
    public bool m_canFly;   // Can fly over slow terrain/blocked terrain

    public List<MagicID> m_magic;

    public List<ItemID> m_inventory;
    
    public string ConvertToString()
    {
        try
        {
            MemoryStream memoryStream = new MemoryStream();
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            binaryFormatter.Serialize(memoryStream, this);
            string str = System.Convert.ToBase64String(memoryStream.ToArray());
            return str;
        }
        catch
        {
            return "";
        }
    }
    
    public CharacterInfo SetFromString( string str )
    {
        try
        {
            str = str.Trim();
            byte[] bytes = System.Convert.FromBase64String(str);
            MemoryStream memoryStream = new MemoryStream(bytes);
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            CharacterInfo charInfo = (CharacterInfo)binaryFormatter.Deserialize(memoryStream);
            if ( charInfo != null )
            {
                return charInfo;
            }
            return this;
        }
        catch
        {
            return this;
        }
    }

    public CharacterInfo(string name, int hp, int mp, int atk, int def, int move, bool canFly)
    {
        m_name = name;

        m_battleNumber = 1;
        m_gold = 50;
        m_maxGoldEver = m_gold;
        m_experience = 0;
        m_maxHp = hp;
        m_maxMp = mp;
        m_currentHp = hp;
        m_currentMp = mp;
        m_attack = atk;
        m_defense = def;
        m_move = move;
        m_canFly = canFly;
        m_level = 1;
        m_magic = new List<MagicID>();
        m_inventory = new List<ItemID>();
    }

    public static CharacterInfo MakeCopyOf( CharacterInfo src )
    {
        CharacterInfo newChar = new CharacterInfo(src.m_name, src.m_maxHp, src.m_maxMp, src.m_attack, src.m_defense, src.m_move, src.m_canFly);

        newChar.m_level = src.m_level;
        newChar.m_enemyIconIndex = src.m_enemyIconIndex;
        newChar.m_magic = new List<MagicID>(src.m_magic);
        newChar.m_inventory = new List<ItemID>(src.m_inventory);

        return newChar;
    }

    public bool IsDead
    {
        get { return m_currentHp <= 0;  }
    }

    public void AwardXPAndGold()
    {
        m_gold += m_pendingGold;
        m_experience += m_pendingXP;

        if (m_gold > m_maxGoldEver )
        {
            m_maxGoldEver = m_gold;
        }

        m_pendingGold = 0;
        m_pendingXP = 0;
    }

    public void SpendGold( int amount )
    {
        m_gold -= amount;
        if ( m_gold < 0 )
        {
            m_gold = 0;
        }
    }

    public void Heal( int amount )
    {
        //Debug.Log(m_name + ": Healed for " + amount + " HP");
        m_currentHp += amount;
        if ( m_currentHp > m_maxHp )
        {
            m_currentHp = m_maxHp;
        }
    }

    public void UseMp( int amount )
    {
        //Debug.Log(m_name + ": Used " + amount + " MP");
        m_currentMp -= amount;
        if ( m_currentMp < 0 )
        {
            m_currentMp = 0;
        }
    }

    public bool Damage(int amount)
    {
        //Debug.Log( m_name + ": Damaged by " + amount );
        m_currentHp -= amount;
        if (m_currentHp <= 0)
        {
            m_currentHp = 0;
            return true;
        }
        return false;
    }
    
    public bool CanMeleeAttack
    {
        get 
        {
            return MeleeWeapon != null;
        }
    }

    public bool CanRangedAttack
    {
        get
        {
            return RangedWeapon != null;
        }
    }

    public bool CanMagicAttack
    {
        get
        {
            foreach (MagicID id in m_magic)
            {
                CharacterMagic mag = CharacterMagic.GetMagic(id);
                if (mag.m_damageAmount > 0)
                {
                    return true;
                }
            }
            return false;
        }
    }

    public bool CanMagicHeal
    {
        get
        {
            foreach( MagicID id in m_magic )
            {
                CharacterMagic mag = CharacterMagic.GetMagic(id);
                if (mag.m_healAmount > 0 )
                {
                    return true;
                }
            }
            return false;
        }
    }

    public List<ItemID> Inventory
    {
        get { return m_inventory; }
    }

    public CharacterInfo AddMagic( MagicID id )
    {
        m_magic.Add(id);
        return this;
    }

    public CharacterInfo AddItem( ItemID id )
    {
        m_inventory.Add(id);
        return this;
    }

    public CharacterInfo EnemyIcon( int index )
    {
        m_enemyIconIndex = index;
        return this;
    }

    public int MaxHP
    {
        get { return m_maxHp; }
    }

    public int MaxMP
    {
        get { return m_maxMp; }
    }
    
    public int Attack
    {
        get { return m_attack; }
    }

    public int Movement
    {
        get { return m_move; }
    }
    
    public bool CanFly
    {
        get { return m_canFly; }
    }
    
    public int Defense
    {
        get { return m_defense; }
    }

    public void ResetHPMP()
    {
        m_currentHp = MaxHP;
        m_currentMp = MaxMP;
    }

    public CharacterItem MeleeWeapon
    {
        get
        {
            CharacterItem bestItem = null;
            int bestAttack = 0;
            foreach (ItemID id in m_inventory)
            {
                CharacterItem item = CharacterItem.GetItem(id);
                if (item.IsMeleeWeapon)
                {
                    if (item.m_attackBuff > bestAttack)
                    {
                        bestAttack = item.m_attackBuff;
                        bestItem = item;
                    }
                }
            }
            return bestItem;
        }
    }

    public CharacterItem RangedWeapon
    {
        get
        {
            CharacterItem bestItem = null;
            int bestAttack = 0;
            foreach (ItemID id in m_inventory)
            {
                CharacterItem item = CharacterItem.GetItem(id);
                if (item.IsRangedWeapon)
                {
                    if (item.m_attackBuff > bestAttack)
                    {
                        bestAttack = item.m_attackBuff;
                        bestItem = item;
                    }
                }
            }
            return bestItem;
        }
    }

    public int MeleeAttackValue
    {
        get
        {
            int attack = m_attack;
            if (MeleeWeapon == null)
            {
                return 0;
            }
            attack += MeleeWeapon.m_attackBuff;
            return attack;
        }
    }

    public int RangedAttackValue
    {
        get
        {
            int attack = m_attack;
            if (RangedWeapon == null)
            {
                return 0;
            }
            attack += RangedWeapon.m_attackBuff;
            return attack;
        }
    }

    public int BestMagicAttackValue(out int magicIndex)
    {
        magicIndex = -1;
        {
            int maxDamage = 0;

            int index = 0;
            foreach (MagicID id in m_magic)
            {
                CharacterMagic mag = CharacterMagic.GetMagic(id);
                if ( m_currentMp >= mag.m_mpCost )
                {
                    if (mag.m_damageAmount > maxDamage)
                    {
                        magicIndex = index;
                        maxDamage = mag.m_damageAmount;
                    }
                }
                index++;
            }
                        
            return maxDamage;
        }
    }

    public AttackType PreferredAttackType(int minRange, int maxRange, out int magicIndex)
    {
        {
            // Always prefer magic over everything else, if an enemy can use it; it should try.
            int magic = BestMagicAttackValue(out magicIndex);
            if ( magic > 0 )
            {
                if ( magicIndex >= 0 )
                {
                    CharacterMagic mag = CharacterMagic.GetMagic(m_magic[magicIndex]);

                    if (minRange <= mag.m_range)
                    {
                        // Can get within range to use it!
                        return AttackType.Magic;
                    }
                }
            }

            // NOTE: These values are randomized, so purely estimates here for the AI.
            int meleeDamage = BattleCalculator.CalculateAttackDamage(this, PlayerObject.Instance.Info, true);
            int rangedDamage = BattleCalculator.CalculateAttackDamage(this, PlayerObject.Instance.Info, false);

            if ( CanMeleeAttack == false )
            {
                meleeDamage = 0;
            }
            if (CanRangedAttack == false)
            {
                rangedDamage = 0;
            }

            if ( meleeDamage > rangedDamage )
            {
                // Check can get within range
                if ( minRange == 1 )
                {
                    return AttackType.Melee;
                }
            }
            
            if ( rangedDamage > 0 )
            {
                // Check can get within range
                if ( minRange <= RangedWeapon.m_weaponMaxRange )
                {
                    // Could be right next to the player and not able to move outside to do a ranged attack
                    if ( maxRange > RangedWeapon.m_weaponMinRange )
                    {
                        return AttackType.Ranged;
                    }
                }
            }
        }

        return AttackType.None;
    }


}
