using UnityEngine;
using System.Collections;

public class BattleCalculator 
{
    static public int MAX_XP_TURN = 47;

    static public void HandleDamage(CharacterInfo attacker, CharacterInfo defender, int damage )
    {
        if (defender.IsDead)
        {
            GuiStatusText.AppendText(string.Format("\n{0} killed {1}!", attacker.m_name, defender.m_name));
            attacker.m_pendingGold += GetGoldForKilling(attacker, defender);
        }

        attacker.m_pendingXP += GetXPForDamage(attacker, defender, damage);
    }

    static public int CalculateAttackDamage( CharacterInfo attacker, CharacterInfo defender, bool meleeAttack )
    {
        const float ATTACK_MULTIPLIER_MIN = 0.8f;
        const float ATTACK_MULTIPLIER_MAX = 1.2f;

        int damage = 1;
        int attack = 0;
        if (meleeAttack)
        {
            attack = attacker.MeleeAttackValue;
        }
        else
        {
            attack = attacker.RangedAttackValue;
        }

        if (attack > 0)
        {
            float ratio = (float)attack / (float)defender.Defense;

            damage = (int)Random.Range(ratio * ATTACK_MULTIPLIER_MIN, ratio * ATTACK_MULTIPLIER_MAX);

            int maxDamage = Mathf.Max(defender.m_maxHp * 2, attacker.m_maxHp * 2);
            if (damage > maxDamage)
            {
                damage = maxDamage;
            }
            if (damage < 1)
            {
                damage = 1;
            }
        }

        return damage;
    }
    
    static public void ProcessAttack(CharacterInfo attacker, CharacterInfo defender, bool meleeAttack)
    {
        int damage = CalculateAttackDamage(attacker, defender, meleeAttack);

        defender.Damage(damage);
        GuiStatusText.DisplayText(string.Format("{0} did {1} damage to {2}", attacker.m_name, damage, defender.m_name), GuiStatusText.ATTACK_TIME);

        HandleDamage(attacker, defender, damage);

        // Ha. Melee attack being passed wasn't useless after all!
        if ( meleeAttack )
        {
            SFXManager.Play(SFXManager.Instance.m_meleeHit);
        }
        else
        {
            SFXManager.Play(SFXManager.Instance.m_rangedHit);
        }
    }

    static public void ProcessMagicAttack(CharacterInfo attacker, CharacterInfo defender, MagicID magicId, bool lastHit)
    {
        // TODO : Magic resistance? That is what the magic 'type' field is for, btw.


        CharacterMagic charMagic = CharacterMagic.GetMagic(magicId);
        int damage = (int)Random.Range((0.9f * (float)charMagic.m_damageAmount), (1.1f * (float)charMagic.m_damageAmount));
        
        defender.Damage(damage);

        if (lastHit )
        {
            // AOE could hit several. Deduct just on the last guy hit.
            attacker.UseMp(charMagic.m_mpCost);
        }

        switch ( charMagic.m_type )
        {
            case MagicType.Fire:
                SFXManager.Play(SFXManager.Instance.m_magicFlame);
                break;
            case MagicType.Frost:
                SFXManager.Play(SFXManager.Instance.m_magicFrost);
                break;
            case MagicType.Bolt:
                SFXManager.Play(SFXManager.Instance.m_magicBolt);
                break;
        }

        GuiStatusText.DisplayText(string.Format("{0} did {1} damage to {2}", charMagic.m_name, damage, defender.m_name), GuiStatusText.ATTACK_TIME);

        HandleDamage(attacker, defender, charMagic.m_damageAmount);
    }

    static public void ProcessHealing(CharacterInfo attacker, CharacterInfo defender, MagicID magicId)
    {
        int amount = CharacterMagic.GetMagic(magicId).m_healAmount;
        attacker.m_pendingXP += GetXPForHealing(attacker, amount);
        defender.Heal( amount );
        attacker.UseMp(CharacterMagic.GetMagic(magicId).m_mpCost);

        GuiStatusText.DisplayText(string.Format("{0} healed by {1} HP", defender.m_name, amount), GuiStatusText.ATTACK_TIME);

    }

    static public void ProcessHealing(CharacterInfo attacker, CharacterInfo defender, int itemIndex)
    {
        ItemID itemId = attacker.m_inventory[itemIndex];
        attacker.m_inventory.RemoveAt(itemIndex);
        int amount = CharacterItem.GetItem(itemId).m_healing;
        attacker.m_pendingXP += GetXPForHealing(attacker, amount);
        defender.Heal(amount);

        GuiStatusText.DisplayText(string.Format("{0} healed by {1} HP", defender.m_name, amount), GuiStatusText.ATTACK_TIME);

    }
    
    static public float GetScaledHPDifference(CharacterInfo attacker, CharacterInfo defender)
    {
        float diff = ((attacker.m_maxHp * 0.5f) / (defender.m_maxHp * 1.0f));
        if ( diff < 0.1f )
        {
            return 0.1f;
        }
        if (diff > 1.0f)
        {
            return 1.0f;
        }
        return diff;
    }

    static public int GetXPForKilling( CharacterInfo attacker, CharacterInfo defender)
    {
        // The XP for killing this character
        return Mathf.Max( 1, (int)(GetScaledHPDifference(attacker, defender) * (float)MAX_XP_TURN ));
    }

    static public int GetGoldForKilling(CharacterInfo attacker, CharacterInfo defender)
    {
        return Mathf.Max( 10, (int)((defender.m_maxHp + defender.m_maxMp) * 2.5f) );
    }

    static public int GetXPForHealing(CharacterInfo info, int amount)
    {
        if (info.m_currentHp < info.m_maxHp)
        {
            return 15;
        }
        else
        {
            return 1;
        }
    }

    static public int GetXPForDamage(CharacterInfo attacker, CharacterInfo defender, int damage)
    {
        int xpCap = GetXPForKilling(attacker, defender);

        int xp = (int)(GetScaledHPDifference(attacker, defender) * ((float)damage / (float)defender.m_maxHp) * (float)MAX_XP_TURN);

        if ( xp > xpCap )
        {
            xp = xpCap;
        }
        if (xp < 1)
        {
            xp = 1;
        }

        return xp;
    }
}
