#include "Game.h"

#include <AsynchronCall.h>
#include <FileSystem.h>
#include <Screen.h>

#include "Globals.h"
#include "Initialization.h"
#include "ItemBar.h"
#include "StatusBars.h"

Game::Game(Screen* zScreen)
    : Menu(zScreen),
      recipieVisible(0),
      itemListContainer(0)
{
    LTDBFile dat;
    dat.setFile(new Text("data/images/gui_icons.ltdb"));
    dat.readData(0);
    Image* search = dat.load(0, new Text("search.png"));
    searchIcon = new ImageView();
    searchIcon->setImageZ(search);
    searchIcon->setStyle(ImageView::Style::Visible | ImageView::Style::Alpha
                         | ImageView::Style::Border);
    searchIcon->setSize(20, 20);
    searchIcon->setBorderColor(0xFF6d6d6d);
    elements.add(new ::ScreenCenter());
    inventoryDragController = new DragController<InventoryDragSource, int>();
    logout = initButton(10, 10, 200, 20, Button::Style::Normal, "Verlassen");
    logout->setMouseEvent([this, zScreen](void* p, void* o, MouseEvent me) {
        if (me.id == ME_RLeft)
        {
            logout->removeStyle(Button::Style::Allowed);
            zScreen->postAction([this, zScreen]() {
                World::INSTANCE->zClient()->leaveGame();
                logout->addStyle(Button::Style::Allowed);
            });
        }
        return 1;
    });
    elements.add(logout);
    debug = initTextField(10,
        40,
        500,
        250,
        TextField::Style::Text | TextField::Style::Multiline,
        "");
    elements.add(debug);
    guiView = new UIMLView("<v/>", uiFactory);
    guiView->addKnownElement(new ItemBarElement());
    guiView->addKnownElement(new StatusBarsElement());
    guiView->setStyle(UIMLView::Style::Visible);
    guiView->setSize(window->zScreen()->getBackBufferSize());
    elements.add(guiView);
    targetUIMLView = new UIMLView("<v/>", uiFactory);
    targetUIMLView->setStyle(
        UIMLView::Style::Background | UIMLView::Style::BAlpha);
    targetUIMLView->setBackgroundColor(0xA0000000);
    elements.add(targetUIMLView);

    filter = initTextField(zScreen->getBackBufferSize().x / 2 - 200,
        zScreen->getBackBufferSize().y - 200,
        400,
        20,
        Framework::TextField::Style::TextField,
        "");
    searchIcon->setPosition(
        filter->getX() + filter->getWidth() - 20, filter->getY());

    chat = new Chat();
    elements.add(chat);

    LTDBFile iconsDat;
    iconsDat.setFile(new Text("data/images/gui_icons.ltdb"));
    iconsDat.readData(0);

    chatButton = uiFactory.createButton(uiFactory.initParam);
    chatButton->setToolTipText("Chat", zScreen, uiFactory.initParam.font);
    chatButton->setAlphaFieldColor(0x5F337AB7);
    chatButton->setSize(40, 40);
    chatButton->setPosition(5, zScreen->getBackBufferSize().y - 45);
    chatButton->addStyle(Framework::Button::Style::BImage
                         | Framework::Button::Style::BAlpha
                         | Framework::Button::Style::Background);
    chatButton->setBackgroundImageZ(iconsDat.load(0, new Text("chat.png")));
    chatButton->setMouseEvent(
        [this](void* p, void* o, Framework::MouseEvent me) {
            if (me.id == ME_RLeft)
            {
                chat->addStyle(Window::Style::Visible);
                chatButton->removeStyle(Button::Style::Visible);
            }
            return 1;
        });
    elements.add(chatButton);

    mapWindow = new MapWindow();
    elements.add(mapWindow);
}

Game::~Game()
{
    inventoryDragController->release();
    filter->release();
    searchIcon->release();
    if (itemListContainer) itemListContainer->release();
}

void Game::updatePosition(
    Vec3<float> position, bool target, Vec3<int> targetPos)
{
    Text txt = "Position: (";
    txt.setPrecision(2);
    txt += position.x;
    txt += ", ";
    txt += position.y;
    txt += ", ";
    txt += position.z;
    txt += ")";
    if (target)
    {
        txt += "\nTarget: (";
        txt += targetPos.x;
        txt += ", ";
        txt += targetPos.y;
        txt += ", ";
        txt += targetPos.z;
        txt += ")\n";
        Block* b = World::INSTANCE->zBlockAt(targetPos);
        if (b)
        {
            txt += "TargetLight: \n";
            txt += b->printLightInfo();
        }
        txt += "Target Chunk: ";
        txt += World::INSTANCE->getChunkCenter(targetPos.x, targetPos.y).x;
        txt += ", ";
        txt += World::INSTANCE->getChunkCenter(targetPos.x, targetPos.y).y;
    }
    debug->setText(txt);
}

void Game::api(char* data)
{
    switch (data[0])
    {
    case 0: // open dialog
        {
            bool exists = 0;
            short len = *(short*)(data + 1);
            char* dialogName = new char[len + 1];
            memcpy(dialogName, data + 3, len);
            dialogName[len] = 0;
            dialogLock.lockRead();
            for (UIMLDialog* dialog : dialogs)
            {
                if (dialog->getName().isEqual(dialogName))
                {
                    exists = 1;
                    break;
                }
            }
            dialogLock.unlockRead();
            delete[] dialogName;
            if (!exists)
            {
                int uimlLen = *(int*)(data + 3 + len);
                char* uiml = new char[uimlLen + 1];
                memcpy(uiml, data + 7 + len, uimlLen);
                uiml[uimlLen] = 0;
                window->zScreen()->postAction([this, uiml]() {
                    UIMLDialog* dialog
                        = new UIMLDialog(uiml, [this](UIMLDialog* dialog) {
                              window->zScreen()->postAction([this, dialog]() {
                                  int index = 0;
                                  dialogLock.lockWrite();
                                  for (UIMLDialog* d : dialogs)
                                  {
                                      if (d == dialog)
                                      {
                                          window->zScreen()->removeMember(d);
                                          dialogs.remove(index);
                                          World::INSTANCE->zKamera()
                                              ->setControlEnabled(
                                                  dialogs.getEntryCount() == 0);
                                          updateRecipieVisibility();
                                          break;
                                      }
                                      index++;
                                  }
                                  dialogLock.unlockWrite();
                              });
                          });
                    dialogLock.lockWrite();
                    dialogs.add(dialog);
                    dialogLock.unlockWrite();
                    updateRecipieVisibility();
                    World::INSTANCE->zKamera()->setControlEnabled(0);
                    window->zScreen()->addMember(dialog);
                    delete[] uiml;
                    });
            }
            break;
        }
    case 1:
        { // element message
            dialogLock.lockRead();
            for (UIMLDialog* dialog : dialogs)
            {
                dialog->api(data + 1);
            }
            dialogLock.unlockRead();
            short idLen = *(short*)(data + 1);
            char* id = new char[idLen + 1];
            memcpy(id, data + 3, idLen);
            id[idLen] = 0;
            NetworkAPIProcessor* processor = dynamic_cast<NetworkAPIProcessor*>(
                guiView->zDrawableById(id));
            int processorId = *(int*)(data + 3 + idLen);
            if (processor
                && (processorId == -1 || processorId == processor->getId()))
            {
                processor->api(data + 7 + idLen);
            }
            delete[] id;
            break;
        }
    case 2:
        { // set gui
            int uimlLen = *(int*)(data + 1);
            char* uiml = new char[uimlLen + 1];
            memcpy(uiml, data + 5, uimlLen);
            uiml[uimlLen] = 0;
            guiView->setUIML(uiml);
            guiView->layout();
            delete[] uiml;
        }
    case 3:
        { // update dialog content
            short dialogNameLen = *(short*)(data + 1);
            char* dialogName = new char[dialogNameLen + 1];
            memcpy(dialogName, data + 3, dialogNameLen);
            dialogName[dialogNameLen] = 0;
            dialogLock.lockRead();
            for (UIMLDialog* dialog : dialogs)
            {
                if (dialog->getName().isEqual(dialogName))
                {
                    int uimlLen = *(int*)(data + 3 + dialogNameLen);
                    char* uiml = new char[uimlLen + 1];
                    memcpy(uiml, data + 7 + dialogNameLen, uimlLen);
                    uiml[uimlLen] = 0;
                    dialog->updateUIML(uiml);
                    delete[] uiml;
                    break;
                }
            }
            dialogLock.unlockRead();
        }
    }
}

void Game::closeCurrentDialog()
{
    dialogLock.lockRead();
    if (dialogs.getEntryCount() > 0)
    {
        UIMLDialog* d = dialogs.get(dialogs.getEntryCount() - 1);
        d->close();
    }
    dialogLock.unlockRead();
}

DragController<InventoryDragSource, int>* Game::zInventoryDragController()
{
    return inventoryDragController;
}

void Game::setTargetUIML(Framework::Text uiml)
{
    if (uiml.getLength())
    {
        targetUIMLView->setUIML(uiml);
        targetUIMLView->layout();
        targetUIMLView->setSize(targetUIMLView->calculateContentSize());
        targetUIMLView->setPosition(
            window->zScreen()->zGraphicsApi()->getBackBufferSize()
            - targetUIMLView->getSize());
        targetUIMLView->addStyle(UIMLView::Style::Visible);
    }
    else
    {
        targetUIMLView->removeStyle(UIMLView::Style::Visible);
    }
}

void Game::updateRecipieVisibility()
{
    if (!recipieVisible)
    {
        if (dialogs.getEntryCount() > 0)
        {
            recipieVisible = 1;
            window->zScreen()->addMember(
                dynamic_cast<Drawable*>(filter->getThis()));
            window->zScreen()->addMember(
                dynamic_cast<Drawable*>(searchIcon->getThis()));
        }
    }
    else
    {
        if (dialogs.getEntryCount() == 0)
        {
            recipieVisible = 0;
            window->zScreen()->removeMember(filter);
            window->zScreen()->removeMember(searchIcon);
            if (itemListContainer)
                itemListContainer->removeStyle(Window::Style::Visible);
        }
    }
}

void Game::showItemList()
{
    if (!itemListContainer)
    {
        itemListContainer = new ItemListContainer();
        window->zScreen()->postAction([this]() {
            window->zScreen()->addMember(
                dynamic_cast<Drawable*>(itemListContainer->getThis()));
        });
    }
    itemListContainer->addStyle(Window::Style::Visible);
}

bool Game::isItemListVisible()
{
    return itemListContainer
        && itemListContainer->hasStyle(Window::Style::Visible);
}

const Text* Game::zFilterText()
{
    return filter->zText();
}

void Game::makeChatButtonVisible()
{
    chatButton->addStyle(Button::Style::Visible);
}

Chat* Game::zChat() const
{
    return chat;
}

MapWindow* Game::zMap() const
{
    return mapWindow;
}

void Game::hide()
{
    Menu::hide();
    window->zScreen()->postAction([this]() {
        if (itemListContainer)
        {
            window->zScreen()->removeMember(itemListContainer);
            itemListContainer->release();
            itemListContainer = 0;
        }
    });
}

ScreenCenter::ScreenCenter()
    : Drawable()
{
    setPosition(0, 0);
    setSize(uiFactory.initParam.bildschirm->getBackBufferSize());
}

void ScreenCenter::render(Image& zRObj)
{
    zRObj.drawLineH(gr.x / 2 - 5, gr.y / 2, 10, 0xFFFFFFFF);
    zRObj.drawLineV(gr.x / 2, gr.y / 2 - 5, 10, 0xFFFFFFFF);
}