#include "Dialog.h"

#include <FileSystem.h>
#include <InMemoryBuffer.h>
#include <KeyboardEvent.h>
#include <TextField.h>
#include <XML.h>

#include "CraftingGrid.h"
#include "CraftingRecipies.h"
#include "Equipment.h"
#include "Globals.h"
#include "InventoryView.h"
#include "ItemStack.h"
#include "ListView.h"
#include "QuestGraph.h"
#include "RecipieIngredient.h"
#include "RecipieOutput.h"
#include "ShapedRecipie.h"
#include "UIMLCraftingProgress.h"
#include "UIMLFuelState.h"
#include "UnshapedRecipie.h"

using namespace Framework;

UIMLDialog::UIMLDialog(
    Framework::Text uiml, std::function<void(UIMLDialog* self)> onClose)
    : Window()
{
    XML::Element* xml = new XML::Element(uiml);
    view = new UIMLView("<v/>", uiFactory);
    view->setStyle(UIMLView::Style::Allowed | UIMLView::Style::Visible
                   | UIMLView::Style::GlobalMouseEvent);
    view->setMouseEvent(_ret1ME);
    view->addKnownElement(new InventoryElement());
    view->addKnownElement(new EquipmentElement());
    view->addKnownElement(new CraftingGridElement());
    view->addKnownElement(new CraftingRecipiesElement());
    view->addKnownElement(new RecipieIngredientElement());
    view->addKnownElement(new RecipieOutputElement());
    view->addKnownElement(new ShapedRecipieElement());
    view->addKnownElement(new UnshapedRecipieElement());
    view->addKnownElement(new ListViewElement());
    view->addKnownElement(new QuestGraphElement());
    view->addKnownElement(new QuestGraphItemElement());
    view->addKnownElement(new ItemStackElement());
    view->addKnownElement(new UIMLCraftingProgressElement());
    view->addKnownElement(new UIMLFuelStateElement());
    view->setOnMemberMouseEvent([](Framework::XML::Element& element,
                                    Framework::Drawable& z,
                                    MouseEvent me) {
        if (me.id == ME_RLeft)
        {
            Framework::Text onClick = element.getAttributeValue("onClick");
            if (onClick.getLength() == 0)
            {
                return !element.hasAttribute("onClick");
            }
            Framework::Text* dialogName
                = onClick.getTeilText(0, onClick.positionOf(";"));
            Framework::Text* tail
                = onClick.getTeilText(onClick.positionOf(";") + 1);
            Framework::InMemoryBuffer buffer;
            buffer.write("\0", 1); // element message
            while (tail->getLength() > 0)
            {
                Framework::Text* current = 0;
                if (tail->positionOf(";") >= 0)
                {
                    current = tail->getTeilText(0, tail->positionOf(";"));
                    Framework::Text* tmp
                        = tail->getTeilText(tail->positionOf(";") + 1);
                    tail->release();
                    tail = tmp;
                }
                else
                {
                    current = new Text(*tail);
                    tail->setText("");
                }
                if (current->positionOf("(char)") == 0)
                {
                    current->replace("(char)", "");
                    char d = (char)(int)*current;
                    buffer.write(&d, 1);
                }
                else if (current->positionOf("(short)") == 0)
                {
                    current->replace("(short)", "");
                    short d = (short)(int)*current;
                    buffer.write((char*)&d, 2);
                }
                else if (current->positionOf("(int)") == 0)
                {
                    current->replace("(int)", "");
                    int d = (int)*current;
                    buffer.write((char*)&d, 4);
                }
                else if (current->positionOf("(__int64)") == 0)
                {
                    current->replace("(__int64)", "");
                    __int64 d = (__int64)*current;
                    buffer.write((char*)&d, 8);
                }
                else if (current->positionOf("(float)") == 0)
                {
                    current->replace("(float)", "");
                    float d = (float)*current;
                    buffer.write((char*)&d, 4);
                }
                else if (current->positionOf("(double)") == 0)
                {
                    current->replace("(double)", "");
                    double d = (double)*current;
                    buffer.write((char*)&d, 8);
                }
                else
                {
                    unsigned char len = (unsigned char)current->getLength();
                    buffer.write((char*)&len, 1);
                    buffer.write(*current, len);
                }
                current->release();
            }
            char* message = new char[buffer.getSize()];
            buffer.read(message, (int)buffer.getSize());
            World::INSTANCE->zClient()->uiRequest(
                *dialogName, message, (int)buffer.getSize());
            delete[] message;
            dialogName->release();
            tail->release();
        }
        return (bool)1;
    });
    view->setUIML(xml);
    view->setSize((int)xml->getAttributeValue("width"),
        (int)xml->getAttributeValue("height"));
    name = xml->getAttributeValue("id");
    view->layout();
    if (!xml->hasAttribute("width"))
    {
        view->setSize(view->calculateContentSize().x, view->getHeight());
    }
    if (!xml->hasAttribute("height"))
    {
        view->setSize(view->getWidth(), view->calculateContentSize().y);
    }
    bool notifyOnClose = 0;
    int notifyOnCloseDimension = -1;
    Vec3<int> notifyOnCloseBlock;
    if (xml->hasAttribute("notifyOnClose"))
    {
        notifyOnClose = 1;
        Text value = xml->getAttributeValue("notifyOnClose");
        if (value.has(","))
        {
            Text* first = value.getTeilText(0, value.positionOf(",", 0) + 1);
            Text* second = value.getTeilText(
                value.positionOf(",", 0) + 1, value.positionOf(",", 1));
            Text* third = value.getTeilText(
                value.positionOf(",", 1) + 1, value.positionOf(",", 2));
            Text* forth = value.getTeilText(value.positionOf(",", 2) + 1);
            notifyOnCloseDimension = (int)*first;
            notifyOnCloseBlock.x = (int)*second;
            notifyOnCloseBlock.y = (int)*third;
            notifyOnCloseBlock.z = (int)*forth;
            first->release();
            second->release();
            third->release();
            forth->release();
        } // TODO: else entity id
    }
    addMember(view);

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

    setStyle(Window::Style::Visible | Window::Style::Allowed
             | Window::Style::Border | Window::Style::BodyBAlpha
             | Window::Style::Movable | Window::Style::Title
             | Window::Style::TitleBAlpha | Window::Style::Closable
             | Window::Style::ClosingBAlpha | Window::Style::ClosingBuffer
             | Window::Style::TitleBackground | Window::Style::BodyBackground
             | Window::Style::ClosingBackground | Window::Style::MEIgnoreInside
             | Style::ClosingBuffer | Style::ClosingBImage);
    setBodyBgColor(0xA0000000);
    setTBgColor(0xA0000000);
    setCloseBgColor(0xA0000000);
    setSize(view->getWidth() + 4, view->getHeight() + 24);
    setPosition(window->zScreen()->getBackBufferSize() / 2 - getSize() / 2);
    setBorderWidth(2);
    this->onClose = [onClose,
                        notifyOnClose,
                        notifyOnCloseDimension,
                        notifyOnCloseBlock,
                        this](UIMLDialog* self) {
        if (notifyOnClose)
        {
            if (notifyOnCloseDimension >= 0)
            {
                char* msg = new char[name.getLength() + 3];
                msg[0] = 1; // dialog closed
                *(short*)(msg + 1) = (short)name.getLength();
                memcpy(msg + 3, name.getText(), name.getLength());
                World::INSTANCE->zClient()->blockAPIRequest(
                    notifyOnCloseDimension,
                    notifyOnCloseBlock,
                    msg,
                    name.getLength() + 3);
                delete[] msg;
            } // TODO: else entity notification
        }
        onClose(this);
    };
    setClosingMe(
        [notifyOnClose, notifyOnCloseDimension, notifyOnCloseBlock, this](
            void* p, void* o, MouseEvent me) {
            if (me.id == ME_RLeft)
            {
                this->close();
            }
            return 1;
        });
    setBorderColor(0xFF52525E);
    setTitel(xml->getAttributeValue("title"));
    setTFontZ(dynamic_cast<Font*>(uiFactory.initParam.font->getThis()));
    zTTextField()->setSize(0, 20);
    zTTextField()->addStyle(TextField::Style::Center);
    setKeyboardEvent(_ret1TE);
    setCloseAfStrength(10);
    setCloseAfColor(0x5F9C0A0A);
    setCloseBgImageZ(iconsDat.load(0, new Text("close.png")));
    setCloseClickAfColor(0xFF9C0A0A);
    setCloseClickAfStrength(10);
}

UIMLDialog::~UIMLDialog() {}

void UIMLDialog::api(char* message)
{
    short idLen = *(short*)message;
    char* id = new char[idLen + 1];
    memcpy(id, message + 2, idLen);
    id[idLen] = 0;
    NetworkAPIProcessor* processor
        = dynamic_cast<NetworkAPIProcessor*>(view->zDrawableById(id));
    if (processor
        && (*(int*)(message + 2 + idLen) == -1 // global message
            || processor->getId() == *(int*)(message + 2 + idLen)))
    {
        processor->api(message + 6 + idLen);
    }
    delete[] id;
}

const Framework::Text& UIMLDialog::getName() const
{
    return name;
}

void UIMLDialog::updateUIML(const char* uiml)
{
    XML::Element* xml = new XML::Element(uiml);
    uiFactory.initParam.bildschirm->postAction([this, xml]() {
        view->setSize((int)xml->getAttributeValue("width"),
            (int)xml->getAttributeValue("height"));
        view->setUIML(xml);
        view->layout();
        if (!xml->hasAttribute("width"))
        {
            view->setSize(view->calculateContentSize().x, view->getHeight());
        }
        if (!xml->hasAttribute("height"))
        {
            view->setSize(view->getWidth(), view->calculateContentSize().y);
        }
        Point oldSize = getSize();
        setSize(view->getWidth() + 4, view->getHeight() + 24);
        setPosition(getPosition() + (oldSize - getSize()) / 2);
    });
}

void UIMLDialog::close()
{
    World::INSTANCE->zClient()->uiRequest(name, "\1", 1);
    onClose(this);
}