#include "EditorKarte.h"
#include <Model2D.h>
#include <Bild.h>
#include <Textur2D.h>
#include <Datei.h>
#include <DateiSystem.h>
#include <M2Datei.h>
#include <Globals.h>
#include <DLLRegister.h>

using namespace Editor;


SpielerDaten::SpielerDaten()
    : Model()
{}

SpielerDaten::SpielerDaten( const SpielerDaten &daten )
{
    *this = daten;
    views->getThis();
}


TeamDaten::TeamDaten()
    : Model()
{}

TeamDaten::TeamDaten( const TeamDaten &daten )
{
    *this = daten;
    views->getThis();
}


ObjektDaten::ObjektDaten()
    : Model()
{}

ObjektDaten::ObjektDaten( const ObjektDaten &daten )
{
    *this = daten;
    views->getThis();
}


EditorObject::EditorObject( EditorObject::ObjektTyp typ, int id )
    : Model2DObject()
{
    this->typ = typ;
    this->id = id;
}

EditorObject::~EditorObject()
{}

bool EditorObject::isObjekt( EditorObject::ObjektTyp typ, int id )
{
    return this->typ == typ && this->id == id;
}


EdSpieler::EdSpieler( SpielerDaten *model )
    : EditorObject( SPIELER, model->id )
{
    mdl = model;
}

EdSpieler::~EdSpieler()
{}

void EdSpieler::update()
{
    mdl->pos = position;
    mdl->rot = (double)rotation;
    mdl->update();
}


EdObjekt::EdObjekt( ObjektDaten *model )
    : EditorObject( OBJEKT, model->id )
{
    mdl = model;
}

EdObjekt::~EdObjekt()
{}

void EdObjekt::update()
{
    mdl->pos = position;
    mdl->rot = rotation;
    mdl->scale = size;
    mdl->update();
}


UpdateObserver::UpdateObserver( std::function< void() > f )
{
    this->f = f;
}

void UpdateObserver::update( Model *m )
{
    f();
}


KarteDaten::KarteDaten( EditorKlient *client, SpielerTeamStruktur *sts )
    : Thread()
{
    HMODULE dll = Framework::getDLLRegister()->ladeDLL( "GSL.dll", "data/bin/GSL.dll" );
    getGSLDatei = (GetGSLDatei)GetProcAddress( dll, "getGSLDatei" );
    welt = new Welt2D();
    welt->setCircular( 1 );
    this->client = client;
    this->sts = (SpielerTeamStruktur *)sts->getThis();
    client->loadMapSize( size );
    client->loadResources( resources );
    client->loadSpieler( spieler );
    client->loadTeams( teams );
    client->loadObjekte( objekte );
    welt->setSize( size.x, size.y );
    welt->setSize( 1 );
    for( auto s = spieler.getIterator(); s; s++ )
    {
        Model2DObject *model = new EdSpieler( s._ );
        Bild *bild;
        int fc = sts->spielerFarbe->get( s->id - 1 );
        for( auto r = resources.getIterator(); r; r++ )
        {
            if( r->id == s->m2d )
                model->setModel( client->loadModel( r->path ) );
            if( r->id == s->bild )
                bild = client->loadBild( r->path );
        }
        if( bild )
        {
            Bild *shb = new Bild();
            shb->neuBild( bild->getBreite(), bild->getHeight(), 0 );
            int maxP = shb->getBreite() * shb->getHeight();
            int *buffer = bild->getBuffer();
            for( int i = 0; i < maxP; i++ )
            {
                if( buffer[ i ] )
                {
                    shb->setPixelDP( i, fc );
                    shb->alphaPixelDP2D( i, buffer[ i ] );
                }
            }
            Textur2D *textur = new Textur2D();
            textur->setTexturZ( shb );
            model->setTextur( textur, "ship" );
            bild->release();
        }
        model->setPosition( s->pos );
        model->setDrehung( (float)s->rot );
        model->setCollision( 0 );
        welt->addObject( model );
        SpielerDaten *d = s._;
        d->addView( new UpdateObserver( [this, d]()
        {
            aktionen.add( [this, d]()
            {
                this->client->saveSpieler( d );
            } );
        } ) );
    }
    for( auto t = teams.getIterator(); t; t++ )
    {
        TeamDaten *d = t._;
        d->addView( new UpdateObserver( [this, d]()
        {
            aktionen.add( [this, d]()
            {
                this->client->saveTeam( d );
            } );
        } ) );
    }
    for( auto o = objekte.getIterator(); o; o++ )
    {
        Model2DObject *model = new EdObjekt( o._ );
        Bild *bild;
        for( auto r = resources.getIterator(); r; r++ )
        {
            if( r->id == o->m2d )
                model->setModel( client->loadModel( r->path ) );
            if( r->id == o->bild )
                bild = client->loadBild( r->path );
        }
        if( bild )
        {
            Textur2D *textur = new Textur2D();
            textur->setTexturZ( bild );
            model->setTextur( textur );
        }
        model->setPosition( o->pos );
        model->setDrehung( o->rot );
        model->setSize( o->scale );
        model->setCollision( 0 );
        welt->addObject( model );
        ObjektDaten *d = o._;
        d->addView( new UpdateObserver( [this, d, model]()
        {
            aktionen.add( [this, d, model]()
            {
                this->client->saveObjekt( d );
                Bild *bild = 0;
                Model2DData *da = 0;
                for( auto r = resources.getIterator(); r; r++ )
                {
                    if( r->id == d->m2d )
                        da = this->client->loadModel( r->path );
                    if( r->id == d->bild )
                        bild = this->client->loadBild( r->path );
                }
                if( bild && da )
                {
                    Textur2D *textur = new Textur2D();
                    textur->setTexturZ( bild );
                    model->postAction( [model, da, textur]()
                    {
                        model->setModel( da );
                        model->setTextur( textur );
                    } );
                }
                else
                {
                    if( bild )
                        bild->release();
                    if( da )
                        da->release();
                }
            } );
        } ) );
    }
    exit = 0;
    start();
}

KarteDaten::~KarteDaten()
{
    cs.lock();
    for( auto i = resources.getIterator(); i; i++ )
        delete i._;
    for( auto i = objekte.getIterator(); i; i++ )
        delete i._;
    for( auto i = spieler.getIterator(); i; i++ )
        delete i._;
    for( auto i = teams.getIterator(); i; i++ )
        delete i._;
    sts->release();
    client->release();
    welt->release();
    Framework::getDLLRegister()->releaseDLL( "GSL.dll" );
    cs.unlock();
}

void KarteDaten::addObjekt( ObjektDaten &daten, std::function< void( int ) > callBack )
{
    ObjektDaten *nd = new ObjektDaten( daten );
    cs.lock();
    nd->id = 0;
    bool found = 0;
    do
    {
        nd->id++;
        found = 0;
        for( auto o = objekte.getIterator(); o; o++ )
        {
            if( o->id == nd->id )
            {
                found = 1;
                break;
            }
        }
    } while( found );
    objekte.add( nd );
    Model2DObject *model = new EdObjekt( nd );
    Bild *bild;
    for( auto r = resources.getIterator(); r; r++ )
    {
        if( r->id == nd->m2d )
            model->setModel( client->loadModel( r->path ) );
        if( r->id == nd->bild )
            bild = client->loadBild( r->path );
    }
    if( bild )
    {
        Textur2D *textur = new Textur2D();
        textur->setTexturZ( bild );
        model->setTextur( textur );
    }
    model->setPosition( nd->pos );
    model->setDrehung( nd->rot );
    model->setSize( nd->scale );
    model->setCollision( 0 );
    welt->addObject( model );
    nd->addView( new UpdateObserver( [this, nd, model]()
    {
        aktionen.add( [this, nd, model]()
        {
            this->client->saveObjekt( nd );
            Bild *bild = 0;
            Model2DData *d = 0;
            for( auto r = resources.getIterator(); r; r++ )
            {
                if( r->id == nd->m2d )
                    d = this->client->loadModel( r->path );
                if( r->id == nd->bild )
                    bild = this->client->loadBild( r->path );
            }
            if( bild && d )
            {
                Textur2D *textur = new Textur2D();
                textur->setTexturZ( bild );
                model->postAction( [model, d, textur]()
                {
                    model->setModel( d );
                    model->setTextur( textur );
                } );
            }
            else
            {
                if( bild )
                    bild->release();
                if( d )
                    d->release();
            }
        } );
    } ) );
    EditorKlient *c = client;
    aktionen.add( [nd, c, callBack]()
    {
        if( c->saveObjekt( nd ) )
            callBack( nd->id );
        else
            callBack( 0 );
    } );
    cs.unlock();
}

void KarteDaten::removeObjekt( int index )
{
    cs.lock();
    int id = objekte.get( index )->id;
    EditorObject *obj = 0;
    for( auto o = welt->getMembers(); o; o++ )
    {
        obj = (EditorObject *)o._;
        if( obj->isObjekt( EditorObject::OBJEKT, id ) )
            break;
    }
    welt->removeObject( obj );
    delete objekte.get( index );
    objekte.remove( index );
    EditorKlient *c = client;
    aktionen.add( [id, c]()
    {
        c->deleteObjekt( id );
    } );
    cs.unlock();
}

void KarteDaten::thread()
{
    while( !exit )
    {
        cs.lock();
        while( hasAktions() )
        {
            std::function< void() > ak = aktionen.get( 0 );
            cs.unlock();
            ak();
            cs.lock();
            aktionen.remove( 0 );
        }
        cs.unlock();
        Sleep( 100 );
    }
}

ResourceDaten *KarteDaten::getResource( int index )
{
    ResourceDaten *ret = 0;
    cs.lock();
    ret = resources.get( index );
    cs.unlock();
    return ret;
}

ObjektDaten *KarteDaten::getObjekt( int index )
{
    ObjektDaten *ret = 0;
    cs.lock();
    ret = objekte.get( index );
    cs.unlock();
    return ret;
}

SpielerDaten *KarteDaten::getSpieler( int index )
{
    SpielerDaten *ret = 0;
    cs.lock();
    ret = spieler.get( index );
    cs.unlock();
    return ret;
}

TeamDaten *KarteDaten::getTeam( int index )
{
    TeamDaten *ret = 0;
    cs.lock();
    ret = teams.get( index );
    cs.unlock();
    return ret;
}

const char *KarteDaten::getTeamName( int index )
{
    return sts->teamName->z( teams.get( index )->id - 1 )->getText();
}

int KarteDaten::getSpielerIndexById( int id )
{
    int index = 0;
    cs.lock();
    for( auto i = spieler.getIterator(); i; i++ )
    {
        if( i->id == id )
            break;
        index++;
    }
    cs.unlock();
    return index;
}

int KarteDaten::getSpielerAnzahl() const
{
    return spieler.getEintragAnzahl();
}

int KarteDaten::getTeamIndexById( int id )
{
    int index = 0;
    cs.lock();
    for( auto i = teams.getIterator(); i; i++ )
    {
        if( i->id == id )
            break;
        index++;
    }
    cs.unlock();
    return index;
}

int KarteDaten::getTeamAnzahl() const
{
    return teams.getEintragAnzahl();
}

int KarteDaten::getObjektIndexById( int id )
{
    int index = 0;
    cs.lock();
    for( auto i = objekte.getIterator(); i; i++ )
    {
        if( i->id == id )
            break;
        index++;
    }
    cs.unlock();
    return index;
}

int KarteDaten::getObjektAnzahl() const
{
    return objekte.getEintragAnzahl();
}

int KarteDaten::getResourceIndexById( int id )
{
    int index = 0;
    cs.lock();
    for( auto i = resources.getIterator(); i; i++ )
    {
        if( i->id == id )
            break;
        index++;
    }
    cs.unlock();
    return index;
}

int KarteDaten::getResourceAnzahl()
{
    return resources.getEintragAnzahl();
}

bool KarteDaten::hasError() const
{
    return !error.istGleich( "" );
}

char *KarteDaten::getError() const
{
    return error;
}

bool KarteDaten::hasAktions() const
{
    return aktionen.getEintragAnzahl() > 0;
}

Welt2D *KarteDaten::getWelt() const
{
    return dynamic_cast<Welt2D *>( welt->getThis() );
}

Welt2D *KarteDaten::zWelt() const
{
    return welt;
}

void KarteDaten::getResourceIdFromPath( const char *path, std::function< void( int ) > callBack )
{
    for( auto r = resources.getIterator(); r; r++ )
    {
        if( r->path.istGleich( path ) )
        {
            callBack( r->id );
            return;
        }
    }
    aktionen.add( [this, path, callBack]()
    {
        int id = client->addResource( path );
        if( !id )
        {
            callBack( 0 );
            return;
        }
        ResourceDaten *nr = new ResourceDaten();
        nr->id = id;
        nr->path = path;
        cs.lock();
        resources.add( nr );
        cs.unlock();
        callBack( id );
    } );
}

bool KarteDaten::doesResourceExist( const char *path )
{
    for( auto r = resources.getIterator(); r; r++ )
    {
        if( r->path.istGleich( path ) )
            return 1;
    }
    return 0;
}

Model2DData *KarteDaten::loadModelFromRessource( int id )
{
    for( auto r = resources.getIterator(); r; r++ )
    {
        if( r->id == id )
            return client->loadModel( r->path );
    }
    return 0;
}

Bild *KarteDaten::loadBildFromRessource( int id )
{
    for( auto r = resources.getIterator(); r; r++ )
    {
        if( r->id == id )
            return client->loadBild( r->path );
    }
    return 0;
}

Model2DData *KarteDaten::loadModelFromPath( const char *path )
{
    return client->loadModel( path );
}

Bild *KarteDaten::loadBildFromPath( const char *path )
{
    return client->loadBild( path );
}

void KarteDaten::loadUnusedResourcePaths( std::function< void( RCArray< Text > * ) > callBack )
{
    cs.lock();
    aktionen.add( [this, callBack]()
    {
        RCArray< Text > *result = new RCArray< Text >();
        loadSpielResourcePathsFromFolder( "data/spiele/Asteroids", result );
        RCArray< Text > *mapPaths = client->getAllMapResourcePaths();
        for( auto mp = mapPaths->getIterator(); mp; mp++ )
        {
            if( !doesResourceExist( mp->getText() ) )
                result->add( dynamic_cast<Text *>( mp->getThis() ) );
        }
        mapPaths->release();
        callBack( result );
    } );
    cs.unlock();
}

void KarteDaten::loadSpielResourcePathsFromFolder( const char *folderPath, RCArray< Text > *zPaths )
{
    Datei f;
    f.setDatei( folderPath );
    if( f.istOrdner() )
    {
        RCArray< Text > *list = f.getDateiListe();
        for( auto n = list->getIterator(); n; n++ )
        {
            Text path( folderPath );
            path += Text( "/" ) + (const char *)n->getText();
            loadSpielResourcePathsFromFolder( path, zPaths );
        }
        list->release();
    }
    else
    {
        if( f.zPfad()->hat( ".ltdb" ) )
        {
            LTDBDatei d;
            d.setDatei( new Text( folderPath ) );
            d.leseDaten( 0 );
            int anz = d.getBildAnzahl();
            for( int i = 0; i < anz; i++ )
            {
                Text *path = new Text( folderPath );
                path->ersetzen( 0, (int)strlen( "data/spiele/Asteroids" ), "spiel:" );
                path->append( (const char *)( Text( "/" ) + (const char *)d.zBildListe()->z( i )->getText() ) );
                if( doesResourceExist( path->getText() ) )
                    path->release();
                else
                    zPaths->add( path );
            }
        }
        if( f.zPfad()->hat( ".m2" ) )
        {
            M2Datei d;
            d.setPfad( folderPath );
            d.leseDaten();
            int anz = d.getModelAnzahl();
            for( int i = 0; i < anz; i++ )
            {
                Text *path = new Text( folderPath );
                path->ersetzen( 0, (int)strlen( "data/spiele/Asteroids" ), "spiel:" );
                path->append( (const char *)( Text( "/" ) + (const char *)d.zModelName( i )->getText() ) );
                if( doesResourceExist( path->getText() ) )
                    path->release();
                else
                    zPaths->add( path );
            }
        }
        if( f.zPfad()->hat( ".gsl" ) )
        {
            HMODULE dll = Framework::getDLLRegister()->ladeDLL( "GSL.dll", "data/bin/GSL.dll" );
            GSL::GSLDateiV *d = getGSLDatei();
            d->setDatei( (char *)folderPath );
            d->leseDaten();
            int anz = d->getSoundAnzahl();
            for( int i = 0; i < anz; i++ )
            {
                Text *path = new Text( folderPath );
                path->ersetzen( 0, (int)strlen( "data/spiele/Asteroids" ), "spiel:" );
                Text *name = d->getSoundName( i );
                path->append( (const char *)( Text( "/" ) + (const char *)name->getText() ) );
                name->release();
                if( doesResourceExist( path->getText() ) )
                    path->release();
                else
                    zPaths->add( path );
            }
        }
    }
}

// l�scht das objekt wenn es nicht mehr gebraucht wird und beendet den Thread
void KarteDaten::stopWork()
{
    if( run )
    {
        exit = 1;
        if( isRunning() )
            warteAufThread( INT_MAX );
    }
}