#include "level.h"
#include <sstream>
#include <vector>
#include <render/render.h>
#include <game/interface/selectable.h>
#include <resources.h>

namespace game
{

level::level(const vec2u &size)
{
    _size = size;
    _map = new cell*[size.x];

    for(uint i = 0; i < size.x;)
        _map[i++] = new cell[size.y];

    const vec2i offs[4] = {vec2i(1,0),vec2i(0,1),vec2i(-1,0),vec2i(0,-1)};

    for(int i = 0; i < size.x; i++)
        for(int j = 0; j < size.y; j++)
        {
            _map[i][j]._root = this;
            for(int k = 0; k < 4; k++)
            {
                vec2i p = vec2i(i+offs[k].x,j+offs[k].y);
                if(p.x < 0 || p.x >= _size.x
                || p.y < 0 || p.y >= _size.y)
                    _map[i][j]._siblings[k] = 0;
                else
                    _map[i][j]._siblings[k] = &_map[p.x][p.y];
            }
        }
}

level::~level()
{
    for(uint i = 0; i < _size.x;)
        delete _map[i++];
    delete _map;
}

bool level::isCellsFree(const vec2u &pos, const vec2u &size)
{
    for(uint i = pos.x, x = pos.x+size.x; i < x; i++)
    {
        if(i < 0 || i > _size.x)
            return false;

        for(uint j = pos.y, y = pos.y+size.y; j < y; j++)
        {
            if(j < 0 || j > _size.y)
                return false;

            if(!_map[i][j].isFree())
                return false;
        }
    }

    return true;
}

bool level::allocateCells(std::vector<cell*> &c, const vec2u &pos, const vec2u &size, entity *ent)
{
    for(uint i = pos.x, x = pos.x+size.x; i < x; i++)
    {
        if(i < 0 || i > _size.x)
            return false;

        for(uint j = pos.y, y = pos.y+size.y; j < y; j++)
        {
            if(j < 0 || j > _size.y)
                return false;

            cell *target = &_map[i][j];
            target->set(ent, false);
            c.push_back(target);

            _touchNeighbours(vec2i(i,j),ent);
        }
    }

    c[0]->set(ent, true);
    return true;
}

void level::_touchNeighbours(const vec2i &pos, entity *ent)
{
    constexpr vec2i offsets[4] = {vec2i(0,1),vec2i(1,0),vec2i(0,-1),vec2i(-1,0)};

    for(const vec2i &offset:offsets)
    {
        int px, py;
        if((px = pos.x+offset.x) < 0 || px >= _size.x
        || (py = pos.y+offset.y) < 0 || py >= _size.y)
            continue;

        entity *t = _map[px][py].get();

        if(!t || t == ent) // не будем трогать сами себя (и пустые)
            continue;

        t->onNeighbourCreated(ent);
    }
}

void level::selectCell(const vec2u &pos, bool pressed)
{
    if(pos.x < 0 || pos.x >= _size.x
    || pos.y < 0 || pos.y >= _size.y)
        return;
    _hoveredCell = pos;

    if(entity *en = _map[pos.x][pos.y].get())
        if(selectable *s = dynamic_cast<selectable*>(en))
            if(pressed)
                s->select();
            else
                s->hover();
}

bool level::selectCells(const vec2u &pos, const vec2u& size)
{
    if(pos.x < 0 || pos.x >= _size.x
    || pos.y < 0 || pos.y >= _size.y)
        return 0;
    _hoveredCell = pos;
    _placeArea = size;

    return (_freeSpace = isCellsFree(pos,size));
}

void level::draw()
{
    if(!_floor.count)
    {
        render::flush();
        for(uint i = 0; i < _size.x; i++)
            for(uint j = 0; j < _size.y; j++)
                resources::atlas.draw(vec2f(i,j), vec2f((i*j)&3,15));
        render::sprite::saveBuffer(&_floor);
    }else
    {
        resources::atlas.bind();
        render::sprite::draw(&_floor);
    }

    if(_placeArea.x != 0xFFFFFFFF)
    {
        if(_freeSpace)
            render::primitives2d::quad(_hoveredCell, _placeArea, color::uint2rgba(0x3e8948, 0.3));
        else
            render::primitives2d::quad(_hoveredCell, _placeArea, color::uint2rgba(0xe43b44, 0.3));
        _placeArea.x = _hoveredCell.x = 0xFFFFFFFF;
    }

    if(_hoveredCell.x != 0xFFFFFFFF)
    {
        render::primitives2d::quad(_hoveredCell, vec2f(1), color::uint2rgba(0x0095e9, 0.3));
        _hoveredCell.x = 0xFFFFFFFF;
    }

    for(uint i = 0; i < _size.x; i++)
        for(uint j = 0; j < _size.y; j++)
            if(_map[i][j]._entity && _map[i][j]._primary)
                _map[i][j]._entity->draw();
}

}

