#include <iostream>
#include <cassert>

using namespace std;

#include "Battle.h"
#include "Factory.h"
#include "arena.h"
#include "archer.h"
#include "cleric.h"
#include "robot.h"

void test_fighter() {
    archer a = archer("bilbo", 100, 60, 1, 5);
    a.takeDamage(90);
    a.regenerate();
    assert(a.getCurrentHP() == 20);
    archer b = archer("frodo", 100, 0, 1, 5);
    b.takeDamage(90);
    b.regenerate();
}

void test_archer()  {
    archer a("stephen", 666, 66, 6, 0);
    a.takeDamage(100);
    assert(a.getCurrentHP() == 567);
    assert(a.get_original_speed() == 6);
    assert(a.getSpeed() == 6);
}

void test_cleric()  {
    cleric a("Derek", 666, 66, 6, 20);
    assert(a.getDamage() == 20);
    assert(a.get_mana() == 100);
    a.takeDamage(100);
    assert(a.getCurrentHP() == 567);
    a.reset();
    assert(a.getCurrentHP() == 666);

    cleric b("SaintColleen", 100, 90, 4, 60);
    b.takeDamage(100);
    b.useAbility();
    assert(b.getCurrentHP() == 21);

    cleric c("Vanessa", 100, 90, 4, 1);
    c.takeDamage(100);
    c.useAbility();
    assert(c.getCurrentHP() == 1);

    cleric d("Vanessa", 100, 90, 4, 5);
    d.takeDamage(100);
    d.useAbility();
    assert(d.getCurrentHP() == 2);
}

void test_robot() {
    robot a("Derek", 100, 20, 10, 10);
    assert(a.getDamage() == 20);
    a.useAbility();
    assert(a.getDamage() == 40);
    assert(a.getDamage() == 20);
    a.useAbility();
    assert(a.getDamage() == 26);
    a.takeDamage(100);
    assert(a.getCurrentHP() == 2);
    a.reset();
    assert(a.getCurrentHP() == 100);
}
    
void test_input_parser() {
    ArenaInterface * A = Factory::createArena();
    arena * a = dynamic_cast<arena *>(A);

    string input = "Xephos A 200 13 21 10";
    assert(a->addFighter(input));

    input = "Stephen A 200 13 21 10";
    assert(a->addFighter(input));

    input = "Billy Bob Thorton A 0 13 21 10";
    assert(not a->addFighter(input));

    input = "Xephos a 200 13 21 10";
    assert(not a->addFighter(input));

    input = "Derek A 200 13 21 a10";
    assert(not a->addFighter(input));

    input = "Derek A 200 13 stupid21 10";
    assert(not a->addFighter(input));

    delete A;
}

void test_add() {
    ArenaInterface * A = Factory::createArena();
    arena * a = dynamic_cast<arena *>(A);

    string input = "Xephos A 200 13 21 10";
    assert(a->addFighter(input));

    input = "Stephen A 200 13 21 10";
    assert(a->addFighter(input));

    // do not allow for duplicate fighers
    input = "Xephos A 200 13 21 10";
    bool double_add = a->addFighter(input);
    assert(not double_add);

    input = "Stephen A 0 13 21 10";
    assert(not a->addFighter(input));

    input = "Derek A 200 13 21 10";
    assert(a->addFighter(input));

    // another attempt for not allowing duplicate fighters
    input = "Stephen A 200 13 21 10";
    assert(not a->addFighter(input));
    delete A;
}

void test_remove() {
    ArenaInterface * A = Factory::createArena();
    arena * a = dynamic_cast<arena *>(A);

    string input = "Michael A 200 13 21 10";
    a->addFighter(input);
    input = "Derek A 200 13 21 10";
    a->addFighter(input);
    input = "Bryan A 200 13 21 10";
    a->addFighter(input);

    assert(not a->removeFighter("Zaphodbeblebrox"));

    bool remove_non_existant = a->removeFighter("Stephen");
    assert(not remove_non_existant);
    assert(a->getSize() == 3);

    bool remove_fighter = a->removeFighter("Michael");
    assert(remove_fighter);
    assert(a->getSize() == 2);

    a->removeFighter("Derek");
    a->removeFighter("Bryan");
    assert(a->getSize() == 0);
    delete a;
}

void test_find() {
    ArenaInterface * A = Factory::createArena();
    arena * a = dynamic_cast<arena *>(A);

    string input = "Michael A 200 13 21 10";
    a->addFighter(input);
    input = "Derek A 200 13 21 10";
    a->addFighter(input);
    input = "Bryan A 200 13 21 10";
    a->addFighter(input);

    fighter * f;
    f = dynamic_cast<fighter *>(a->getFighter("Stephen"));
    assert(not f);
    f = dynamic_cast<fighter *>(a->getFighter("Derek"));
    assert(f);

    delete a;
}

void test_archer_reset() {
    archer a("stephen", 666, 66, 6, 0);
    a.takeDamage(100);
    assert(a.getCurrentHP() == 567);
    a.reset();
    assert(a.getCurrentHP() == a.getMaximumHP());
    assert(a.getSpeed() == a.get_original_speed());
    assert(a.getSpeed() == 6);
    assert(a.get_original_speed() == 6);
}

void test_archer_usability() {
    archer a("stephen", 666, 66, 6, 0);
    assert(a.getSpeed() == 6);
    a.useAbility();
    assert(a.getSpeed() == 7);
}

void test_battle() {
    archer a("stephen", 200, 66, 60, 100);
    robot b("derek", 1000, 20, 15, 200);
    fight(&a, &b, true);
}

int main() {
    test_fighter();
    test_archer();
    test_cleric();
    test_robot();
    test_input_parser();
    test_add();
    test_remove();
    test_find();
    test_archer_reset();
    test_archer_usability();
    test_battle();
}
