#include <algorithm>
#include "plant.h"
#include <render/render.h>
#include <imgui/imgui.h>
#include <resources.h>
#include <game/game.h>

namespace game{

plant::plant(const vec2u &pos, level *root):entity(pos, vec2u(3), root)
{
    _receipt = 0;
    _placeOnMap();
    memset(_stat,-1,0xFFFF);
    _statPos = 0;
}

void plant::update()
{
    std::lock_guard<std::mutex> lg(_locker);
    if(!_receipt)
        return;

    _stat[_statPos++] = _update();
    if(_statPos == 0xFFFF)
        _statPos = 0;
}

char plant::_update()
{
    // хапаем ресурсов откуда можем
    for(uint32_t i = 0; i < _input.size(); i++)
        if(_input[i].amount < _receipt->input[i].amount*5)
        {
            float resourceNeed = _receipt->input[i].amount*5 - _input[i].amount;
            for(auto *r: _providers)
            {
                float get = r->require(_input[i].id, resourceNeed);
                _input[i].amount += get;
                resourceNeed -= get;
                if(resourceNeed <= 0.1)
                    break;
            }
        }

    // отдадим результат куда сможем
    for(uint32_t i = 0; i < _output.size(); i++)
        if(_output[i].amount > 0)
            for(auto *p: _pipes)
            {
                _output[i].amount -= p->add(_output[i].id, _output[i].amount);
                if(_output[i].amount <= 0.1)
                    break;
            }

    if(_receiptTicks == _receipt->ticks)
    {
        // проверяем что у нас есть куда накласть результат
        for(uint32_t i = 0; i < _output.size(); i++)
            if(_output[i].amount+_receipt->output[i].amount >= _receipt->output[i].amount*40)
            {
                return 1; // некуда положить продукцию
            }

        // непосредственно откладываем
        for(uint32_t i = 0; i < _output.size(); i++)
            _output[i].amount += _receipt->output[i].amount;

        _receiptTicks = 0;
    }else
    {
        // проверяем что у нас есть из чего делать
        if(!_receiptTicks)
        {
            for(uint32_t i = 0; i < _input.size(); i++)
                if(_input[i].amount < _receipt->input[i].amount)
                    return 2; // нет ресурсов

            for(uint32_t i = 0; i < _input.size(); i++)
                _input[i].amount -= _receipt->input[i].amount;

            getMoney(_receipt->price);
        }

        _receiptTicks++;
    }
    return 0;
}

void plant::draw()
{
    vec2u cursorDrawOffsets[4] = {vec2u(0),vec2u(2,0),vec2u(0,2),vec2u(2)};

    resources::atlas.draw(_pos, vec2u(0,4),vec2u(3));

    if(_needShowGui)
        for(int i = 0; i < 4; i++)
            resources::atlas.draw(_pos+cursorDrawOffsets[i],vec2u(i+1,14));
    else if(_needShowShortInfo)
        for(int i = 0; i < 4; i++)
            resources::atlas.draw(_pos+cursorDrawOffsets[i],vec2u(i+1,13));

    for(int i = 0; i < 3; i++)
        if(cell *c = _cells[i]->sibling(2))
            if(c->get())
                resources::atlas.draw(_pos+vec2u(0,i),vec2f(3,4+(((int)c->get())%3)));

    for(int i = 0; i < 3; i++)
        if(cell *c = _cells[i*3+2]->sibling(1))
            if(c->get())
                resources::atlas.draw(_pos+vec2u(i,2),vec2f(4,4+(((int)c->get())%3)));

    for(int i = 0; i < 3; i++)
        if(cell *c = _cells[i+6]->sibling(0))
            if(c->get())
                resources::atlas.draw(_pos+vec2u(2,i),vec2f(5,4+(((int)c->get())%3)));

    if(_receipt)
        render::primitives2d::quad(_pos, vec2f(float(_receiptTicks)/_receipt->ticks, 1)*_size, rgba(0.1, .2, 0.5, 0.5));
}

void plant::onNeighbourCreated(entity *ent)
{
    std::lock_guard<std::mutex> lg(_locker);

    if(basicPipe *p = dynamic_cast<basicPipe*>(ent))
        if(std::find(_pipes.begin(), _pipes.end(), p) == _pipes.end())
            _pipes.push_back(p);

    if(resourceProvider *p = dynamic_cast<resourceProvider*>(ent))
        if(std::find(_providers.begin(), _providers.end(), p) == _providers.end())
        {
            if(plant *pl = dynamic_cast<plant*>(p))
                pl->addProvider(this);

            _providers.push_back(p);
        }
}

void plant::addPipe(basicPipe *p) noexcept
{
    std::lock_guard<std::mutex> lg(_locker);

    if(std::find(_pipes.begin(), _pipes.end(), p) == _pipes.end())
        _pipes.push_back(p);

    if(resourceProvider *r = dynamic_cast<resourceProvider*>(p))
        if(std::find(_providers.begin(), _providers.end(), r) == _providers.end())
            _providers.push_back(r);
}

void plant::addProvider(resourceProvider *p) noexcept
{
    std::lock_guard<std::mutex> lg(_locker);

    if(resourceProvider *r = dynamic_cast<resourceProvider*>(p))
        if(std::find(_providers.begin(), _providers.end(), r) == _providers.end())
            _providers.push_back(r);
}

void plant::beforeNeighbourDestroyed(entity *ent)
{
    std::lock_guard<std::mutex> lg(_locker);

    if(resourceProvider *r = dynamic_cast<resourceProvider*>(ent))
        _providers.remove(r);

    if(pipe *p = dynamic_cast<pipe*>(ent))
        _pipes.remove(p);
}

float plant::require(uint item, float amount) noexcept
{
    for(auto &i: _output)
        if(i.id == item)
        {
            if(i.amount >= amount)
            {
                i.amount -= amount;
                return amount;
            }

            amount = i.amount;
            i.amount = 0;
            return amount;
        }

    return 0;
}

void plant::setReceipt(receiptInfo *receipt)
{
    std::lock_guard<std::mutex> lg(_locker);

    if(receipt == _receipt)
        return;

    _receipt = receipt;
    _receiptTicks = 0;

    _input.clear();
    _output.clear();

    if(!receipt)
        return;

    for(auto&i:receipt->input)
        _input.emplace_back(productInfo{i.id,0});

    for(auto&i:receipt->output)
        _output.emplace_back(productInfo{i.id,0});
}

void plant::hover() noexcept
{
    _needShowShortInfo = true;
}

void plant::onGui() noexcept
{
    if(_needShowShortInfo && !_needShowGui)
    {
        _drawShortInfo();
        _needShowShortInfo = false;
    }

    if(!_needShowGui)
        return;

    if(currentlyShown != this)
    {
        _needShowGui = false;
        return;
    }
    _drawWindow();
}


}
