#pragma once
#include <QObject>
#include <QGraphicsItem>

#include <queue>
#include <vector>
#include <memory>
#include <iostream>
#include <list>
/*
 * Решили, что метод canAttackForDistance(int) имеет шаблон, который обозначает тип атаки
 * Melee - без учёта препятствий
 * Range - с учётом препятствий
 * Шаблон - строка. При такой реализации можно добавить новый вид атаки
 * OK! - Необходимо в Unit добавить метод bool isCharacter() - является ли данный юнит персонажем
 */
class Unit;
/*
 * Этот класс Игоря ждёт своей реализации
 * (Название неточное)
 * Необходимо добавить методы:
 * OperateOnCell(Cell*)
 * OperateOnUnit(Unit*)
 */
class EffectsForCell;

class Cell : public QGraphicsItem {
private:
	/*
	 * Соседние клетки к текущей
	 * nullptr, если ее не существует
	*/
	Cell* leftUp_;
	Cell* left_;
	Cell* leftDown_;
	Cell* rightUp_;
	Cell* right_;
	Cell* rightDown_;
	/*
	 * Ссылка на любую дичь, который находится на клетке
	 * nullptr, клетка пустая
	 */
    std::shared_ptr<Unit> character_;
	/*
	 * Список всех эффектов, применённых к этой клетке
	 * Работа с этим списком в методах ниже
	 * Добавление, удаление, указатель на начало и конец
	 */
	std::vector<EffectsForCell*> effects_list_;
	/*
	 * isMoveAble - Можно ли попасть в данную клетку из точки пересчета
	 * переходя только по клеткам без юнитов
	 * isMeleeAttackAble - Можно ли атаковать данную клетку
	 * isRangeAttackAble_;
	 */
	bool isMoveAble_;
	bool isMeleeAttackAble_;
	bool isRangeAttackAble_;
	/*
	 * Расстояние от точки пересчёта до текущей точки
	 * distance_barrier - расстояние с учетом других препятствий
	 * distance_through - расстояние без учета препятствий
	 * -1, если недостижимо
	 */
	int distance_barrier_;
	int distance_through_;

    QPolygon polygon;

public:
    QRectF boundingRect() const override;

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;

    virtual bool contains(const QPointF &point) const override;

    bool isHovered();

    void setHovered(bool hovered);

    void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;

    void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;

private:
    /*
     * Локальные методы и переменные
     */
	bool AddedToQuery_;
	int col_, row_;

    double coor_x_;
    double coor_y_;
    bool hovered_;

	void clearTable_();
	void clearCell_();
	void recalcAttackable_(Cell*, bool);
	void recalcMoveable_(Cell*, bool);
	/*
	 * BFS Moveable Cells And Find Unmoveable Cells
	 */
	void updateMoveableCells_(std::queue<Cell *> &Q);
	/*
	 * BFS UnMoveable Cells
	 */
	void updateUnMovealeCells_(std::queue<Cell *> &Q);

public:
    explicit Cell(QGraphicsItem* parent = nullptr);
	/*
	 * Геттеры и сеттеры соседних клеток и персонажа
	 */
	Cell* getleftUp();
	void setleftUp(Cell*);
	Cell* getleft();
	void setleft(Cell*);
	Cell* getleftDown();
	void setleftDown(Cell*);
	Cell* getrightUp();
	void setrightUp(Cell*);
	Cell* getright();
	void setright(Cell*);
	Cell* getrightDown();
	void setrightDown(Cell *);
    std::shared_ptr<Unit> getCharacter();
    void setCharacter(std::shared_ptr<Unit>);

    double getXCoordinate();
    void setXCoordinate(double coordinate);
    double getYCoordinate();
    void setYCoordinate(double coordinate);

	/*
	 * Ещё геттеры и сеттеры
	 */
    int getdistance_barrier();
    void setdistance_barrier(int);
    int getdistance_through();
    void setdistance_through(int);
    /*
     * Нужно больше геттеров и сеттеров
     */
    bool getisMoveAble();
	void setisMoveAble(bool);
	bool getisMeleeAttackAble();
	void setisMeleeAttackAble(bool);
	bool getisRangeAttackAble();
	void setisRangeAttackAble(bool);
	/*
	 * Содержит ли текущая клетка unit
	 */
	bool isEmpty();
	/*
	 * Методы для работы с эффектами
	 */
	void recalculateAllEffectsList();
	void add(EffectsForCell*);
	void remove(std::vector<EffectsForCell*>::iterator);
	void remove(EffectsForCell*);
	std::vector<EffectsForCell*>::iterator beginIteratorEffectsList();
	std::vector<EffectsForCell*>::iterator endIteratorEffectsList();
	/*
	 * Пересчитывает ВСЁ поле, делая текущую клетку - точкой пересчета
	 */
	void RecalculateTableWithCenterThisPoint();
	/*
	 * Работает корректно только в случае, если текущая - точка пересчета
	 * Возвращает список всех клеток от параметра до точки пересчета
	 * Включает в себя и начало, и конец
	 * При этом список составлен в порядке прохода, начиная с последнего
	 * Если пути нету, то вернется пустой список
	 */
	std::vector <Cell*> actualPath(Cell*);
	/*
	 * Отвечает за запрос, куда попадет шар, если ее направить в клетку-параметр
	 * Выполнится только с лучае, если эта следующая клетка существует
	 * Gi1dor знает, что это такое
	 */
    std::vector<std::pair<int, int> > getPoints(double, double);
    Cell* getRealShootTarget(Cell*);
};
