#include "UIMLView.h"
#include "XML.h"
#include "TextFeld.h"
#include "Knopf.h"
#include "Tabelle.h"
#include "Fenster.h"
#include "Schrift.h"
#include "Bildschirm.h"
#include "Rahmen.h"
#include "Scroll.h"
#include "Bild.h"

using namespace Framework;


// Erstellt eine UIML View
UIMLView::UIMLView()
    : ZeichnungHintergrund()
{
    style = Style::MEIgnoreInside | Style::MEIgnoreParentInside | Style::MEIgnoreSichtbar | Style::MEIgnoreVerarbeitet;
    members = new Trie< Zeichnung >();
    dom = 0;
    nextId = 0;
    memset( &init, 0, sizeof( UIInit ) );
}

// Erstellt eine UIML View zu einem UIML Text
//  uiml: Ein xml element gemät des ksg uiml standarts
UIMLView::UIMLView( XML::Element *uiml, UIInit &init )
    : ZeichnungHintergrund()
{
    this->init = init;
    members = new Trie< Zeichnung >();
    dom = 0;
    nextId = 0;
    setUIML( uiml );
}

// Erstellt eine UIML View zu einem UIML Text
//  uiml: Ein xml text gemät des ksg uiml standarts
UIMLView::UIMLView( Text uiml, UIInit &init )
{
    this->init = init;
    members = new Trie< Zeichnung >();
    dom = 0;
    setUIML( uiml );
}

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

// Verarbeitet ein Maus Ereignis. Wird vom Framework automatisch aufgerufen.
//  me: Das Ereignis
void UIMLView::doMausEreignis( MausEreignis &me, bool userRet )
{
    if( dom )
    {
        bool verarbeitet = me.verarbeitet;
        me.verarbeitet |= hatStyleNicht( Style::Sichtbar );
        bool insideParent = me.insideParent;
        if( !hatStyle( Style::Sichtbar ) || !me.insideParent || me.verarbeitet || me.mx < 0 || me.my < 0 || me.mx >= gr.x || me.my >= gr.y || !userRet )
            me.insideParent = 0;
        int rbr = 0;
        if( hatStyle( Style::Rahmen ) && rahmen )
            rbr = rahmen->getRBreite();
        me.mx -= rbr;
        me.my -= rbr;
        if( hatStyle( Style::VScroll ) && vertikalScrollBar )
            me.my += vertikalScrollBar->getScroll();
        if( hatStyle( Style::HScroll ) && horizontalScrollBar )
            me.mx += horizontalScrollBar->getScroll();
        if( dom )
        {
            for( auto i = dom->getChilds(); i; i++ )
            { // TODO render elements backwards
                Zeichnung *z = members->z( i->getAttributeValue( "id" ), i->getAttributeValue( "id" ).getLength() );
                if( z )
                    z->doPublicMausEreignis( me );
            }
        }
        me.mx += rbr;
        me.my += rbr;
        if( hatStyle( Style::VScroll ) && vertikalScrollBar )
            me.my -= vertikalScrollBar->getScroll();
        if( hatStyle( Style::HScroll ) && horizontalScrollBar )
            me.mx -= horizontalScrollBar->getScroll();
        if( !hatStyle( Style::Sichtbar ) || !me.insideParent || me.verarbeitet || me.mx < 0 || me.my < 0 || me.mx >= gr.x || me.my >= gr.y || !userRet )
            me.insideParent = insideParent;
        else
            me.verarbeitet = 1;
        if( hatStyleNicht( Style::Sichtbar ) )
            me.verarbeitet = verarbeitet;
    }
}

void UIMLView::parseTable( Iterator<XML::Element *> childs, ObjTabelle *table )
{
    for( auto i = childs; i; i++ )
    {
        Text id;
        if( i->hasAttribute( "id" ) )
            id = i->getAttributeValue( "id" );
        else
        {
            id = Text( "_" ) += nextId++;
            i->setAttribute( "id", id );
        }
        if( i->getName().istGleich( "tr" ) )
        {
            table->addZeile( id );
            Text line = id;
            int c = 1;
            for( auto j = i->getChilds(); j; j++ )
            {
                Zeichnung *z = parseElement( j._ );
                if( table->getSpaltenAnzahl() < c )
                    table->addSpalte( Text( c - 1 ) );
                if( z )
                    table->setZeichnungZ( (char *)Text( c - 1 ), (char *)line, dynamic_cast<Zeichnung *>(z->getThis()) );
                c++;
            }
        }
    }
}

void UIMLView::parseFrame( Iterator<XML::Element *> childs, Fenster *frame )
{
    for( auto i = childs; i; i++ )
    {
        Zeichnung *z = parseElement( i._ );
        if( z )
            frame->addMember( dynamic_cast<Zeichnung *>(z->getThis()) );
    }
}

Zeichnung *UIMLView::parseElement( XML::Element *e )
{
    Text id;
    if( e->hasAttribute( "id" ) )
        id = e->getAttributeValue( "id" );
    else
    {
        id = Text( "_" ) += nextId++;
        e->setAttribute( "id", id );
    }
    Zeichnung *z = members->z( id, id.getLength() );
    if( !z )
    {
        // precompute attributes
        if( e->hasAttribute( "margin" ) )
        {
            Text m = e->getAttributeValue( "margin" );
            if( !e->hasAttribute( "margin-left" ) )
                e->setAttribute( "margin-left", m );
            if( !e->hasAttribute( "margin-top" ) )
                e->setAttribute( "margin-top", m );
            if( !e->hasAttribute( "margin-right" ) )
                e->setAttribute( "margin-right", m );
            if( !e->hasAttribute( "margin-bottom" ) )
                e->setAttribute( "margin-bottom", m );
        }
        if( e->hasAttribute( "class" ) )
        {
            Text c = e->getAttributeValue( "class" );
            while( 1 )
            {
                Text *t;
                if( c.hat( "," ) )
                    t = c.getTeilText( 0, c.positionVon( ',' ) );
                else
                    t = new Text( c );
                XML::Editor ce = dom->selectChildsByName( "class" ).whereAttributeEquals( "id", *t );
                for( auto i = ce.getIterator(); i; i++ )
                {
                    for( auto j = i->getAttributeNames(), k = i->getAttributeValues(); j && k; j++, k++ )
                    {
                        if( !e->hasAttribute( j->getText() ) )
                            e->setAttribute( j->getText(), i->getText() );
                    }
                }
                t->release();
                if( c.hat( "," ) )
                    c.remove( 0, c.positionVon( ',' + 1 ) );
                else
                    break;
            }
        }
        if( e->hasAttribute( "text-align" ) )
        {
            if( !e->hasAttribute( "text-align-horizontal" ) )
                e->setAttribute( "text-align-horizontal", e->getAttributeValue( "text-align" ) );
            if( !e->hasAttribute( "text-align-vertical" ) )
                e->setAttribute( "text-align-vertical", e->getAttributeValue( "text-align" ) );
        }
        // create objects
        if( e->getName().istGleich( "textfield" ) ||
            e->getName().istGleich( "text" ) ||
            e->getName().istGleich( "textarea" ) )
        {
            TextFeld *t = init.createTextFeld( init.initParam );
            if( e->getName().istGleich( "textfield" ) )
                t->addStyle( TextFeld::Style::TextFeld );
            if( e->getName().istGleich( "text" ) )
                t->addStyle( TextFeld::Style::Text );
            if( e->getName().istGleich( "textarea" ) )
                t->addStyle( TextFeld::Style::TextGebiet );
            t->setText( e->getText() );
            if( e->hasAttribute( "font-size" ) )
                t->setSchriftSize( (unsigned char)(int)e->getAttributeValue( "font-size" ) );
            if( e->hasAttribute( "text-align-horizontal" ) )
            {
                if( e->getAttributeValue( "text-align-horizontal" ).istGleich( "center" ) )
                    t->addStyle( TextFeld::Style::HCenter );
            }
            if( e->hasAttribute( "text-align-vertical" ) )
            {
                if( e->getAttributeValue( "text-align-vertical" ).istGleich( "center" ) )
                    t->addStyle( TextFeld::Style::VCenter );
            }
            if( e->hasAttribute( "disabled" ) )
                t->removeStyle( TextFeld::Style::Editierbar );
            z = t;
        }
        if( e->getName().istGleich( "button" ) )
        {
            Knopf *k = init.createKnopf( init.initParam );
            k->setText( e->getText() );
            if( e->hasAttribute( "font-size" ) )
                k->setSchriftSize( (unsigned char)(int)e->getAttributeValue( "font-size" ) );
            z = k;
        }
        if( e->getName().istGleich( "check" ) )
        {
            KontrollKnopf *k = init.createKontrollKnopf( init.initParam );
            k->setText( e->getText() );
            k->setSText( e->getText() );
            k->setStyle( KontrollKnopf::Style::Selected, e->hasAttribute( "selected" ) );
            if( e->hasAttribute( "font-size" ) )
                k->setSSize( (unsigned char)(int)e->getAttributeValue( "font-size" ) );
            z = k;
        }
        if( e->getName().istGleich( "table" ) )
        {
            ObjTabelle *t = init.createObjTabelle( init.initParam );
            parseTable( e->getChilds(), t );
            if( e->hasAttribute( "scroll" ) )
            {
                if( e->getAttributeValue( "scroll" ).istGleich( "horizontal" ) )
                    t->addStyle( ObjTabelle::Style::HScroll );
                if( e->getAttributeValue( "scroll" ).istGleich( "vertical" ) )
                    t->addStyle( ObjTabelle::Style::VScroll );
                if( e->getAttributeValue( "scroll" ).istGleich( "both" ) )
                    t->addStyle( ObjTabelle::Style::scroll );
            }
            z = t;
        }
        if( e->getName().istGleich( "frame" ) )
        {
            Fenster *f = init.createFenster( init.initParam );
            parseFrame( e->getChilds(), f );
            if( e->hasAttribute( "title" ) )
                f->setTitel( e->getAttributeValue( "title" ) );
            if( e->hasAttribute( "title-height" ) )
                f->zTTextFeld()->setSize( f->zTTextFeld()->getBreite(), e->getAttributeValue( "title-height" ) );
            z = f;
        }
        // add general attributes
        if( z && e->hasAttribute( "tooltip" ) )
            z->setToolTipText( e->getAttributeValue( "tooltip" ), init.initParam.bildschirm, init.initParam.schrift );
        if( z && e->hasAttribute( "style" ) )
            z->setStyle( (__int64)e->getAttributeValue( "style" ) );
        if( z && e->hasAttribute( "hidden" ) )
            z->removeStyle( Zeichnung::Style::Sichtbar );
        if( z && e->hasAttribute( "disabled" ) )
            z->removeStyle( Zeichnung::Style::Erlaubt );
        if( z )
            members->set( id, id.getLength(), z );
    }
    return z;
}

void UIMLView::layout( XML::Element *e, int pWidth, int pHeight )
{
    Text id = e->getAttributeValue( "id" );
    Zeichnung *z = members->z( id, id.getLength() );
    if( z )
    {
        int width = z->getBreite();
        int height = z->getHeight();
        if( e->hasAttribute( "width" ) )
        {
            Text w = e->getAttributeValue( "width" );
            width = w;
            if( w.getText()[ w.getLength() - 1 ] == '%' )
                width = (int)((pWidth / 100.0) * width);
        }
        if( e->hasAttribute( "height" ) )
        {
            Text h = e->getAttributeValue( "height" );
            height = h;
            if( h.getText()[ h.getLength() - 1 ] == '%' )
                height = (int)((pHeight / 100.0) * height);
        }
        z->setSize( width, height );
        if( e->hasAttribute( "align-left" ) )
        {
            Text la = e->getAttributeValue( "align-left" );
            int x = 0;
            if( la.istGleich( "start" ) )
                x = 0;
            else if( la.istGleich( "end" ) )
                x = pWidth;
            else
            {
                XML::Editor ed = e->zParent()->selectChildsByAttribute( "id", la );
                for( auto i = ed.getIterator(); i; i++ )
                    layout( i, pWidth, pHeight );
                Zeichnung *laz = members->z( la, la.getLength() );
                if( laz )
                    x = laz->getX() + laz->getBreite();
            }
            if( e->hasAttribute( "margin-left" ) )
            {
                Text mt = e->getAttributeValue( "margin-left" );
                int m = mt;
                if( mt.getText()[ mt.getLength() - 1 ] == '%' )
                    m = (int)((pWidth / 100.0) * m);
                x += m;
            }
            z->setX( x );
        }
        else if( e->hasAttribute( "align-right" ) )
        {
            Text ra = e->getAttributeValue( "align-right" );
            int x = 0;
            if( ra.istGleich( "start" ) )
                x = -z->getBreite();
            else if( ra.istGleich( "end" ) )
                x = pWidth - z->getBreite();
            else
            {
                XML::Editor ed = e->zParent()->selectChildsByAttribute( "id", ra );
                for( auto i = ed.getIterator(); i; i++ )
                    layout( i, pWidth, pHeight );
                Zeichnung *raz = members->z( ra, ra.getLength() );
                if( raz )
                    x = raz->getX() - z->getBreite();
            }
            if( e->hasAttribute( "margin-right" ) )
            {
                Text mt = e->getAttributeValue( "margin-right" );
                int m = mt;
                if( mt.getText()[ mt.getLength() - 1 ] == '%' )
                    m = (int)((pWidth / 100.0) * m);
                x -= m;
            }
            z->setX( x );
        }
        if( e->hasAttribute( "align-top" ) )
        {
            Text ta = e->getAttributeValue( "align-top" );
            int y = 0;
            if( ta.istGleich( "start" ) )
                y = 0;
            else if( ta.istGleich( "end" ) )
                y = pHeight;
            else
            {
                XML::Editor ed = e->zParent()->selectChildsByAttribute( "id", ta );
                for( auto i = ed.getIterator(); i; i++ )
                    layout( i, pWidth, pHeight );
                Zeichnung *taz = members->z( ta, ta.getLength() );
                if( taz )
                    y = taz->getY() + taz->getHeight();
            }
            if( e->hasAttribute( "margin-top" ) )
            {
                Text mt = e->getAttributeValue( "margin-top" );
                int m = mt;
                if( mt.getText()[ mt.getLength() - 1 ] == '%' )
                    m = (int)((pHeight / 100.0) * m);
                y += m;
            }
            z->setY( y );
        }
        else if( e->hasAttribute( "align-bottom" ) )
        {
            Text ba = e->getAttributeValue( "align-bottom" );
            int y = 0;
            if( ba.istGleich( "start" ) )
                y = -z->getHeight();
            else if( ba.istGleich( "end" ) )
                y = pHeight - z->getHeight();
            else
            {
                XML::Editor ed = e->zParent()->selectChildsByAttribute( "id", ba );
                for( auto i = ed.getIterator(); i; i++ )
                    layout( i, pWidth, pHeight );
                Zeichnung *baz = members->z( ba, ba.getLength() );
                if( baz )
                    y = baz->getY() - z->getHeight();
            }
            if( e->hasAttribute( "margin-bottom" ) )
            {
                Text mt = e->getAttributeValue( "margin-bottom" );
                int m = mt;
                if( mt.getText()[ mt.getLength() - 1 ] == '%' )
                    m = (int)((pHeight / 100.0) * m);
                y -= m;
            }
            z->setY( y );
        }
        int x = z->getX();
        int y = z->getY();
        if( e->hasAttribute( "x" ) )
        {
            Text xt = e->getAttributeValue( "x" );
            x = xt;
            if( xt.getText()[ xt.getLength() - 1 ] == '%' )
                x = (int)((pWidth / 100.0) * x);
        }
        if( e->hasAttribute( "y" ) )
        {
            Text yt = e->getAttributeValue( "y" );
            y = yt;
            if( yt.getText()[ yt.getLength() - 1 ] == '%' )
                y = (int)((pHeight / 100.0) * y);
        }
        z->setPosition( x, y );
        if( e->getName().istGleich( "textarea" ) )
        {
            ((TextFeld *)z)->zTextRenderer()->textFormatieren( ((TextFeld *)z)->zText(), z->getInnenBreite() );
        }
    }
    if( z )
    {
        pWidth = z->getInnenBreite();
        pHeight = z->getInnenHeight();
    }
    // recursive layout
    for( auto i = e->getChilds(); i; i++ )
        layout( i, pWidth, pHeight );
    if( z )
    {
        if( e->getName().istGleich( "table" ) )
        {
            ObjTabelle *objT = (ObjTabelle *)z;
            if( objT->getZeilenAnzahl() > 0 )
            {
                if( e->hasAttribute( "line-height" ) )
                {
                    int height = e->getAttributeValue( "line-height" );
                    for( int i = 0; i < objT->getZeilenAnzahl(); i++ )
                        objT->setZeilenHeight( i, height );
                }
                for( int i = 0; i < objT->getSpaltenAnzahl(); i++ )
                {
                    if( objT->zZeichnung( i, 0 ) )
                        objT->setSpaltenBreite( i, objT->zZeichnung( i, 0 )->getBreite() );
                }
            }
        }
    }
}

// setzt den inhalt der view
//  uiml: Ein xml element gemät des ksg uiml standarts
void UIMLView::setUIML( XML::Element *uiml )
{
    if( dom )
        dom->release();
    dom = uiml;
    members->leeren();
    nextId = 0;
    if( dom )
    {
        for( auto i = dom->getChilds(); i; i++ )
        {
            parseElement( i._ );
        }
    }
}

// setzt den inhalt der view
//  uiml: Ein xml text gemät des ksg uiml standarts
void UIMLView::setUIML( Text uiml )
{
    setUIML( new XML::Element( uiml ) );
}

// Gibt eine zeichnung zurück, welche in uiml eine bestimmte id hat
//  id: die id der Zeichnung
Zeichnung *UIMLView::zZeichnung( Text id )
{
    return members->z( id, id.getLength() );
}

// aktualisiert größe und position aller Zeichnungen gemäß den spezifikationen in UIML
void UIMLView::layout()
{
    if( dom )
    {
        for( auto i = dom->getChilds(); i; i++ )
        {
            layout( i._, this->getInnenBreite(), this->getInnenHeight() );
        }
    }
}

// fügt ein element hinzu
//  uiml: Ein xml text gemät des KSG UIML standarts, welcher das neue Objekt darstellt
Text UIMLView::addMember( Text uiml )
{
    XML::Element *e = new XML::Element( uiml );
    if( parseElement( e ) )
        dom->addChildAtFront( e );
    return e->getAttributeValue( "id" );
}

// fügt ein element zu einem Elternelement hinzu (funktioniert momentan nur mit frame Objekten)
//  uiml: Ein xml text gemät des KSG UIML standarts, welcher das neue Objekt darstellt
Text UIMLView::addMember( Text uiml, Text parentId )
{
    XML::Element *e = new XML::Element( uiml );
    XML::Editor ed = dom->selectChildren();
    while( ed.getIterator() )
    {
        XML::Editor ed2 = ed.whereAttributeEquals( "id", parentId );
        if( ed2.getIterator() )
        {
            if( ed2.getIterator()->getName().istGleich( "frame" ) )
            {
                Zeichnung *z = parseElement( e );
                if( z )
                {
                    dynamic_cast<Fenster *>(members->z( parentId, parentId.getLength() ))->addMember( dynamic_cast<Zeichnung *>(z->getThis()) );
                    ed2.getIterator()->addChild( e );
                }
                return e->getAttributeValue( "id" );
            }
        }
        ed = ed.selectChildren();
    }
    e->release();
    return "";
}

// entfernt ein element
//  id: id des Elements
void UIMLView::removeMember( Text id )
{
    XML::Editor e = dom->selectChildsByAttribute( "id", id );
    e.remove();
    members->remove( id, id.getLength() );
}

// Verarbeitet ein Tastatur Ereignis. Wird vom Framework automatisch aufgerufen
//  te: Das Ereignis
void UIMLView::doTastaturEreignis( TastaturEreignis &te )
{
    bool verarbeitet = te.verarbeitet;
    ZeichnungHintergrund::doTastaturEreignis( te );
    te.verarbeitet = verarbeitet;
    if( dom )
    {
        for( auto i = dom->getChilds(); i; i++ )
        { // TODO render elements backwards
            Zeichnung *z = members->z( i->getAttributeValue( "id" ), i->getAttributeValue( "id" ).getLength() );
            if( z )
                z->doTastaturEreignis( te );
        }
    }
}

// Updated den Zeichenhintergrund
//  tickVal: Die vergangene Zeit in Sekunden, die seit dem Letzten Aufruf dieser Funktion verstrichen ist
//  return: 1, wenn das Bild neu gezeichnet werden muss. 0 sonnst
bool UIMLView::tick( double tickVal )
{
    if( dom )
    {
        for( auto i = dom->getChilds(); i; i++ )
        { // TODO render elements backwards
            Zeichnung *z = members->z( i->getAttributeValue( "id" ), i->getAttributeValue( "id" ).getLength() );
            if( z )
                rend |= z->tick( tickVal );
        }
    }
    return ZeichnungHintergrund::tick( tickVal );
}

// Zeichnet den Hintergrund eines Zeichnunges nach rObj
void UIMLView::render( Bild &rObj )
{
    ZeichnungHintergrund::render( rObj );
    if( dom )
    {
        if( !rObj.setDrawOptions( pos.x + getRahmenBreite(), pos.y + getRahmenBreite(), gr.x + getRahmenBreite() * 2, gr.y + getRahmenBreite() * 2 ) )
            return;
        bool vSc = hatStyle( Style::VScroll ) && vertikalScrollBar;
        bool hSc = hatStyle( 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 );
            Zeichnung *z = members->z( e->getAttributeValue( "id" ), e->getAttributeValue( "id" ).getLength() );
            if( z )
                z->render( rObj );
        }
        rObj.releaseDrawOptions();
    }
}

// Gibt den Dom Tree ohne erhöhten reference counter zurück
// Änderungen am Dom Tree sollten vermieden werden (nur änderungen von attributen einzelner elemente sind erlaubt)
XML::Element *UIMLView::zDom() const
{
    return dom;
}

// Gibt den Dom Tree zurück
// Änderungen am Dom Tree sollten vermieden werden (nur änderungen von attributen einzelner elemente sind erlaubt)
XML::Element *UIMLView::getDom() const
{
    return dom ? dynamic_cast<XML::Element *>(dom->getThis()) : 0;
}