#include "PrecompiledHeader.h"

#include "ThisDescriptor.h"
#include "Counters.h"
#include "MTGCardInstance.h"
#include "CardDescriptor.h"

ThisDescriptor::~ThisDescriptor()
{
    //nothing to do for now
}

//Returns the amount by which a value passes the comparison.
int ThisDescriptor::matchValue(int value)
{
    switch (comparisonMode)
    {
    case COMPARISON_AT_MOST:
        return (comparisonCriterion - value + 1);
    case COMPARISON_AT_LEAST:
        return (value - comparisonCriterion + 1);
    case COMPARISON_EQUAL:
        return (comparisonCriterion == value);
    case COMPARISON_GREATER:
        return (value - comparisonCriterion);
    case COMPARISON_LESS:
        return (comparisonCriterion - value);
    case COMPARISON_UNEQUAL:
        return (comparisonCriterion != value);
    }
    return 0;
}

ThisDescriptor * ThisDescriptorFactory::createThisDescriptor(string s)
{
    size_t found;

    string whitespaces(" \t\f\v\n\r");

    found = s.find_last_not_of(whitespaces);
    if (found != string::npos)
        s.erase(found + 1);
    else
        return NULL;

    found = s.find_first_not_of(whitespaces);
    if (found != string::npos)
        s.erase(0, found);
    else
        return NULL;

    //set comparison mode
    //equal, greater, and less must remain above the others, otherwise the others may never be used.
    int mode = 0;
    size_t found2 = string::npos;
    int opLength = 0;

    found = s.find("=");
    if (found != string::npos)
    {
        mode = COMPARISON_EQUAL;
        found2 = found + 1;
        opLength = 1;
    }
    found = s.find(">");
    if (found != string::npos)
    {
        mode = COMPARISON_GREATER;
        found2 = found + 1;
        opLength = 1;
    }
    found = s.find("<");
    if (found != string::npos)
    {
        mode = COMPARISON_LESS;
        found2 = found + 1;
        opLength = 1;
    }
    found = s.find("<=");
    if (found != string::npos)
    {
        mode = COMPARISON_AT_MOST;
        found2 = found + 2;
        opLength = 2;
    }
    found = s.find(">=");
    if (found != string::npos)
    {
        mode = COMPARISON_AT_LEAST;
        found2 = found + 2;
        opLength = 2;
    }
    found = s.find("!=");
    if (found != string::npos)
    {
        mode = COMPARISON_UNEQUAL;
        found2 = found + 2;
        opLength = 2;
    }
    if (!mode) mode = COMPARISON_AT_LEAST;

    //get comparison criterion
    int criterionFound = 0;
    int criterion = 1;
    if ((found2 != string::npos) && (found2 < s.length()))
    {
        criterion = atoi(s.substr(found2).c_str());
        criterionFound = 1;
    }
    if (found2 != string::npos) s.erase(found2 - opLength);

    //counters
    found = s.find("counter{");
    if (found != string::npos)
    {
        size_t start = s.find("{");
        size_t end = s.find("}");
        string counterString = s.substr(start + 1, end - start - 1);
        AbilityFactory abf;
        Counter * counter = abf.parseCounter(counterString, NULL);
        if (counter)
        {
            if (criterionFound) counter->nb = criterion;
            ThisCounter * td = NEW ThisCounter(counter);
            if (td)
            {
                td->comparisonMode = mode;
                return td;
            }
        }
        return NULL;
    }

    //any counter
    found = s.find("counters");
    if (found != string::npos)
    {
        ThisCounterAny * td = NEW ThisCounterAny(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }

    //equips and auras
    found = s.find("gear");//still same meaning, better wording to word conflict with MTGAbility equip.
    if (found != string::npos)
    {
        ThisEquip * td = NEW ThisEquip(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }
    found = s.find("auras");
    if (found != string::npos)
    {
        ThisAuras * td = NEW ThisAuras(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }
    // opponent damage count
        found = s.find("opponentdamagecount");
    if (found != string::npos)
    {
        ThisOpponentDamageAmount * td = NEW ThisOpponentDamageAmount(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }
    //untapped status
    found = s.find("untapped");
    if (found != string::npos)
    {
        ThisUntapped * td = NEW ThisUntapped(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }
    found = s.find("tapped");
    if (found != string::npos)
    {
        ThisTapped * td = NEW ThisTapped(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }
    //whenever this creature attacks do effect
    found = s.find("attacking");
    if (found != string::npos)
    {
        ThisAttacked * td = NEW ThisAttacked(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }

    //whenever this creature attacks do effect
    found = s.find("notblocked");
    if (found != string::npos)
    {
        ThisNotBlocked * td = NEW ThisNotBlocked(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }

    //this creature was dealt damage this turn
    found = s.find("damaged");
    if (found != string::npos)
    {
        ThisDamaged * td = NEW ThisDamaged(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }
    
    //controller life
    found = s.find("opponentlife");
    if (found != string::npos)
    {
        ThisOpponentlife * td = NEW ThisOpponentlife(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }

    //controller life
    found = s.find("controllerlife");
    if (found != string::npos)
    {
        ThisControllerlife * td = NEW ThisControllerlife(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }

    //power
    found = s.find("power");
    if (found != string::npos)
    {
        ThisPower * td = NEW ThisPower(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }

    //toughness
    found = s.find("toughness");
    if (found != string::npos)
    {
        ThisToughness * td = NEW ThisToughness(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }

    // X
    found = s.find("x");
    if (found != string::npos)
    {
        ThisX * td = NEW ThisX(criterion);
        if (td)
        {
            td->comparisonMode = mode;
            return td;
        }
        return NULL;
    }

    return NULL;
}

ThisCounter::ThisCounter(Counter * _counter)
{
    counter = _counter;
    comparisonCriterion = counter->nb;
}

ThisCounter::ThisCounter(int power, int toughness, int nb, const char * name)
{
    counter = NEW Counter(NULL, name, power, toughness);
    comparisonCriterion = nb;
}

int ThisCounter::match(MTGCardInstance * card)
{
    Counter * targetCounter = card->counters->hasCounter(counter->name.c_str(), counter->power, counter->toughness);
    if (targetCounter)
    {
        return matchValue(targetCounter->nb);
    }
    else
    {
        switch (comparisonMode)
        {
        case COMPARISON_LESS:
            return comparisonCriterion;
        case COMPARISON_AT_MOST:
            return comparisonCriterion + 1;
        case COMPARISON_UNEQUAL:
            if (comparisonCriterion)
                return 1;
            else
                return 0;
        case COMPARISON_EQUAL:
            if (comparisonCriterion)
                return 0;
            else
                return 1;
        default:
            return 0;
        }
    }
}

ThisCounter::~ThisCounter()
{
    SAFE_DELETE(counter);
}

ThisOpponentlife::ThisOpponentlife(int olife)
{
    comparisonCriterion = olife;
}

int ThisOpponentlife::match(MTGCardInstance * card)
{
    return matchValue(card->controller()->opponent()->life);
}

ThisControllerlife::ThisControllerlife(int life)
{
    comparisonCriterion = life;
}

int ThisControllerlife::match(MTGCardInstance * card)
{
    return matchValue(card->controller()->life);
}

ThisPower::ThisPower(int power)
{
    comparisonCriterion = power;
}

int ThisPower::match(MTGCardInstance * card)
{
    return matchValue(card->power);
}

ThisEquip::ThisEquip(int equipment)
{
    comparisonCriterion = equipment;
}
int ThisEquip::match(MTGCardInstance * card)
{
    return matchValue(card->equipment);
}

ThisAuras::ThisAuras(int auras)
{
    comparisonCriterion = auras;
}
int ThisAuras::match(MTGCardInstance * card)
{
    return matchValue(card->auras);
}

ThisOpponentDamageAmount::ThisOpponentDamageAmount(int damagecount)
{
    comparisonCriterion = damagecount;
}
int ThisOpponentDamageAmount::match(MTGCardInstance * card)
{
    return matchValue(card->controller()->opponent()->damageCount);
}

ThisUntapped::ThisUntapped(int untapped)
{
    comparisonCriterion = untapped;
}
int ThisUntapped::match(MTGCardInstance * card)
{
    return matchValue(!card->isTapped());
}

ThisTapped::ThisTapped(int tapped)
{
    comparisonCriterion = tapped;
}
int ThisTapped::match(MTGCardInstance * card)
{
    return matchValue(card->isTapped());
}

ThisAttacked::ThisAttacked(int attack)
{

    comparisonCriterion = attack;
}

int ThisAttacked::match(MTGCardInstance * card)
{

    return matchValue(card->didattacked);
}

ThisNotBlocked::ThisNotBlocked(int unblocked)
{

    comparisonCriterion = unblocked;
}

int ThisNotBlocked::match(MTGCardInstance * card)
{

    return matchValue(card->notblocked);
}

ThisDamaged::ThisDamaged(int wasDealtDamage)
{

    comparisonCriterion = wasDealtDamage;
}

int ThisDamaged::match(MTGCardInstance * card)
{
int result = 0;
if(card->wasDealtDamage)
result = 1;
    return matchValue(result);
}

ThisToughness::ThisToughness(int toughness)
{
    comparisonCriterion = toughness;
}

int ThisToughness::match(MTGCardInstance * card)
{
    return matchValue(card->toughness);
}

ThisCounterAny::ThisCounterAny(int nb)
{
    comparisonCriterion = nb;
}

int ThisCounterAny::match(MTGCardInstance * card)
{
    int result = 0;
    for (int i = 0; i < card->counters->mCount; i++)
    {
        result += card->counters->counters[i]->nb;
    }
    return matchValue(result);
}

ThisX::ThisX(int x)
{
    comparisonCriterion = x;
}

int ThisX::match(MTGCardInstance * card)
{
    return matchValue(card->X);
}
