#ifndef CELL_H
#define CELL_H

#include <memory>
#include <vector>

#include <SFML/Graphics/Color.hpp>

#include "entity.h"

namespace palette
{
const sf::Color Brown  = sf::Color(165,  42,  42);
const sf::Color White  = sf::Color(255, 255, 255);
const sf::Color Blue   = sf::Color(  0, 255, 255);
const sf::Color Gray   = sf::Color(125, 125, 125);
const sf::Color Green  = sf::Color(  0, 255,   0);
const sf::Color Red    = sf::Color(250,   0,   0);
const sf::Color Purple = sf::Color(128,   0, 128);
const sf::Color Pink   = sf::Color(255, 192, 203);
const sf::Color Black  = sf::Color(  0,   0,   0);
}

enum CELL_TYPE {
    PASSABLE_CELL = 0,
    WATER_CELL    = 1,
    WALL_CELL     = 2,
    CHARGE_CELL   = 3,
    EXIT_CELL     = 4,
    TELEPORT_CELL = 5,
    TRIGGER_CELL  = 6,

    N_CELLS
};

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

class Hero;
class Level;
class Cell;

using HeroPtr   = std::unique_ptr<Hero>;
using LevelPtr  = std::unique_ptr<Level>;
using CellPtr   = std::unique_ptr<Cell>;

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

/// Represents interface for all level cells
class Cell : public Entity
{
protected:
    sf::Color cell_color;
    coordinate height_shift;

public:
    Cell(coordinate cell_row = 0,
         coordinate cell_col = 0,
         const sf::Color &color = palette::White);

    virtual ~Cell() override;

    sf::Color color() const noexcept;

    /// "shift_by_y" indicates the height of current cell
    /// Height is a shift of y coordinate on the scene, relatively to the ground (which is 0)
    void setHeightShift(coordinate shift_by_y);
    coordinate heightShift() const;

    /// Determine if Hero can move onto this cell or not
    virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) = 0;

    virtual CellPtr clone() const = 0;
};

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

/// Any cell where Hero is free to move
class PassableCell final : public Cell
{
public:
    PassableCell(coordinate cell_row = 0,
                 coordinate cell_col = 0,
                 const sf::Color &color = palette::Brown);

    virtual ~PassableCell() override;

    virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override;

    virtual CellPtr clone() const override;
};

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

/// A cell which requires Hero to spend a charge for bridge to move on
class WaterCell final : public Cell
{
public:
    WaterCell(coordinate cell_row = 0,
              coordinate cell_col = 0,
              const sf::Color &color = palette::Blue);

    virtual ~WaterCell() override;

    virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override;

    virtual CellPtr clone() const override;
};

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

/// A cell which is impossible to move on
class WallCell final : public Cell
{
public:
    WallCell(coordinate cell_row = 0,
             coordinate cell_col = 0,
             const sf::Color &color = palette::Gray);

    virtual ~WallCell() override;

    virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override;

    virtual CellPtr clone() const override;
};

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

/// A cell which gives hero a charge
class ChargeCell final : public Cell
{
private:
    int cell_charges;

public:
    ChargeCell(coordinate cell_row = 0,
               coordinate cell_col = 0,
               int has_charges = 1,
               const sf::Color &color = palette::Green);

    /// Sets value of cell_charges to num_charges
    void setCharges(const int &num_charges);

    virtual ~ChargeCell() override;

    virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override;

    virtual CellPtr clone() const override;
};

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

/// A cell which moves hero to next level
class ExitCell final : public Cell
{
public:
    ExitCell(coordinate cell_row = 0,
             coordinate cell_col = 0,
             const sf::Color &color = palette::Red);

    virtual ~ExitCell() override;

    virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override;

    virtual CellPtr clone() const override;
};

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

/// A cell which teleports hero to following coordinates
class TeleportCell final : public Cell
{
private:
    coordinate new_row, new_col;

public:
    TeleportCell(coordinate cell_row = 0,
                 coordinate cell_col = 0,
                 coordinate new_cell_row = 0,
                 coordinate new_cell_col = 0,
                 const sf::Color &color = palette::Purple);

    virtual ~TeleportCell() override;

    /// Set the coordinates of this teleport destination
    void setDestination(coordinate new_cell_row, coordinate new_cell_col);

    virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override;

    virtual CellPtr clone() const override;
};

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

/// A cell which replaces and changes other map cells when activated
class TriggerCell final : public Cell
{
private:
    // Vector of cells to place on map
    std::vector<CellPtr> vector_cells;

public:
    TriggerCell(coordinate cell_row = 0,
                coordinate cell_col = 0,
                const sf::Color &color = palette::Pink);

    virtual ~TriggerCell() override;

    /// Add a cell which has to be placed to map when the trigger gets activated
    void addTarget(CellPtr &&cell);

    virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override;

    virtual CellPtr clone() const override;
};

#endif // CELL_H
