#include "UIMLView.h"

#include "Border.h"
#include "Button.h"
#include "Font.h"
#include "Image.h"
#include "RCPointer.h"
#include "Screen.h"
#include "Scroll.h"
#include "Table.h"
#include "TextField.h"
#include "Window.h"
#include "XML.h"

using namespace Framework;

UIMLElement::UIMLElement()
    : ReferenceCounter()
{}

UIMLElement::~UIMLElement() {}

//! applies the layout parameters to a Drawable
void UIMLElement::layout(XML::Element& element,
    Drawable& z,
    int pWidth,
    int pHeight,
    UIMLContainer& generalLayouter)
{
    int width = z.getWidth();
    int height = z.getHeight();
    if (element.hasAttribute("style"))
    {
        z.setStyle((__int64)element.getAttributeValue("style"));
    }
    if (element.hasAttribute("width"))
    {
        Text w = element.getAttributeValue("width");
        if (!w.isEqual("auto"))
        {
            width = calculateSize(w, pWidth);
        }
    }
    if (element.hasAttribute("height"))
    {
        Text h = element.getAttributeValue("height");
        if (!h.isEqual("auto"))
        {
            height = calculateSize(h, pHeight);
        }
    }
    z.setSize(width, height);
    if (element.hasAttribute("align-x"))
    {
        Text la = element.getAttributeValue("align-x");
        int x = 0;
        if (la.isEqual("start"))
            x = 0;
        else if (la.isEqual("end"))
            x = pWidth;
        else if (la.isEqual("center"))
            x = pWidth / 2 - width / 2;
        else
        {
            XML::Editor ed
                = element.zParent()->selectChildsByAttribute("id", la);
            if (ed.exists())
            {
                generalLayouter.layout(*ed.begin().val(),
                    *generalLayouter.zDrawableById(la),
                    pWidth,
                    pHeight,
                    generalLayouter);
                Drawable* laz = generalLayouter.zDrawableById(la);
                if (laz) x = laz->getX();
            }
        }
        if (element.hasAttribute("margin-x"))
        {
            Text mt = element.getAttributeValue("margin-x");
            x += calculateSize(mt, pWidth);
        }
        z.setX(x);
    }
    else if (element.hasAttribute("align-left"))
    {
        Text la = element.getAttributeValue("align-left");
        int x = 0;
        if (la.isEqual("start"))
            x = 0;
        else if (la.isEqual("end"))
            x = pWidth;
        else if (la.isEqual("center"))
            x = pWidth / 2 - width / 2;
        else
        {
            XML::Editor ed
                = element.zParent()->selectChildsByAttribute("id", la);
            if (ed.exists())
            {
                generalLayouter.layout(*ed.begin().val(),
                    *generalLayouter.zDrawableById(la),
                    pWidth,
                    pHeight,
                    generalLayouter);
                Drawable* laz = generalLayouter.zDrawableById(la);
                if (laz) x = laz->getX() + laz->getWidth();
            }
        }
        if (element.hasAttribute("margin-left"))
        {
            Text mt = element.getAttributeValue("margin-left");
            x += calculateSize(mt, pWidth);
        }
        z.setX(x);
    }
    else if (element.hasAttribute("align-right"))
    {
        Text ra = element.getAttributeValue("align-right");
        int x = 0;
        if (ra.isEqual("start"))
            x = -z.getWidth();
        else if (ra.isEqual("end"))
            x = pWidth - z.getWidth();
        else if (ra.isEqual("center"))
            x = pWidth / 2 - width / 2;
        else
        {
            XML::Editor ed
                = element.zParent()->selectChildsByAttribute("id", ra);
            if (ed.exists())
            {
                generalLayouter.layout(*ed.begin().val(),
                    *generalLayouter.zDrawableById(ra),
                    pWidth,
                    pHeight,
                    generalLayouter);
                Drawable* raz = generalLayouter.zDrawableById(ra);
                if (raz) x = raz->getX() - z.getWidth();
            }
        }
        if (element.hasAttribute("margin-right"))
        {
            Text mt = element.getAttributeValue("margin-right");
            x -= calculateSize(mt, pWidth);
        }
        z.setX(x);
    }
    if (element.hasAttribute("align-y"))
    {
        Text ta = element.getAttributeValue("align-y");
        int y = 0;
        if (ta.isEqual("start"))
            y = 0;
        else if (ta.isEqual("end"))
            y = pHeight;
        else if (ta.isEqual("center"))
            y = pHeight / 2 - height / 2;
        else
        {
            XML::Editor ed
                = element.zParent()->selectChildsByAttribute("id", ta);
            if (ed.exists())
            {
                generalLayouter.layout(*ed.begin().val(),
                    *generalLayouter.zDrawableById(ta),
                    pWidth,
                    pHeight,
                    generalLayouter);
                Drawable* taz = generalLayouter.zDrawableById(ta);
                if (taz) y = taz->getY();
            }
        }
        if (element.hasAttribute("margin-y"))
        {
            Text mt = element.getAttributeValue("margin-y");
            y += calculateSize(mt, pHeight);
        }
        z.setY(y);
    }
    else if (element.hasAttribute("align-top"))
    {
        Text ta = element.getAttributeValue("align-top");
        int y = 0;
        if (ta.isEqual("start"))
            y = 0;
        else if (ta.isEqual("end"))
            y = pHeight;
        else if (ta.isEqual("center"))
            y = pHeight / 2 - height / 2;
        else
        {
            XML::Editor ed
                = element.zParent()->selectChildsByAttribute("id", ta);
            if (ed.exists())
            {
                generalLayouter.layout(*ed.begin().val(),
                    *generalLayouter.zDrawableById(ta),
                    pWidth,
                    pHeight,
                    generalLayouter);
                Drawable* taz = generalLayouter.zDrawableById(ta);
                if (taz) y = taz->getY() + taz->getHeight();
            }
        }
        if (element.hasAttribute("margin-top"))
        {
            Text mt = element.getAttributeValue("margin-top");
            y += calculateSize(mt, pHeight);
        }
        z.setY(y);
    }
    else if (element.hasAttribute("align-bottom"))
    {
        Text ba = element.getAttributeValue("align-bottom");
        int y = 0;
        if (ba.isEqual("start"))
            y = -z.getHeight();
        else if (ba.isEqual("end"))
            y = pHeight - z.getHeight();
        else if (ba.isEqual("center"))
            y = pHeight / 2 - height / 2;
        else
        {
            XML::Editor ed
                = element.zParent()->selectChildsByAttribute("id", ba);
            if (ed.exists())
            {
                generalLayouter.layout(*ed.begin().val(),
                    *generalLayouter.zDrawableById(ba),
                    pWidth,
                    pHeight,
                    generalLayouter);
                Drawable* baz = generalLayouter.zDrawableById(ba);
                if (baz) y = baz->getY() - z.getHeight();
            }
        }
        if (element.hasAttribute("margin-bottom"))
        {
            Text mt = element.getAttributeValue("margin-bottom");
            y -= calculateSize(mt, pHeight);
        }
        z.setY(y);
    }
    int x = z.getX();
    int y = z.getY();
    if (element.hasAttribute("x"))
    {
        Text xt = element.getAttributeValue("x");
        x = calculateSize(xt, pWidth);
    }
    if (element.hasAttribute("y"))
    {
        Text yt = element.getAttributeValue("y");
        y = calculateSize(yt, pHeight);
    }
    z.setPosition(x, y);
    DrawableBackground* zh = dynamic_cast<DrawableBackground*>(&z);
    if (zh)
    {
        if (element.hasAttribute("border"))
        {
            int br = (int)element.getAttributeValue("border");
            zh->setStyle(DrawableBackground::Style::Border, br > 0);
            zh->setBorderWidth((int)element.getAttributeValue("border"));
        }
        if (element.hasAttribute("border-color"))
        {
            zh->setBorderColor((int)element.getAttributeValue("border-color"));
        }
    }
    // recursive layout
    pWidth = z.getInnerWidth();
    pHeight = z.getInnerHeight();
    for (auto i : getChildren(element))
    {
        Drawable* z = 0;
        if (i->hasAttribute("id"))
        {
            z = generalLayouter.zDrawableById(i->getAttributeValue("id"));
        }
        if (z)
        {
            generalLayouter.layout(*i, *z, pWidth, pHeight, generalLayouter);
        }
    }
}

int Framework::UIMLElement::calculateSize(Text attributeValue, int parentSize)
{
    attributeValue.removeWhitespaceAfter(0);
    attributeValue.removeWhitespaceBefore(attributeValue.getLength());
    if (attributeValue.has('('))
    {
        int start = -1;
        int len = attributeValue.getLength();
        for (int i = 0; i < len; i++)
        {
            if (attributeValue.getText()[i] == '(') start = i;
            if (attributeValue.getText()[i] == ')')
            {
                if (start < 0) return 0;
                Text* part = attributeValue.getTeilText(start + 1, i);
                int value = calculateSize(*part, parentSize);
                part->release();
                attributeValue.remove(start, i + 1);
                attributeValue.insert(start, Text(value));
                return calculateSize(attributeValue, parentSize);
            }
        }
        return 0;
    }
    else if (attributeValue.has('*') || attributeValue.has('/'))
    {
        int posD = attributeValue.positionOf('/');
        int posM = attributeValue.positionOf('*');
        if ((posD >= 0 && posD < posM) || posM < 0)
        {
            Text* firstPart = attributeValue.getTeilText(0, posD);
            Text* secondPart = attributeValue.getTeilText(posD + 1);
            int first = calculateSize(*firstPart, parentSize);
            int second = calculateSize(*secondPart, parentSize);
            firstPart->release();
            secondPart->release();
            return first / second;
        }
        else
        {
            Text* firstPart = attributeValue.getTeilText(0, posM);
            Text* secondPart = attributeValue.getTeilText(posM + 1);
            int first = calculateSize(*firstPart, parentSize);
            int second = calculateSize(*secondPart, parentSize);
            firstPart->release();
            secondPart->release();
            return first * second;
        }
    }
    else if (attributeValue.has('+') || attributeValue.has('-'))
    {
        int posP = attributeValue.positionOf('+');
        int posN = attributeValue.positionOf('-');
        if ((posP >= 0 && posP < posN) || posN < 0)
        {
            Text* firstPart = attributeValue.getTeilText(0, posP);
            Text* secondPart = attributeValue.getTeilText(posP + 1);
            int first = calculateSize(*firstPart, parentSize);
            int second = calculateSize(*secondPart, parentSize);
            firstPart->release();
            secondPart->release();
            return first + second;
        }
        else
        {
            Text* firstPart = attributeValue.getTeilText(0, posN);
            Text* secondPart = attributeValue.getTeilText(posN + 1);
            int first = calculateSize(*firstPart, parentSize);
            int second = calculateSize(*secondPart, parentSize);
            firstPart->release();
            secondPart->release();
            return first - second;
        }
    }
    else if (attributeValue.hasAt(attributeValue.getLength() - 1, "%"))
    {
        return (int)((int)attributeValue * (parentSize / 100.0));
    }
    else
    {
        return (int)attributeValue;
    }
}

XML::Editor Framework::UIMLElement::getChildren(XML::Element& element)
{
    return element.selectChildren();
}

UIMLContainer::UIMLContainer()
    : UIMLElement()
{}

UIMLContainer::~UIMLContainer() {}

UIMLTextField::UIMLTextField()
    : UIMLElement()
{}

bool UIMLTextField::isApplicableFor(XML::Element& element)
{
    return element.getName().isEqual("textfield");
}

Drawable* UIMLTextField::parseElement(
    XML::Element& element, UIMLContainer& generalFactory)
{
    TextField* t = generalFactory.getFactory().createTextField(
        generalFactory.getFactory().initParam);
    t->setText(element.getText());
    updateElement(element, *t, generalFactory);
    return t;
}

bool Framework::UIMLTextField::updateElement(
    XML::Element& element, Drawable& z, UIMLContainer& generalFactory)
{
    TextField* t = dynamic_cast<TextField*>(&z);
    if (!t) return false;
    if (!element.hasAttribute("style"))
    {
        TextField* tmp = generalFactory.getFactory().createTextField(
            generalFactory.getFactory().initParam);
        tmp->addStyle(TextField::Style::TextField);
        t->setStyle(tmp->getStyles());
        tmp->release();
    }
    return true;
}

void UIMLTextField::layout(XML::Element& element,
    Drawable& z,
    int pWidth,
    int pHeight,
    UIMLContainer& generalLayouter)
{
    TextField* tf = dynamic_cast<TextField*>(&z);
    if (element.hasAttribute("font-size"))
    {
        tf->setFontSize(
            (unsigned char)(int)element.getAttributeValue("font-size"));
    }
    ((TextField*)&z)->setText(element.getText());
    if (element.hasAttribute("width"))
    {
        Text w = element.getAttributeValue("width");
        if (w.isEqual("auto"))
        {
            z.setWidth(((TextField*)&z)->getNeededWidth());
        }
    }
    if (element.hasAttribute("height"))
    {
        Text h = element.getAttributeValue("height");
        if (h.isEqual("auto"))
        {
            z.setHeight(((TextField*)&z)->getNeededHeight());
        }
    }
    if (element.hasAttribute("text-color"))
    {
        tf->setFontColor((int)element.getAttributeValue("text-color"));
    }
    UIMLElement::layout(element, z, pWidth, pHeight, generalLayouter);
    if (element.hasAttribute("text-align-horizontal"))
        z.setStyle(TextField::Style::HCenter,
            element.getAttributeValue("text-align-horizontal")
                .isEqual("center"));
    if (element.hasAttribute("text-align-vertical"))
        z.setStyle(TextField::Style::VCenter,
            element.getAttributeValue("text-align-vertical").isEqual("center"));
    if (element.hasAttribute("disabled"))
        z.removeStyle(TextField::Style::Editable);
    if (element.getAttributeValue("vScroll").isEqual("auto"))
    {
        tf->setStyle(TextField::Style::VScroll,
            tf->getNeededHeight() > tf->getInnerHeight());
        tf->setVerticalClickScroll(tf->getFontSize());
        tf->updateVScroll();
    }
    if (element.getAttributeValue("hScroll").isEqual("auto"))
    {
        tf->setStyle(TextField::Style::HScroll,
            tf->getNeededWidth() > tf->getInnerWidth());
        tf->setHorizontalClickScroll(tf->getFontSize());
        tf->updateHScroll();
    }
}

UIMLButton::UIMLButton()
    : UIMLElement()
{}

bool UIMLButton::isApplicableFor(XML::Element& element)
{
    return element.getName().isEqual("button");
}

Drawable* UIMLButton::parseElement(
    XML::Element& element, UIMLContainer& generalFactory)
{
    Button* k = generalFactory.getFactory().createButton(
        generalFactory.getFactory().initParam);
    updateElement(element, *k, generalFactory);
    return k;
}

bool Framework::UIMLButton::updateElement(
    XML::Element& element, Drawable& z, UIMLContainer& generalFactory)
{
    Button* k = dynamic_cast<Button*>(&z);
    if (!k) return false;
    if (!element.hasAttribute("style"))
    {
        Button* tmp = generalFactory.getFactory().createButton(
            generalFactory.getFactory().initParam);
        k->setStyle(tmp->getStyles());
        tmp->release();
    }
    return true;
}

void UIMLButton::layout(XML::Element& element,
    Drawable& z,
    int pWidth,
    int pHeight,
    UIMLContainer& generalLayouter)
{
    if (element.hasAttribute("font-size"))
        ((Button*)&z)
            ->setFontSize(
                (unsigned char)(int)element.getAttributeValue("font-size"));
    ((Button*)&z)->setText(element.getText());
    UIMLElement::layout(element, z, pWidth, pHeight, generalLayouter);
}

UIMLCheck::UIMLCheck()
    : UIMLElement()
{}

bool UIMLCheck::isApplicableFor(XML::Element& element)
{
    return element.getName().isEqual("check");
}

Drawable* UIMLCheck::parseElement(
    XML::Element& element, UIMLContainer& generalFactory)
{
    CheckBox* k = generalFactory.getFactory().createCheckBox(
        generalFactory.getFactory().initParam);
    updateElement(element, *k, generalFactory);
    return k;
}

bool Framework::UIMLCheck::updateElement(
    XML::Element& element, Drawable& z, UIMLContainer& generalFactory)
{
    CheckBox* k = dynamic_cast<CheckBox*>(&z);
    if (!k) return false;
    if (!element.hasAttribute("style"))
    {
        CheckBox* tmp = generalFactory.getFactory().createCheckBox(
            generalFactory.getFactory().initParam);
        k->setStyle(tmp->getStyles());
        tmp->release();
    }
    return true;
}

void UIMLCheck::layout(XML::Element& element,
    Drawable& z,
    int pWidth,
    int pHeight,
    UIMLContainer& generalLayouter)
{
    ((CheckBox*)&z)->setText(element.getText());
    ((CheckBox*)&z)->setSText(element.getText());
    z.setStyle(CheckBox::Style::Selected, element.hasAttribute("selected"));
    if (element.hasAttribute("font-size"))
        ((CheckBox*)&z)
            ->setSSize(
                (unsigned char)(int)element.getAttributeValue("font-size"));
    UIMLElement::layout(element, z, pWidth, pHeight, generalLayouter);
}

UIMLText::UIMLText()
    : UIMLElement()
{}

bool UIMLText::isApplicableFor(XML::Element& element)
{
    return element.getName().isEqual("text");
}

Drawable* UIMLText::parseElement(
    XML::Element& element, UIMLContainer& generalFactory)
{
    TextField* t = generalFactory.getFactory().createTextField(
        generalFactory.getFactory().initParam);
    t->setText(element.getText());
    updateElement(element, *t, generalFactory);
    return t;
}

bool Framework::UIMLText::updateElement(
    XML::Element& element, Drawable& z, UIMLContainer& generalFactory)
{
    TextField* t = dynamic_cast<TextField*>(&z);
    if (!t) return false;
    if (!element.hasAttribute("style"))
    {
        TextField* tmp = generalFactory.getFactory().createTextField(
            generalFactory.getFactory().initParam);
        tmp->addStyle(TextField::Style::Text);
        t->setStyle(tmp->getStyles());
        tmp->release();
    }
    return true;
}

void UIMLText::layout(XML::Element& element,
    Drawable& z,
    int pWidth,
    int pHeight,
    UIMLContainer& generalLayouter)
{
    TextField* tf = dynamic_cast<TextField*>(&z);
    if (element.hasAttribute("font-size"))
    {
        tf->setFontSize(
            (unsigned char)(int)element.getAttributeValue("font-size"));
    }
    tf->setText(element.getText());
    if (element.hasAttribute("width"))
    {
        Text w = element.getAttributeValue("width");
        if (w.isEqual("auto"))
        {
            z.setWidth(tf->getNeededWidth());
        }
    }
    if (element.hasAttribute("height"))
    {
        Text h = element.getAttributeValue("height");
        if (h.isEqual("auto"))
        {
            z.setHeight(tf->getNeededHeight());
        }
    }
    if (element.hasAttribute("text-color"))
    {
        tf->setFontColor((int)element.getAttributeValue("text-color"));
    }
    UIMLElement::layout(element, z, pWidth, pHeight, generalLayouter);
    if (element.hasAttribute("text-align-horizontal"))
        z.setStyle(TextField::Style::HCenter,
            element.getAttributeValue("text-align-horizontal")
                .isEqual("center"));
    if (element.hasAttribute("text-align-vertical"))
        z.setStyle(TextField::Style::VCenter,
            element.getAttributeValue("text-align-vertical").isEqual("center"));
    if (element.hasAttribute("disabled"))
        z.removeStyle(TextField::Style::Editable);
    if (element.getAttributeValue("vScroll").isEqual("auto"))
    {
        tf->setStyle(TextField::Style::VScroll,
            tf->getNeededHeight() > tf->getInnerHeight());
        tf->setVerticalClickScroll(tf->getFontSize());
        tf->updateVScroll();
    }
    if (element.getAttributeValue("hScroll").isEqual("auto"))
    {
        tf->setStyle(TextField::Style::HScroll,
            tf->getNeededWidth() > tf->getInnerWidth());
        tf->setHorizontalClickScroll(tf->getFontSize());
        tf->updateHScroll();
    }
}

UIMLTextArea::UIMLTextArea()
    : UIMLElement()
{}

bool UIMLTextArea::isApplicableFor(XML::Element& element)
{
    return element.getName().isEqual("textarea");
}

Drawable* UIMLTextArea::parseElement(
    XML::Element& element, UIMLContainer& generalFactory)
{
    TextField* t = generalFactory.getFactory().createTextField(
        generalFactory.getFactory().initParam);
    t->setText(element.getText());
    updateElement(element, *t, generalFactory);
    return t;
}

bool Framework::UIMLTextArea::updateElement(
    XML::Element& element, Drawable& z, UIMLContainer& generalFactory)
{
    TextField* t = dynamic_cast<TextField*>(&z);
    if (!t) return false;
    if (!element.hasAttribute("style"))
    {
        TextField* tmp = generalFactory.getFactory().createTextField(
            generalFactory.getFactory().initParam);
        tmp->addStyle(TextField::Style::TextGebiet);
        t->setStyle(tmp->getStyles());
        tmp->release();
    }
    return true;
}

void UIMLTextArea::layout(XML::Element& element,
    Drawable& z,
    int pWidth,
    int pHeight,
    UIMLContainer& generalLayouter)
{
    TextField* tf = dynamic_cast<TextField*>(&z);
    if (element.hasAttribute("font-size"))
    {
        tf->setFontSize(
            (unsigned char)(int)element.getAttributeValue("font-size"));
    }
    if (element.hasAttribute("disabled"))
        z.removeStyle(TextField::Style::Editable);
    tf->setText(element.getText());
    tf->zTextRenderer()->formatText(tf->zText(), z.getInnerWidth());
    if (element.hasAttribute("width"))
    {
        Text w = element.getAttributeValue("width");
        if (w.isEqual("auto"))
        {
            z.setWidth(((TextField*)&z)->getNeededWidth());
        }
    }
    if (element.hasAttribute("height"))
    {
        Text h = element.getAttributeValue("height");
        if (h.isEqual("auto"))
        {
            z.setHeight(((TextField*)&z)->getNeededHeight());
        }
    }
    if (element.hasAttribute("text-color"))
    {
        tf->setFontColor((int)element.getAttributeValue("text-color"));
    }
    UIMLElement::layout(element, z, pWidth, pHeight, generalLayouter);
    if (element.hasAttribute("text-align-horizontal"))
        z.setStyle(TextField::Style::HCenter,
            element.getAttributeValue("text-align-horizontal")
                .isEqual("center"));
    if (element.hasAttribute("text-align-vertical"))
        z.setStyle(TextField::Style::VCenter,
            element.getAttributeValue("text-align-vertical").isEqual("center"));
    if (element.hasAttribute("font-size"))
        if (element.getAttributeValue("vScroll").isEqual("auto"))
        {
            tf->setStyle(TextField::Style::VScroll,
                tf->getNeededHeight() > tf->getInnerHeight());
            tf->setVerticalClickScroll(tf->getFontSize());
            tf->updateVScroll();
        }
    if (element.getAttributeValue("hScroll").isEqual("auto"))
    {
        tf->setStyle(TextField::Style::HScroll,
            tf->getNeededWidth() > tf->getInnerWidth());
        tf->setHorizontalClickScroll(tf->getFontSize());
        tf->updateHScroll();
    }
}

UIMLTable::UIMLTable()
    : UIMLElement()
{}

bool UIMLTable::isApplicableFor(XML::Element& element)
{
    return element.getName().isEqual("table");
}

Drawable* UIMLTable::parseElement(
    XML::Element& element, UIMLContainer& generalFactory)
{
    ObjTable* t = generalFactory.getFactory().createObjTable(
        generalFactory.getFactory().initParam);
    updateElement(element, *t, generalFactory);
    return t;
}

DLLEXPORT bool Framework::UIMLTable::updateElement(
    XML::Element& element, Drawable& z, UIMLContainer& generalFactory)
{
    ObjTable* t = dynamic_cast<ObjTable*>(&z);
    if (!t) return false;
    int index = 0;
    int linePos = 0;
    int numCols = 0;
    Text currentId = element.getAttributeValue("id");
    for (auto i = element.getChilds(); i; i++)
    {
        Text id;
        if (i->hasAttribute("id"))
            id = i->getAttributeValue("id");
        else
        {
            id = currentId + "_tr_" + index++;
            i->setAttribute("id", id);
        }
        if (i->getName().isEqual("tr"))
        {
            if (t->getRowNumber(id) == -1) t->addRow(id);
            t->setRowPosition(id, linePos);
            int c = 1;
            for (auto j = i->getChilds(); j; j++)
            {
                if (t->getColumnCount() < c) t->addColumn(Text(c - 1));
                if (numCols < c) numCols = c;
                Drawable* z = t->zDrawable(Text(c - 1), id);
                if (!z
                    || !generalFactory.updateElement(
                        element, *z, generalFactory))
                {
                    z = generalFactory.parseElement(*j.val(), generalFactory);
                    if (z)
                    {
                        if (z->getWidth() > t->getColumnWidth(Text(c - 1)))
                            t->setColumnWidth(Text(c - 1), z->getWidth());
                        if (z->getHeight() > t->getRowHeight(Text(id)))
                            t->setRowHeight(Text(id), z->getHeight());
                        t->setDrawableZ(Text(c - 1), id, z);
                    }
                }
                c++;
            }
        }
        linePos++;
    }
    for (int i = 0; i < t->getRowCount(); i++)
    { // remove all lines that are not in the xml
        if (!element.selectChildsByName("tr")
                .whereAttributeEquals("id", *t->zRowName(i))
                .exists())
        {
            t->removeRow(i);
            i--;
        }
    }
    for (int i = numCols; i < t->getColumnCount(); i++)
    { // remove all columns that are not in the xml
        t->removeColumn(i);
        i--;
    }
    if (!element.hasAttribute("style"))
    {
        ObjTable* tmp = generalFactory.getFactory().createObjTable(
            generalFactory.getFactory().initParam);
        t->setStyle(tmp->getStyles());
        tmp->release();
    }
    return true;
}

void UIMLTable::layout(XML::Element& element,
    Drawable& z,
    int pWidth,
    int pHeight,
    UIMLContainer& generalLayouter)
{
    if (element.hasAttribute("scroll"))
    {
        z.setStyle(ObjTable::Style::HScroll,
            element.getAttributeValue("scroll").isEqual("horizontal"));
        z.setStyle(ObjTable::Style::VScroll,
            element.getAttributeValue("scroll").isEqual("vertical"));
        z.setStyle(ObjTable::Style::scroll,
            element.getAttributeValue("scroll").isEqual("both"));
    }
    UIMLElement::layout(element, z, pWidth, pHeight, generalLayouter);
    ObjTable* objT = (ObjTable*)&z;
    if (objT->getRowCount() > 0)
    {
        if (element.hasAttribute("line-height"))
        {
            int height = (int)element.getAttributeValue("line-height");
            for (int i = 0; i < objT->getRowCount(); i++)
                objT->setRowHeight(i, height);
        }
        for (int i = 0; i < objT->getColumnCount(); i++)
        {
            if (objT->zDrawable(i, 0))
                objT->setColumnWidth(i, objT->zDrawable(i, 0)->getWidth());
        }
    }
}

XML::Editor Framework::UIMLTable::getChildren(XML::Element& element)
{
    return element.selectChildren().selectChildren();
}

UIMLFrame::UIMLFrame()
    : UIMLElement()
{}

bool UIMLFrame::isApplicableFor(XML::Element& element)
{
    return element.getName().isEqual("frame");
}

Drawable* UIMLFrame::parseElement(
    XML::Element& element, UIMLContainer& generalFactory)
{
    Window* f = generalFactory.getFactory().createWindow(
        generalFactory.getFactory().initParam);
    updateElement(element, *f, generalFactory);
    return f;
}

bool Framework::UIMLFrame::updateElement(
    XML::Element& element, Drawable& z, UIMLContainer& generalFactory)
{
    Window* f = dynamic_cast<Window*>(&z);
    if (!f) return false;
    for (auto member = f->getMembers().begin(); member;)
    { // remove all members that are not in the xml
        Framework::Text id = generalFactory.getDrawableId(*member.val());
        if (id.getLength() == 0)
        {
            member.remove();
            f->setRender();
        }
        else
        {
            member++;
        }
    }
    int index = 0;
    for (auto i = element.getChilds(); i; i++)
    {
        Text id = i->getAttributeValue("id");
        Drawable* z = generalFactory.zDrawableById(id);
        if (!id.getLength() || !z || f->getMembers().indexOf(z) < 0
            || !generalFactory.updateElement(*i.val(), *z, generalFactory))
        {
            if (f->getMembers().indexOf(z) >= 0)
            {
                f->removeMember(z);
            }
            if (z) generalFactory.removeDrawable(*z);
            z = generalFactory.parseElement(*i.val(), generalFactory);
            if (z) f->addMember(z);
        }
        if (z) f->setMemberIndex(z, index++);
    }
    if (!element.hasAttribute("style"))
    {
        Window* tmp = generalFactory.getFactory().createWindow(
            generalFactory.getFactory().initParam);
        f->setStyle(tmp->getStyles());
        tmp->release();
    }
    return true;
}

void UIMLFrame::layout(XML::Element& element,
    Drawable& z,
    int pWidth,
    int pHeight,
    UIMLContainer& generalLayouter)
{
    Window* f = dynamic_cast<Window*>(&z);
    if (element.hasAttribute("title"))
        f->setTitel(element.getAttributeValue("title"));
    if (element.hasAttribute("title-height"))
    {
        f->zTTextField()->setSize(f->zTTextField()->getWidth(),
            (int)element.getAttributeValue("title-height"));
    }
    if (element.hasAttribute("title-font-size"))
    {
        f->zTTextField()->setFontSize(
            (unsigned char)(int)element.getAttributeValue("title-font-size"));
    }
    if (element.hasAttribute("title-text-color"))
    {
        f->zTTextField()->setFontColor(
            (int)element.getAttributeValue("title-text-color"));
    }
    if (element.hasAttribute("width"))
    {
        Text w = element.getAttributeValue("width");
        if (w.isEqual("auto"))
        {
            z.setWidth(f->getNeededChildWidth());
        }
    }
    if (element.hasAttribute("height"))
    {
        Text h = element.getAttributeValue("height");
        if (h.isEqual("auto"))
        {
            z.setHeight(f->getNeededChildHeight());
        }
    }
    if (element.hasAttribute("display"))
    {
        UIMLElement::layout(element,
            z,
            pWidth,
            pHeight,
            generalLayouter); // calculate all sizes
        if (element.getAttributeValue("display").isEqual("column"))
        {
            int gap = calculateSize(element.getAttributeValue("gap"), pHeight);
            int y = 0;
            for (Drawable* z : f->getMembers())
            {
                Text id = generalLayouter.getDrawableId(*z);
                auto child = element.selectChildsByAttribute("id", id);

                if (child.exists())
                {
                    if (!child.whereAttributeExists("align-left").exists()
                        && !child.whereAttributeExists("align-right").exists()
                        && !child.whereAttributeExists("align-top").exists()
                        && !child.whereAttributeExists("align-bottom").exists())
                    {
                        z->setY(y);
                        y += z->getHeight() + gap;
                    }
                }
            }
        }
        else if (element.getAttributeValue("display").isEqual("row"))
        {
            int gap = calculateSize(element.getAttributeValue("gap"), pWidth);
            int x = 0;
            for (Drawable* z : f->getMembers())
            {
                Text id = generalLayouter.getDrawableId(*z);
                auto child = element.selectChildsByAttribute("id", id);

                if (child.exists())
                {
                    if (!child.whereAttributeExists("align-left").exists()
                        && !child.whereAttributeExists("align-right").exists()
                        && !child.whereAttributeExists("align-top").exists()
                        && !child.whereAttributeExists("align-bottom").exists())
                    {
                        z->setX(x);
                        x += z->getWidth() + gap;
                    }
                }
            }
        }
    }
    UIMLElement::layout(element,
        z,
        pWidth,
        pHeight,
        generalLayouter); // calculate all remaining positions
    if (element.getAttributeValue("vScroll").isEqual("auto"))
    {
        f->setStyle(Window::Style::VScroll,
            f->getNeededChildHeight() > f->getInnerHeight());
        f->setVSBScroll(10);
        f->updateVScroll();
    }
    if (element.getAttributeValue("hScroll").isEqual("auto"))
    {
        f->setStyle(Window::Style::HScroll,
            f->getNeededChildWidth() > f->getInnerWidth());
        f->setHSBScroll(10);
        f->updateHScroll();
    }
}

// Creates a UIML View
UIMLView::UIMLView()
    : DrawableBackground()
{
    style = Style::MEIgnoreInside | Style::MEIgnoreParentInside
          | Style::MEIgnoreVisible | Style::MEIgnoreProcessed;
    members = new RCTrie<Drawable>();
    dom = 0;
    nextId = 0;
    memset((void*)&init, 0, sizeof(UIInit));
    addKnownElement(new UIMLTextField());
    addKnownElement(new UIMLButton());
    addKnownElement(new UIMLCheck());
    addKnownElement(new UIMLText());
    addKnownElement(new UIMLTextArea());
    addKnownElement(new UIMLTable());
    addKnownElement(new UIMLFrame());
}

// Creates a UIML View from a UIML text
//  uiml: An xml element according to the KSG UIML standard
UIMLView::UIMLView(XML::Element* uiml, UIInit& init)
    : UIMLView()
{
    this->init = init;
    setUIML(uiml);
}

// Creates a UIML View from a UIML text
//  uiml: An xml text according to the KSG UIML standard
UIMLView::UIMLView(Text uiml, UIInit& init)
    : UIMLView()
{
    this->init = init;
    setUIML(uiml);
}

UIMLView::~UIMLView()
{
    if (dom) dom->release();
    members->release();
}

// Processes a mouse event. Called automatically by the framework.
//  me: The event
void UIMLView::doMouseEvent(MouseEvent& me, bool userRet)
{
    if (dom)
    {
        bool processed = me.processed;
        me.processed |= hasStyleNot(Style::Visible);
        bool insideParent = me.insideParent;
        if (!hasStyle(Style::Visible) || !me.insideParent || me.processed
            || me.mx < 0 || me.my < 0 || me.mx >= gr.x || me.my >= gr.y
            || !userRet)
            me.insideParent = 0;
        int rbr = 0;
        if (hasStyle(Style::Border) && border) rbr = border->getRWidth();
        me.mx -= rbr;
        me.my -= rbr;
        if (hasStyle(Style::VScroll) && vertikalScrollBar)
            me.my += vertikalScrollBar->getScroll();
        if (hasStyle(Style::HScroll) && horizontalScrollBar)
            me.mx += horizontalScrollBar->getScroll();
        if (dom)
        {
            for (auto i = dom->getChilds(); i; i++)
            { // TODO render elements backwards
                Drawable* z = members->z(i->getAttributeValue("id"),
                    i->getAttributeValue("id").getLength());
                if (z) z->doPublicMouseEvent(me);
            }
        }
        me.mx += rbr;
        me.my += rbr;
        if (hasStyle(Style::VScroll) && vertikalScrollBar)
            me.my -= vertikalScrollBar->getScroll();
        if (hasStyle(Style::HScroll) && horizontalScrollBar)
            me.mx -= horizontalScrollBar->getScroll();
        if (!hasStyle(Style::Visible) || !me.insideParent || me.processed
            || me.mx < 0 || me.my < 0 || me.mx >= gr.x || me.my >= gr.y
            || !userRet)
            me.insideParent = insideParent;
        else
            me.processed = 1;
        if (hasStyleNot(Style::Visible)) me.processed = processed;
    }
}

void Framework::UIMLView::setOnMemberMouseEvent(
    std::function<bool(XML::Element& element, Drawable& member, MouseEvent me)>
        onEventAction)
{
    onMemberMouseEvent = onEventAction;
}

void Framework::UIMLView::setOnMemberKeyboardEvent(std::function<bool(
        XML::Element& element, Drawable& member, KeyboardEvent te)>
        onEventAction)
{
    onMemberKeyboardEvent = onEventAction;
}

//! removes all known elements that can be used in UIML
void UIMLView::removeAllKnownElements()
{
    knownElements.clear();
}

//! adds a new known element that can then be used in UIML.
void UIMLView::addKnownElement(UIMLElement* element)
{
    knownElements.add(element);
}

//! checks whether an xml element is a known UIML element;
bool UIMLView::isKnownElement(XML::Element* zElement)
{
    for (UIMLElement* element : knownElements)
    {
        if (element->isApplicableFor(*zElement)) return 1;
    }
    return 0;
}

// sets the content of the view
//  uiml: An xml element according to the KSG UIML standard
void UIMLView::setUIML(XML::Element* uiml)
{
    if (dom)
    { // update dom and members
        auto newChilds = uiml->selectChildren().selectAllElements();
        // remove old members
        for (XML::Element* i :
            dom->selectChildren().selectAllElements().whereAttributeExists(
                "id"))
        {
            Framework::Text id = i->getAttributeValue("id");
            if (!newChilds.whereAttributeEquals("id", id).exists())
            {
                Drawable* z = members->z(id, id.getLength());
                if (z) removeDrawable(*z);
            }
        }
        dom = uiml;
        update();
    }
    else
    { // initialize dom and members
        dom = uiml;
        for (auto i = dom->getChilds(); i; i++)
        {
            Drawable* z = parseElement(*i.val(), *this);
            if (z) z->release();
        }
    }
}

// sets the content of the view
//  uiml: An xml text according to the KSG UIML standard
void UIMLView::setUIML(Text uiml)
{
    setUIML(new XML::Element(uiml));
}

// Returns a drawing that has a specific id in UIML
//  id: the id of the drawing
Drawable* UIMLView::zDrawableById(const char* id)
{
    return members->z(id, textLength(id));
}

// Returns a drawing that has a specific id in UIML
//  id: the id of the drawing
Drawable* UIMLView::getDrawableById(const char* id)
{
    return members->get(id, textLength(id));
}

void Framework::UIMLView::update()
{
    for (auto i = dom->getChilds(); i; i++)
    {
        Text id = i->getAttributeValue("id");
        Drawable* z = zDrawableById(id);
        if (!id.getLength() || !z || !updateElement(*i.val(), *z, *this))
        {
            z = parseElement(*i.val(), *this);
            if (z) z->release();
        }
    }
}

// updates size and position of all drawings according to the specifications
// in UIML
void UIMLView::layout()
{
    if (dom)
    {
        for (auto i = dom->getChilds(); i; i++)
        {
            Text id = i->getAttributeValue("id");
            Drawable* z = zDrawableById(id);
            if (z)
            {
                layout(*i.val(),
                    *z,
                    this->getInnerWidth(),
                    this->getInnerHeight(),
                    *this);
            }
        }
    }
}

// adds an element
//  uiml: An xml text according to the KSG UIML standard representing the
//  new object
Text UIMLView::addMember(Text uiml)
{
    XML::Element* e = new XML::Element(uiml);
    Drawable* z = parseElement(*e, *this);
    if (z)
    {
        dom->addChildAtFront(e);
        z->release();
    }
    return e->getAttributeValue("id");
}

// adds an element to a parent element (currently only works with
// frame objects)
//  uiml: An xml text according to the KSG UIML standard representing the
//  new object
Text UIMLView::addMember(Text uiml, Text parentId)
{
    XML::Element* e = new XML::Element(uiml);
    XML::Editor ed = dom->selectChildren();
    while (ed.begin())
    {
        XML::Editor ed2 = ed.whereAttributeEquals("id", parentId);
        if (ed2.begin())
        {
            if (ed2.begin()->getName().isEqual("frame"))
            {
                Drawable* z = parseElement(*e, *this);
                if (z)
                {
                    dynamic_cast<Window*>(
                        members->z(parentId, parentId.getLength()))
                        ->addMember(z);
                    ed2.begin()->addChild(e);
                }
                return e->getAttributeValue("id");
            }
        }
        ed = ed.selectChildren();
    }
    e->release();
    return "";
}

// removes an element
//  id: id of the element
void UIMLView::removeMember(Text id)
{
    XML::Editor e = dom->selectChildsByAttribute("id", id);
    e.remove();
    members->remove(id, id.getLength());
}

// Processes a keyboard event. Called automatically by the framework
//  te: The event
void UIMLView::doKeyboardEvent(KeyboardEvent& te)
{
    bool processed = te.processed;
    DrawableBackground::doKeyboardEvent(te);
    te.processed = processed;
    if (dom)
    {
        for (auto i = dom->getChilds(); i; i++)
        { // TODO render elements backwards
            Drawable* z = members->z(i->getAttributeValue("id"),
                i->getAttributeValue("id").getLength());
            if (z) z->doKeyboardEvent(te);
        }
    }
}

// Updates the drawing background
//  tickVal: The elapsed time in seconds since the last call of this
//  function return: 1 if the image needs to be redrawn.
//  0 otherwise
bool UIMLView::tick(double tickVal)
{
    if (dom)
    {
        for (auto i = dom->getChilds(); i; i++)
        { // TODO render elements backwards
            Drawable* z = members->z(i->getAttributeValue("id"),
                i->getAttributeValue("id").getLength());
            if (z) rend |= z->tick(tickVal);
        }
    }
    return DrawableBackground::tick(tickVal);
}

// Draws the background of a drawing to rObj
void UIMLView::render(Image& rObj)
{
    if (hasStyle(Drawable::Style::Visible))
    {
        DrawableBackground::render(rObj);
        if (dom)
        {
            if (!rObj.setDrawOptions(pos.x + getBorderWidth(),
                    pos.y + getBorderWidth(),
                    gr.x + getBorderWidth() * 2,
                    gr.y + getBorderWidth() * 2))
                return;
            bool vSc = hasStyle(Style::VScroll) && vertikalScrollBar;
            bool hSc = hasStyle(Style::HScroll) && horizontalScrollBar;
            rObj.addScrollOffset(hSc ? horizontalScrollBar->getScroll() : 0,
                vSc ? vertikalScrollBar->getScroll() : 0);
            for (int i = dom->getChildCount() - 1; i >= 0; i--)
            { // TODO render elements backwards
                XML::Element* e = dom->zChild(i);
                Drawable* z = members->z(e->getAttributeValue("id"),
                    e->getAttributeValue("id").getLength());
                if (z) z->render(rObj);
            }
            rObj.releaseDrawOptions();
        }
    }
}

// Returns the DOM tree without increased reference counter
// Changes to the DOM tree should be avoided (only changes to
// attributes of individual elements are allowed)
XML::Element* UIMLView::zDom() const
{
    return dom;
}

// Returns the DOM tree
// Changes to the DOM tree should be avoided (only changes to
// attributes of individual elements are allowed)
XML::Element* UIMLView::getDom() const
{
    return dom ? dynamic_cast<XML::Element*>(dom->getThis()) : 0;
}

bool UIMLView::isApplicableFor(XML::Element& element)
{
    for (UIMLElement* e : knownElements)
    {
        if (e->isApplicableFor(element)) return 1;
    }
    return 0;
}

Drawable* UIMLView::parseElement(
    XML::Element& element, UIMLContainer& generalFactory)
{
    Text id;
    if (element.hasAttribute("id"))
        id = element.getAttributeValue("id");
    else
    {
        id = Text("_") += nextId++;
        element.setAttribute("id", id);
    }
    Drawable* z = members->z(id, id.getLength());
    if (!z)
    {
        // precompute attributes
        if (element.hasAttribute("margin"))
        {
            Text m = element.getAttributeValue("margin");
            if (!element.hasAttribute("margin-left"))
                element.setAttribute("margin-left", m);
            if (!element.hasAttribute("margin-top"))
                element.setAttribute("margin-top", m);
            if (!element.hasAttribute("margin-right"))
                element.setAttribute("margin-right", m);
            if (!element.hasAttribute("margin-bottom"))
                element.setAttribute("margin-bottom", m);
        }
        if (element.hasAttribute("class"))
        {
            Text c = element.getAttributeValue("class");
            while (1)
            {
                Text* t;
                if (c.has(","))
                    t = c.getTeilText(0, c.positionOf(','));
                else
                    t = new Text(c);
                XML::Editor ce
                    = dom->selectChildsByName("class").whereAttributeEquals(
                        "id", *t);
                for (auto i = ce.begin(); i; i++)
                {
                    for (auto j = i->getAttributeNames(),
                              k = i->getAttributeValues();
                        j && k;
                        j++, k++)
                    {
                        if (!element.hasAttribute(j->getText()))
                            element.setAttribute(j->getText(), i->getText());
                    }
                }
                t->release();
                if (c.has(","))
                    c.remove(0, c.positionOf(',' + 1));
                else
                    break;
            }
        }
        if (element.hasAttribute("text-align"))
        {
            if (!element.hasAttribute("text-align-horizontal"))
                element.setAttribute("text-align-horizontal",
                    element.getAttributeValue("text-align"));
            if (!element.hasAttribute("text-align-vertical"))
                element.setAttribute("text-align-vertical",
                    element.getAttributeValue("text-align"));
        }
        // create objects
        for (UIMLElement* e : knownElements)
        {
            if (e->isApplicableFor(element))
            {
                z = e->parseElement(element, *this);
                break;
            }
        }
        if (z)
        {
            if (hasStyle(Style::GlobalMouseEvent))
            {
                z->addMouseEvent([this, z](void* p, void* o, MouseEvent me) {
                    return dom->selectChildren()
                        .selectAllElements()
                        .whereAttributeEquals("id", getDrawableId(*z))
                        .getFirstElement()
                        .map<bool>(
                            [this, &me, z](RCPointer<XML::Element> element) {
                                return onMemberMouseEvent
                                         ? onMemberMouseEvent(*element, *z, me)
                                         : 0;
                            })
                        .orElse(0);
                });
            }
            if (hasStyle(Style::GlobalTastaturEvent))
            {
                z->addKeyboardEvent([this, z](
                                        void* p, void* o, KeyboardEvent te) {
                    return dom->selectChildren()
                        .selectAllElements()
                        .whereAttributeEquals("id", getDrawableId(*z))
                        .getFirstElement()
                        .map<bool>([this, &te, z](
                                       RCPointer<XML::Element> element) {
                            return onMemberKeyboardEvent
                                     ? onMemberKeyboardEvent(*element, *z, te)
                                     : 0;
                        })
                        .orElse(0);
                });
            }
            members->set(
                id, id.getLength(), dynamic_cast<Drawable*>(z->getThis()));
            idList.add(new Text(id));
            memberList.add(z);
        }
    }
    else
        z->getThis();
    return z;
}

bool Framework::UIMLView::updateElement(
    XML::Element& element, Drawable& z, UIMLContainer& generalFactory)
{
    bool result = 0;
    for (UIMLElement* e : knownElements)
    {
        if (e->isApplicableFor(element))
        {
            result = e->updateElement(element, z, *this);
            break;
        }
    }
    if (!result)
    {
        for (XML::Element* element :
            element.selectChildren().selectAllElements().whereAttributeExists(
                "id"))
        { // element could not be updated so all children will be removed
            Framework::Text id = element->getAttributeValue("id");
            Drawable* z = zDrawableById(id);
            if (z) removeDrawable(*z);
        }
        removeDrawable(z);
    }
    return result;
}

void UIMLView::layout(XML::Element& element,
    Drawable& z,
    int pWidth,
    int pHeight,
    UIMLContainer& generalLayouter)
{
    for (UIMLElement* e : knownElements)
    {
        if (e->isApplicableFor(element))
        {
            e->layout(element, z, pWidth, pHeight, *this);
            break;
        }
    }
}

Text Framework::UIMLView::getDrawableId(Drawable& z)
{
    int index = memberList.getValueIndex(&z);
    if (index >= 0) return *idList.z(index);
    return "";
}

void Framework::UIMLView::removeDrawable(Drawable& z)
{
    int index = memberList.getValueIndex(&z);
    if (index >= 0)
    {
        Text id = *idList.z(index);
        idList.remove(index);
        memberList.remove(index);
        members->remove(id, id.getLength());
    }
}

bool Framework::UIMLView::registerDrawable(const char* id, Drawable* z)
{
    Drawable* existing = members->z(id, textLength(id));
    if (existing)
    {
        z->release();
        return 0;
    }
    members->set(id, textLength(id), z);
    return 1;
}

const UIInit& UIMLView::getFactory()
{
    return init;
}

//! calculates the needed size for all content elements to be visible
Point UIMLView::calculateContentSize()
{
    Point maxP(0, 0);
    for (int i = dom->getChildCount() - 1; i >= 0; i--)
    { // TODO render elements backwards
        XML::Element* e = dom->zChild(i);
        Drawable* z = members->z(
            e->getAttributeValue("id"), e->getAttributeValue("id").getLength());
        if (z)
        {
            maxP.x = MAX(maxP.x, z->getPosition().x + z->getWidth());
            maxP.y = MAX(maxP.y, z->getPosition().y + z->getHeight());
        }
    }
    maxP.x += 2 * getBorderWidth();
    maxP.y += 2 * getBorderWidth();
    return maxP;
}