#include "PrecompiledHeader.h"

#include "GameApp.h"
#include "Player.h"
#include "Tasks.h"
#include "AIPlayer.h"

// Todo remove this dependency!
#include "AIPlayerBaka.h"

#include "Translate.h"
#include "MTGDefinitions.h"
#include <JRenderer.h>
#include <math.h>
#include "utils.h"

vector<string> Task::sAIDeckNames;

/*---------------- Task -----------------*/

Task::Task(char _type)
{
    reward = 0;
    expiresIn = 1;
    accepted = false;
    if (_type == ' ')
    {
        type = TASK_BASIC;
    }
    else
    {
        type = _type;
    }
}

int Task::getReward()
{
    if (reward == 0)
    {
        reward = computeReward();
    }

    return reward;
}

string Task::toString()
{
    storeCommonAttribs();
    storeCustomAttribs();
    return join(persistentAttribs, ITEM_SEPARATOR);
}

// Store basic attributes to vector, for saving
void Task::storeCommonAttribs()
{
    char buff[256];

    persistentAttribs.clear();
    persistentAttribs.push_back(string(1, type));

    sprintf(buff, "%i", expiresIn);
    persistentAttribs.push_back(string(buff));

    sprintf(buff, "%i", accepted ? 1 : 0);
    persistentAttribs.push_back(string(buff));

    sprintf(buff, "%i", opponent);
    persistentAttribs.push_back(string(buff));

    sprintf(buff, "%i", reward);
    persistentAttribs.push_back(string(buff));

    persistentAttribs.push_back(getDesc());

    persistentAttribs.push_back(getOpponentName());
}

int Task::restoreCommonAttribs()
{
    if (persistentAttribs.size() < COMMON_ATTRIBS_COUNT)
    {
#if defined (WIN32) || defined (LINUX)
        DebugTrace("\nTasks.cpp::restoreCommonAttribs: Not enough attributes loaded\n");
#endif

        return -1;
    }
    expiresIn = atoi(persistentAttribs[1].c_str());
    accepted = (persistentAttribs[2].compare("1") == 0);
    opponent = atoi(persistentAttribs[3].c_str());
    reward = atoi(persistentAttribs[4].c_str());
    description = persistentAttribs[5];
    opponentName = persistentAttribs[6];
    return 1;
}

void Task::storeCustomAttribs()
{
    // To be extended in child class
}

void Task::restoreCustomAttribs()
{
    // To be extended in child class
}

string Task::getOpponentName()
{
    if (opponentName == "")
    {
        opponentName = Task::getAIDeckName(opponent);
    }

    return opponentName;
}

string Task::getDesc()
{
    return (description == "") ? (description = createDesc()) : description;
}

void Task::randomize()
{
    opponent = rand() % getAIDeckCount() + 1;
    opponentName = "";
    setExpiration((rand() % 3) + 1);
    reward = computeReward();
}

bool Task::isExpired()
{
    return (expiresIn <= 0);
}

int Task::getExpiration()
{
    return expiresIn;
}

void Task::setExpiration(int _expiresIn)
{
    expiresIn = _expiresIn;
}

void Task::passOneDay()
{
    expiresIn--;
    reward = (int) (getReward() * 0.9); // Todo: degradation and minreward constants
    if (reward < 33)
    {
        reward = 33;
    }
}

// AI deck buffering code

void Task::LoadAIDeckNames()
{
    if (sAIDeckNames.empty())
    {
        int found = 1;
        int nbDecks = 0;
        while (found)
        {
            found = 0;
            std::ostringstream stream;
            stream << "ai/baka" << "/deck" << nbDecks + 1 << ".txt";
            if (fileExists(stream.str().c_str()))
            {
                found = 1;
                nbDecks++;
                // TODO: Creating MTGDeck only for getting decks name. Find an easier way.
                MTGDeck * mtgd = NEW MTGDeck(stream.str().c_str(), NULL, 1);
                sAIDeckNames.push_back(mtgd->meta_name);
                delete mtgd;
            }
        }
    }
}

int Task::getAIDeckCount()
{
    LoadAIDeckNames();
    return MIN((size_t) options[Options::AIDECKS_UNLOCKED].number, sAIDeckNames.size());
}

string Task::getAIDeckName(int id)
{
    LoadAIDeckNames();
    return ((unsigned int) id <= sAIDeckNames.size()) ? sAIDeckNames.at(id - 1) : "<Undefined>";
}

// End of AI deck buffering code

// Each child class has to be added to the switch in this function (clumsy..)
Task* Task::createFromStr(const string params, bool rand)
{
    vector<string> exploded;
    Task *result;

    split(params, ITEM_SEPARATOR[0], exploded);

    switch (exploded[0][0])
    {
    case TASK_WIN_AGAINST:
        result = NEW TaskWinAgainst();
        break;
    case TASK_SLAUGHTER:
        result = NEW TaskSlaughter();
        break;
    case TASK_DELAY:
        result = NEW TaskDelay();
        break;
    case TASK_IMMORTAL:
        result = NEW TaskImmortal();
        break;
    case TASK_MASSIVE_BURIAL:
        result = NEW TaskMassiveBurial();
        break;
    case TASK_WISDOM:
        result = NEW TaskWisdom();
        break;
    case TASK_PACIFISM:
        result = NEW TaskPacifism();
        break;
    default:
#if defined (WIN32) || defined (LINUX)
        DebugTrace("\nTasks.cpp::createFromStr: Undefined class type\n");
#endif
        result = NEW TaskWinAgainst();
    }

    if (!result)
    {
#if defined (WIN32) || defined (LINUX)
        DebugTrace("\nTask::createFromStr: Failed to create task\n");
#endif
        return NULL;
    }

    result->persistentAttribs = exploded;

    if (exploded.size() >= COMMON_ATTRIBS_COUNT)
    {
        result->restoreCommonAttribs();
        if (exploded.size() > COMMON_ATTRIBS_COUNT)
        {
            result->restoreCustomAttribs();
        }
    }
    else if (rand)
    {
        result->randomize();
    }

    return result;
}

/*---------------- TaskList -----------------*/

TaskList::TaskList(string _fileName):
    fileName(_fileName), vPos(-SCREEN_HEIGHT), vPosInEasing(vPos), vPosOutEasing(vPos)
{

    if (fileName == "")
    {
        fileName = options.profileFile(PLAYER_TASKS).c_str();
    }
    load(fileName);
    for (int i = 0; i < 9; i++)
        mBg[i] = NULL;
    mBgTex = NULL; //We only load the background if we use the task screen.
}

int TaskList::save(string _fileName)
{
    if (_fileName != "")
    {
        fileName = _fileName;
    }
    if (fileName == "")
    {
#if defined (WIN32) || defined (LINUX)
        DebugTrace("\nTaskList::save: No filename specified\n");
#endif
        return -1;
    }

    std::ofstream file;
    if (JFileSystem::GetInstance()->openForWrite(file, fileName))
    {
#if defined (WIN32) || defined (LINUX)
        DebugTrace("\nsaving tasks\n");
#endif

        file << "# Format: <Type>|<Expiration>|<Accepted>|<Opponent>|<Reward>|<Description>[|Additional attributes]\n";

        for (vector<Task*>::iterator it = tasks.begin(); it != tasks.end(); it++)
        {
            file << (*it)->toString() << "\n";
        }
        file.close();
    }

    return 1;
}

int TaskList::load(string _fileName)
{
    Task *task;
    if (_fileName != "")
    {
        fileName = _fileName;
    }
    if (fileName == "")
    {
        DebugTrace("\nTaskList::load: No filename specified\n");
        return -1;
    }

    std::string contents;
    if (!JFileSystem::GetInstance()->readIntoString(fileName, contents))
    {
        DebugTrace("\nTaskList::load: Failed to open file\n");
        return -1;
    }

    std::stringstream stream(contents);
    std::string s;
    while (std::getline(stream, s))
    {
        if (!s.size()) continue;
        if (s[s.size() - 1] == '\r') s.erase(s.size() - 1); //Handle DOS files
        if (s[0] == '#')
        {
            continue;
        }

        task = Task::createFromStr(s);
        if (task)
        {
            this->addTask(task);
        }
        else
        {
            DebugTrace("\nTaskList::load: error creating task\n");
        }
    }

    return 1;
}

void TaskList::addTask(Task *task)
{
    tasks.push_back(task);
}

void TaskList::addTask(string params, bool rand)
{
    addTask(Task::createFromStr(params, rand));
}

void TaskList::removeTask(Task *task)
{
    vector<Task*>::iterator it;

    it = find(tasks.begin(), tasks.end(), task);

    if (it != tasks.end())
    {
        SAFE_DELETE(*it);
        tasks.erase(it);
    }
    else
    {
        // TODO: task not found handling.
    }
}

void TaskList::Start()
{
    mState = TASKS_IN;
    vPos = -SCREEN_HEIGHT; //Offscreen
    vPosInEasing.start(0.0f, 1.0f);

    if (!mBgTex)
    {
        mBgTex = WResourceManager::Instance()->RetrieveTexture("taskboard.png", RETRIEVE_LOCK);
        float unitH = static_cast<float> (mBgTex->mHeight / 4);
        float unitW = static_cast<float> (mBgTex->mWidth / 4);
        if (unitH == 0 || unitW == 0) return;

        for (int i = 0; i < 9; i++)
            SAFE_DELETE(mBg[i]);
        if (mBgTex)
        {
            mBg[0] = NEW JQuad(mBgTex, 0, 0, unitW, unitH);
            mBg[1] = NEW JQuad(mBgTex, unitW, 0, unitW * 2, unitH);
            mBg[2] = NEW JQuad(mBgTex, unitW * 3, 0, unitW, unitH);
            mBg[3] = NEW JQuad(mBgTex, 0, unitH, unitW, unitH * 2);
            mBg[4] = NEW JQuad(mBgTex, unitW, unitH, unitW * 2, unitH * 2);
            mBg[5] = NEW JQuad(mBgTex, unitW * 3, unitH, unitW, unitH * 2);
            mBg[6] = NEW JQuad(mBgTex, 0, unitH * 3, unitW, unitH);
            mBg[7] = NEW JQuad(mBgTex, unitW, unitH * 3, unitW * 2, unitH);
            mBg[8] = NEW JQuad(mBgTex, unitW * 3, unitH * 3, unitW, unitH);
        }

        sH = 64 / unitH;
        sW = 64 / unitW;
    }
}
void TaskList::End()
{
    mState = TASKS_OUT;
    vPosOutEasing.start(float(-SCREEN_HEIGHT), 0.9f);
}

void TaskList::passOneDay()
{
    // TODO: "You have failed the task" message to the user when accepted task expires
    for (vector<Task*>::iterator it = tasks.begin(); it != tasks.end();)
    {
        (*it)->passOneDay();
        if ((*it)->isExpired())
        {
            SAFE_DELETE(*it);
            it = tasks.erase(it);
        }
        else
        {
            it++;
        }
    }
}

void TaskList::getDoneTasks(GameObserver* observer, GameApp * _app, vector<Task*>* result)
{
    result->clear();
    // TODO: Return only accepted tasks
    for (vector<Task*>::iterator it = tasks.begin(); it != tasks.end(); it++)
    {
        if ((*it)->isDone(observer, _app))
        {
            result->push_back(*it);
        }
    }
}

int TaskList::getTaskCount()
{
    return tasks.size();
}

void TaskList::Update(float dt)
{
    if(!vPosInEasing.finished())
    {
        vPosInEasing.update(dt);

        if(vPosInEasing.finished())
        {
            mState = TaskList::TASKS_ACTIVE;
        }
    }
    else if(!vPosOutEasing.finished())
    {
        vPosOutEasing.update(dt);

        if(vPosOutEasing.finished())
        {
            mState = TASKS_INACTIVE;
        }
    }
}

void TaskList::Render()
{
    JRenderer * r = JRenderer::GetInstance();
    //Setup fonts.
    WFont * f = WResourceManager::Instance()->GetWFont(Fonts::MAIN_FONT);
    WFont * f2 = WResourceManager::Instance()->GetWFont(Fonts::MAGIC_FONT);
    WFont * f3 = WResourceManager::Instance()->GetWFont(Fonts::MENU_FONT); //OPTION_FONT
    f2->SetColor(ARGB(255, 205, 237, 240));
    f3->SetColor(ARGB(255, 219, 206, 151));

    //Render background board
    if (mBgTex)
    {
        r->FillRect(0, vPos, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(128,0,0,0));
        r->RenderQuad(mBg[0], 0, vPos, 0, sW, sH); //TL
        r->RenderQuad(mBg[2], SCREEN_WIDTH - 64, vPos, 0, sW, sH); //TR
        r->RenderQuad(mBg[6], 0, vPos + SCREEN_HEIGHT - 64, 0, sW, sH); //BL
        r->RenderQuad(mBg[8], SCREEN_WIDTH - 64, vPos + SCREEN_HEIGHT - 64, 0, sW, sH); //BR

        //Stretch the sides
        float stretchV = (144.0f / 128.0f) * sH;
        float stretchH = (176.0f / 128.0f) * sW;
        r->RenderQuad(mBg[3], 0, vPos + 64, 0, sW, stretchV); //L
        r->RenderQuad(mBg[5], SCREEN_WIDTH - 64, vPos + 64, 0, sW, stretchV); //R
        r->RenderQuad(mBg[1], 64, vPos, 0, stretchH, sH); //T1
        r->RenderQuad(mBg[1], 240, vPos, 0, stretchH, sH); //T1
        r->RenderQuad(mBg[7], 64, vPos + 208, 0, stretchH, sH); //B1
        r->RenderQuad(mBg[7], 240, vPos + 208, 0, stretchH, sH); //B1
        r->RenderQuad(mBg[4], 64, vPos + 64, 0, stretchH, stretchV); //Center1
        r->RenderQuad(mBg[4], 240, vPos + 64, 0, stretchH, stretchV); //Center2

        f2->SetColor(ARGB(255, 55, 46, 34));
        f = f2;
    }
    else
    {
        r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(128,0,0,0));
        r->FillRect(10, 10 + vPos, SCREEN_WIDTH - 10, SCREEN_HEIGHT - 10, ARGB(128,0,0,0));
    }

    float posX = 40, posY = vPos + 20;
    char buffer[300];
    string title = _("Task Board");

    f3->DrawString(title.c_str(), static_cast<float> ((SCREEN_WIDTH - 20) / 2 - title.length() * 4), posY);
    posY += 30;

    if (0 == tasks.size())
    {
        f->DrawString(_("There are no tasks that need to be done. Come again tomorrow.").c_str(), posX, posY);
        return;
    }

    for (vector<Task*>::iterator it = tasks.begin(); it != tasks.end(); it++)
    {
        sprintf(buffer, "%s", (*it)->getShortDesc().c_str());
        f2->DrawString(buffer, posX, posY);
        if (mBgTex)
        {
            f->SetScale(.8f);
        }
        sprintf(buffer, _("Days left: %i").c_str(), (*it)->getExpiration());
        f->DrawString(buffer, SCREEN_WIDTH - 190, posY);
        sprintf(buffer, _("Reward: %i").c_str(), (*it)->getReward());
        f->DrawString(buffer, SCREEN_WIDTH - 100, posY);
        posY += 15;

        sprintf(buffer, "%s", (*it)->getDesc().c_str());
        f->DrawString(buffer, posX + 10, posY);
        posY += 15;
        if (mBgTex)
        {
            f->SetScale(1);
        }
        //r->DrawLine((SCREEN_WIDTH)/2 - 200, posY, (SCREEN_WIDTH)/2 + 200, posY, ARGB(128, 255, 255, 255));
    }
    f->SetScale(1);
}

void TaskList::addRandomTask(int)
{
    // TODO: Weighted random (rarity of tasks)
    //       - based on counts of finished tasks?
    //         Winning a task several times may slightly lessen the probability of it being generated
    string s(TASKS_ALL);
    char taskType[2];
    sprintf(taskType, "%c", s[rand() % s.length()]);
    addTask(string(taskType), true);
}

TaskList::~TaskList()
{

    for (unsigned int i = 0; i < tasks.size(); i++)
    {
        SAFE_DELETE(tasks[i]);
    }
    if (mBgTex) WResourceManager::Instance()->Release(mBgTex);
    for (int i = 0; i < 9; i++)
        SAFE_DELETE(mBg[i]);
}

/*----------------------------------------*/
/*---------------- Tasks -----------------*/
/*----------------------------------------*/

/*----------- TaskWinAgainst -------------*/

TaskWinAgainst::TaskWinAgainst(int _opponent) :
    Task(TASK_WIN_AGAINST)
{
    opponent = _opponent;
}

int TaskWinAgainst::computeReward()
{
    return 75 + rand() % 75;
}

string TaskWinAgainst::createDesc()
{
    char buffer[4096];
    switch (rand() % 2)
    {
    case 0:
        sprintf(buffer, _("You have to defeat %s before it causes too much harm.").c_str(), getOpponentName().c_str());
        break;
    case 1:
        sprintf(buffer, _("Please defeat %s as soon as possible.").c_str(), getOpponentName().c_str());
        break;
    }
    return buffer;
}

string TaskWinAgainst::getShortDesc()
{
    char buffer[4096];
    string result;

    sprintf(buffer, _("Defeat %s").c_str(), getOpponentName().c_str());
    result = buffer;

    return result;
}

bool TaskWinAgainst::isDone(GameObserver* observer, GameApp *)
{
    AIPlayerBaka * baka = (AIPlayerBaka*) observer->players[1];
    return ((baka) && (!observer->players[0]->isAI()) && (observer->players[1]->isAI()) && (observer->didWin(observer->players[0])) // Human player wins
                    && (baka->deckId == opponent));
}

/*----------- TaskSlaughter -------------*/

TaskSlaughter::TaskSlaughter(int _opponent, int _targetLife) :
    TaskWinAgainst(_opponent)
{
    type = TASK_SLAUGHTER;
    targetLife = _targetLife;
}

int TaskSlaughter::computeReward()
{
    return 2 * TaskWinAgainst::computeReward() - 9 * targetLife * (targetLife < -50 ? 2 : 1);
}

void TaskSlaughter::randomize()
{
    targetLife = -15 - rand() % 10;
    if (!(rand() % 7))
    {
        targetLife *= 5;
    }
    Task::randomize();
}

string TaskSlaughter::createDesc()
{
    char buffer[4096];
    switch (rand() % 2)
    {
    case 0:
        sprintf(buffer, _("Defeat %s in a way it won't forget. Bring it to %i life.").c_str(), getOpponentName().c_str(),
                        targetLife);
        break;
    case 1:
        sprintf(buffer, _("Slaughter %s! Beat it to %i life or less.").c_str(), getOpponentName().c_str(), targetLife);
        break;
    }
    return buffer;
}

string TaskSlaughter::getShortDesc()
{
    char buffer[4096];
    sprintf(buffer, _("Slaughter %s (%i life)").c_str(), getOpponentName().c_str(), targetLife);
    return buffer;
}

bool TaskSlaughter::isDone(GameObserver* observer, GameApp * _app)
{
    return TaskWinAgainst::isDone(observer, _app) && (observer->players[1]->life <= targetLife);
}

void TaskSlaughter::storeCustomAttribs()
{
    char buff[256];

    sprintf(buff, "%i", targetLife);
    persistentAttribs.push_back(buff);
}

void TaskSlaughter::restoreCustomAttribs()
{
    targetLife = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT].c_str());
}

/*----------- TaskDelay -------------*/
// Now serves as both 'Delay' and 'Fast defeat' task.

TaskDelay::TaskDelay(int _opponent, int _turn) :
    TaskWinAgainst(_opponent)
{
    type = TASK_DELAY;
    turn = _turn;
    afterTurn = true;
}

int TaskDelay::computeReward()
{
    return TaskWinAgainst::computeReward() + (afterTurn ? turn * 33 : (17 - turn) * (17 - turn) * 17);
}

void TaskDelay::randomize()
{
    afterTurn = rand() % 2 == 1;
    turn = afterTurn ? rand() % 15 + 20 : 15 - rand() % 9;
    Task::randomize();
}

string TaskDelay::createDesc()
{
    char buffer[4096];
    if (afterTurn)
    {
        switch (rand() % 2)
        {
        case 0:
            sprintf(buffer, _("Defeat %s after keeping it occupied for %i turns.").c_str(), getOpponentName().c_str(), turn);
            break;
        case 1:
            sprintf(buffer, _("Defeat %s, but play with it for %i turns first.").c_str(), getOpponentName().c_str(), turn);
            break;
        }
    }
    else
    {
        switch (rand() % 2)
        {
        case 0:
            sprintf(buffer, _("Defeat %s and make sure it doesn't take more than %i turns.").c_str(), getOpponentName().c_str(),
                            turn);
            break;
        case 1:
            sprintf(buffer, _("Defeat %s, in a duel no longer than %i turns.").c_str(), getOpponentName().c_str(), turn);
            break;
        }
    }
    return buffer;
}

string TaskDelay::getShortDesc()
{
    char buffer[4096];
    if (afterTurn)
    {
        sprintf(buffer, _("Delay %s for %i turns").c_str(), getOpponentName().c_str(), turn);
    }
    else
    {
        sprintf(buffer, _("Defeat %s before turn %i").c_str(), getOpponentName().c_str(), turn + 1);
    }
    return buffer;
}

bool TaskDelay::isDone(GameObserver* observer, GameApp * _app)
{
    return TaskWinAgainst::isDone(observer, _app) && (afterTurn ? (observer->turn >= turn) : (observer->turn <= turn));
}

void TaskDelay::storeCustomAttribs()
{
    char buff[256];

    sprintf(buff, "%i", turn);
    persistentAttribs.push_back(buff);
    sprintf(buff, "%i", afterTurn);
    persistentAttribs.push_back(buff);
}

void TaskDelay::restoreCustomAttribs()
{
    turn = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT].c_str());
    if (persistentAttribs.size() > COMMON_ATTRIBS_COUNT + 1)
    {
        afterTurn = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT + 1].c_str()) > 0;
    }
}

/* ------------ TaskImmortal ------------ */

TaskImmortal::TaskImmortal(int _targetLife) :
    Task(TASK_IMMORTAL)
{
    targetLife = _targetLife;
    level = (targetLife < 100) ? 0 : ((targetLife < 1000) ? 1 : 2);
}

int TaskImmortal::computeReward()
{
    return targetLife * 2 + 150 + rand() % 50;
}

string TaskImmortal::createDesc()
{
    char buffer[4096];

    sprintf(buffer, _("Defeat any opponent, having at least %i life in the end.").c_str(), targetLife);

    return buffer;
}

string TaskImmortal::getShortDesc()
{
    char buffer[4096];

    switch (level)
    {
    case 0:
        sprintf(buffer, _("Win flawlessly (%i life)").c_str(), targetLife);
        break;
    case 1:
        sprintf(buffer, _("Reach Invulnerability (%i life)").c_str(), targetLife);
        break;
    case 2:
        sprintf(buffer, _("Reach Immortality! (%i life)").c_str(), targetLife);
        break;
    }

    return buffer;
}

bool TaskImmortal::isDone(GameObserver *observer, GameApp *)
{
    return (!observer->players[0]->isAI()) && (observer->players[1]->isAI()) && (observer->didWin(observer->players[0])) // Human player wins
                    && (observer->players[0]->life >= targetLife);
}

void TaskImmortal::storeCustomAttribs()
{
    char buff[256];

    sprintf(buff, "%i", targetLife);
    persistentAttribs.push_back(buff);

    sprintf(buff, "%i", level);
    persistentAttribs.push_back(buff);
}

void TaskImmortal::restoreCustomAttribs()
{
    targetLife = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT].c_str());
    level = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT + 1].c_str());
}

void TaskImmortal::randomize()
{
    level = rand() % 3;
    switch (level)
    {
    case 0:
        targetLife = 20 + rand() % 10;
        break;
    case 1:
        targetLife = 100 + 5 * (rand() % 5);
        break;
    case 2:
        targetLife = 1000 + 50 * (rand() % 10);
        break;
    }
    Task::randomize();
}
/* ------------ TaskMassiveBurial ------------ */

TaskMassiveBurial::TaskMassiveBurial(int _color, int _bodyCount) :
    Task(TASK_MASSIVE_BURIAL)
{
    color = _color;
    bodyCount = _bodyCount;
    if ((0 == color) || (0 == bodyCount))
    {
        randomize();
    }
}

int TaskMassiveBurial::computeReward()
{
    return rand() % 150 + bodyCount * ((Constants::MTG_COLOR_LAND == color) ? 70 : 50);
}

string TaskMassiveBurial::createDesc()
{
    char buffer[4096];

    sprintf(buffer, _("Bury %i %s cards to your opponent's graveyard and defeat him.").c_str(), bodyCount,
                    Constants::MTGColorStrings[color]);

    return buffer;
}

string TaskMassiveBurial::getShortDesc()
{
    char buffer[4096];
    switch (color)
    {
    case Constants::MTG_COLOR_GREEN:
        sprintf(buffer, _("Tame the nature (%i)").c_str(), bodyCount);
        break;
    case Constants::MTG_COLOR_BLUE:
        sprintf(buffer, _("Evaporation (%i)").c_str(), bodyCount);
        break;
    case Constants::MTG_COLOR_RED:
        sprintf(buffer, _("Bring the order (%i)").c_str(), bodyCount);
        break;
    case Constants::MTG_COLOR_BLACK:
        sprintf(buffer, _("Exorcism (%i)").c_str(), bodyCount);
        break;
    case Constants::MTG_COLOR_WHITE:
        sprintf(buffer, _("Dusk (%i)").c_str(), bodyCount);
        break;
    case Constants::MTG_COLOR_LAND:
        sprintf(buffer, _("Selective disaster (%i)").c_str(), bodyCount);
        break;
    }
    return buffer;
}

bool TaskMassiveBurial::isDone(GameObserver* observer, GameApp *)
{
    int countColor = 0;
    vector<MTGCardInstance *> cards = observer->players[1]->game->graveyard->cards;

    for (vector<MTGCardInstance *>::iterator it = cards.begin(); it != cards.end(); it++)
    {
        if ((*it)->hasColor(color))
        {
            countColor++;
        }
    }

    return (countColor >= bodyCount);
}

void TaskMassiveBurial::storeCustomAttribs()
{
    char buff[256];
    sprintf(buff, "%i", color);
    persistentAttribs.push_back(buff);

    sprintf(buff, "%i", bodyCount);
    persistentAttribs.push_back(buff);
}

void TaskMassiveBurial::restoreCustomAttribs()
{
    color = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT].c_str());
    bodyCount = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT + 1].c_str());
}

void TaskMassiveBurial::randomize()
{
    color = rand() % (Constants::NB_Colors - 1) + 1;
    bodyCount = 5 + ((Constants::MTG_COLOR_LAND == color) ? rand() % 10 : rand() % 20);
    Task::randomize();
}

/* ------------ TaskWisdom ------------ */

TaskWisdom::TaskWisdom(int _color, int _cardCount) :
    Task(TASK_WISDOM)
{
    color = _color;
    cardCount = _cardCount;
    if ((0 == color) || (0 == cardCount))
    {
        randomize();
    }
}

int TaskWisdom::computeReward()
{
    return (rand() % 150) + (cardCount * 50) + (cardCount > 7 ? 200 : 0);
}

string TaskWisdom::createDesc()
{
    char buffer[4096];

    sprintf(buffer, _("Win a game with at least %i %s cards in your hand.").c_str(), cardCount, Constants::MTGColorStrings[color]);

    return buffer;
}

string TaskWisdom::getShortDesc()
{
    char buffer[4096];
    switch (color)
    {
    case Constants::MTG_COLOR_GREEN:
        sprintf(buffer, _("Animal herder (%i)").c_str(), cardCount);
        break;
    case Constants::MTG_COLOR_BLUE:
        sprintf(buffer, _("Accumulated knowledge (%i)").c_str(), cardCount);
        break;
    case Constants::MTG_COLOR_RED:
        sprintf(buffer, _("Retained anger (%i)").c_str(), cardCount);
        break;
    case Constants::MTG_COLOR_BLACK:
        sprintf(buffer, _("Necropotence (%i)").c_str(), cardCount);
        break;
    case Constants::MTG_COLOR_WHITE:
        sprintf(buffer, _("Dawn of crusade (%i)").c_str(), cardCount);
        break;
    case Constants::MTG_COLOR_LAND:
        sprintf(buffer, _("Mana reserves (%i)").c_str(), cardCount);
        break;
    }
    return buffer;
}

bool TaskWisdom::isDone(GameObserver* observer, GameApp *)
{
    int countColor = 0;
    vector<MTGCardInstance *> cards = observer->players[0]->game->hand->cards;

    for (vector<MTGCardInstance *>::iterator it = cards.begin(); it != cards.end(); it++)
    {
        if ((*it)->hasColor(color))
        {
            countColor++;
        }
    }

    return (!observer->players[0]->isAI()) && (observer->players[1]->isAI()) && (observer->didWin(observer->players[0])) // Human player wins
                    && (countColor >= cardCount);
}

void TaskWisdom::storeCustomAttribs()
{
    char buff[256];
    sprintf(buff, "%i", color);
    persistentAttribs.push_back(buff);

    sprintf(buff, "%i", cardCount);
    persistentAttribs.push_back(buff);
}

void TaskWisdom::restoreCustomAttribs()
{
    color = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT].c_str());
    cardCount = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT + 1].c_str());
}

void TaskWisdom::randomize()
{
    color = rand() % (Constants::NB_Colors - 1) + 1;
    cardCount = 2 + ((Constants::MTG_COLOR_LAND == color) ? rand() % 5 : rand() % 7);
    Task::randomize();
}

/* ------------ TaskPacifism ------------ */

TaskPacifism::TaskPacifism(int _lifeSlashCardMin) :
    Task(TASK_PACIFISM)
{
    lifeSlashCardMin = _lifeSlashCardMin;
    if (lifeSlashCardMin == 0)
    {
        randomize();
    }
}

int TaskPacifism::computeReward()
{
    return 200 + (rand() % 50) + (lifeSlashCardMin * 5) + (lifeSlashCardMin > 20 ? 200 : 0);
}

string TaskPacifism::createDesc()
{
    char buffer[4096];

    switch (rand() % 2)
    {
    case 0:
        sprintf(buffer, _("Let your opponent live with at least %i life and cards in library, but defeat him.").c_str(),
                        lifeSlashCardMin);
        break;
    case 1:
        sprintf(buffer, _("Win a game with your opponent having at least %i life and cards in library.").c_str(), lifeSlashCardMin);
        break;
    }
    return buffer;
}

string TaskPacifism::getShortDesc()
{
    char buffer[4096];
    sprintf(buffer, _("Pacifism (%i)").c_str(), lifeSlashCardMin);
    return buffer;
}

bool TaskPacifism::isDone(GameObserver* observer, GameApp *)
{
    return (!observer->players[0]->isAI()) && (observer->players[1]->isAI()) && (observer->didWin(observer->players[0])) // Human player wins
                    && (observer->players[1]->life >= lifeSlashCardMin) && ((int) observer->players[1]->game->library->cards.size() >= lifeSlashCardMin);
}

void TaskPacifism::storeCustomAttribs()
{
    char buff[256];

    sprintf(buff, "%i", lifeSlashCardMin);
    persistentAttribs.push_back(buff);
}

void TaskPacifism::restoreCustomAttribs()
{
    lifeSlashCardMin = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT].c_str());
}

void TaskPacifism::randomize()
{
    lifeSlashCardMin = rand() % 25 + 1;
    Task::randomize();
}

/* ------------ Task template ------------ 

 TaskXX::TaskXX() : Task(TASK_XX) {
 // TODO: Implement
 }

 int TaskXX::computeReward() {
 // TODO: Implement
 return 100;
 }

 string TaskXX::createDesc() {
 // TODO: Implement
 char buffer[4096];

 switch (rand()%2) {
 case 0:
 sprintf(buffer, _("%s").c_str(), getOpponentName().c_str());
 break;
 case 1:
 sprintf(buffer, _("%s").c_str(), getOpponentName().c_str());
 break;
 }
 return buffer;
 }

 string TaskXX::getShortDesc(){
 // TODO: Implement
 char buffer[4096];
 sprintf(buffer, _("%s").c_str(), getOpponentName().c_str());
 return buffer;
 }

 bool TaskXX::isDone(GameObserver* observer, GameApp * _app) {
 // TODO: Implement
 return (!observer->players[0]->isAI()) && (observer->players[1]->isAI()) && (observer->gameOver != _p1) // Human player wins
 }

 void TaskXX::storeCustomAttribs() {
 // TODO: Implement
 char buff[256];
 persistentAttribs.push_back(VarXX);

 sprintf(buff, "%i", VarXY);
 persistentAttribs.push_back(buff);
 }

 void TaskXX::restoreCustomAttribs() {
 // TODO: Implement
 VarXX = persistentAttribs[COMMON_ATTRIBS_COUNT];
 VarXY = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT+1].c_str());
 }

 void TaskXX::randomize() {
 // TODO: Implement
 VarXX = rand()%10 + 1;
 Task::randomize();
 }

 */
