#include "cell.h"

#include "hero.h"
#include "level.h"

#define UNUSED(expr) (void)(expr)

Cell::Cell(coordinate cell_row, coordinate cell_col, const sf::Color &color) :
    Entity(cell_row, cell_col),
    cell_color(color),
	height_shift(0) // ground by default
{}

Cell::~Cell()
{}

sf::Color Cell::color() const noexcept
{
    return cell_color;
}

void Cell::setHeightShift(coordinate shift_by_y)
{
	height_shift = shift_by_y;
}

coordinate Cell::heightShift() const
{
	return height_shift;
}

///////////////////////////////////////

PassableCell::PassableCell(coordinate cell_row, coordinate cell_col, const sf::Color &color) :
    Cell(cell_row, cell_col, color)
{}

PassableCell::~PassableCell()
{}

bool PassableCell::onMovingTo(HeroPtr &hero, LevelPtr &level)
{
    UNUSED(hero), UNUSED(level);

    // Hero just moves on.
    return true;
}

CellPtr PassableCell::clone() const
{
    return std::make_unique<PassableCell>();
}

///////////////////////////////////////

WaterCell::WaterCell(coordinate cell_row, coordinate cell_col, const sf::Color &color) :
    Cell(cell_row, cell_col, color)
{}

WaterCell::~WaterCell()
{}

bool WaterCell::onMovingTo(HeroPtr &hero, LevelPtr &level)
{
    // Try to use one charge to place a bridge
    if (hero->useCharge())
        level->placeBridge(entity_row, entity_col);

    return false;
}

CellPtr WaterCell::clone() const
{
    return std::make_unique<WaterCell>();
}

///////////////////////////////////////

WallCell::WallCell(coordinate cell_row, coordinate cell_col, const sf::Color &color) :
    Cell(cell_row, cell_col, color)
{
    // walls are a bit higher than ground and water
    height_shift = 10;
}

WallCell::~WallCell()
{}

bool WallCell::onMovingTo(HeroPtr &hero, LevelPtr &level)
{
    UNUSED(hero), UNUSED(level);

    // Hero never passes this cell.
    return false;
}

CellPtr WallCell::clone() const
{
    return std::make_unique<WallCell>();
}

///////////////////////////////////////

ChargeCell::ChargeCell(coordinate cell_row, coordinate cell_col, int has_charges, const sf::Color &color) :
    Cell(cell_row, cell_col, color),
    cell_charges(has_charges)
{
    height_shift = 5; // charges are a bit higher than ground and water, but lower than walls
}

ChargeCell::~ChargeCell()
{}

void ChargeCell::setCharges(const int &num_charges)
{
    cell_charges = num_charges;
}

bool ChargeCell::onMovingTo(HeroPtr &hero, LevelPtr &level)
{
    // Hero picks up the charge; remove it from the map
    hero->refillCharges(cell_charges);
    level->removeCharge(entity_row, entity_col);

    return true;
}

CellPtr ChargeCell::clone() const
{
    return std::make_unique<ChargeCell>();
}

///////////////////////////////////////

ExitCell::ExitCell(coordinate cell_row, coordinate cell_col, const sf::Color &color) :
    Cell(cell_row, cell_col, color)
{}

ExitCell::~ExitCell()
{}

bool ExitCell::onMovingTo(HeroPtr &hero, LevelPtr &level)
{
    UNUSED(level);

    // Level is over.
    hero->reachExit();
    return true;
}

CellPtr ExitCell::clone() const
{
    return std::make_unique<ExitCell>();
}

///////////////////////////////////////

TeleportCell::TeleportCell(coordinate cell_row, coordinate cell_col, coordinate new_cell_row, coordinate new_cell_col, const sf::Color &color) :
    Cell(cell_row, cell_col, color),
    new_row(new_cell_row),
    new_col(new_cell_col)
{}

TeleportCell::~TeleportCell()
{}

void TeleportCell::setDestination(coordinate new_cell_row, coordinate new_cell_col)
{
    new_row = new_cell_row;
    new_col = new_cell_col;
}

bool TeleportCell::onMovingTo(HeroPtr &hero, LevelPtr &level)
{
    UNUSED(level);

    // Hero jumps into teleport!
    hero->setPosition(new_row, new_col);
    return true;
}

CellPtr TeleportCell::clone() const
{
    return std::make_unique<TeleportCell>();
}

///////////////////////////////////////

TriggerCell::TriggerCell(coordinate cell_row, coordinate cell_col, const sf::Color &color) :
    Cell(cell_row, cell_col, color)
{
    // triggers are a bit higher than ground and water, but lower than walls
    height_shift = 5;
}

TriggerCell::~TriggerCell()
{}

void TriggerCell::addTarget(CellPtr &&cell)
{
    vector_cells.emplace_back(std::move(cell));
}

bool TriggerCell::onMovingTo(HeroPtr &hero, LevelPtr &level)
{
    UNUSED(hero);

	// We replace needed cells with the ones that the trigger provides.
    for (CellPtr &cell : vector_cells)
    {
        const coordinate &row = cell->row();
        const coordinate &col = cell->col();

        level->getCellAt(row, col).release();
        level->getCellAt(row, col) = std::move(cell);
    }
	
    vector_cells.clear();

	// It's an impassable object, so player can't move to here.
    return false;
}

CellPtr TriggerCell::clone() const
{
    return std::make_unique<TriggerCell>();
}
