#include "Map.h"
#include <InitDatei.h>
#include <Datei.h>
#include <KSGTDatei.h>
#include <Zeit.h>
#include <Text.h>
#include <TastaturEreignis.h>
#include <MausEreignis.h>
#include <AsynchronCall.h>

// Inhalt der Map Klasse aus Map.h
// Konstruktor
Map::Map( KSGClient::MinigameServerClient *klient )
{
    this->klient = klient;
    map = new RCArray< Array< int > >();
    score = 0;
    scoreCheck = score * 11197;
    breite = 0;
    h�he = 0;
    geschwindigkeit = 0;
    bAnzahl = 0;
    next = 0;
    beendet = 1;
    rend = 0;
    rGen = 0;
    gameTime = 0;
    ref = 1;
}

// Destruktor
Map::~Map()
{
    speichern();
    map->release();
    if( rGen )
        rGen->release();
    if( klient )
        klient->release();
}

// private
void Map::remove( int x, int y )
{
    if( !map->z( x ) || !map->z( x )->hat( y ) || !map->z( x )->get( y ) )
        return;
    int f = map->z( x )->get( y );
    map->z( x )->set( 0, y );
    if( !f )
        return;
    if( map->z( x - 1 ) && map->z( x - 1 )->hat( y ) && map->z( x - 1 )->get( y ) == f )
        remove( x - 1, y );
    if( map->z( x + 1 ) && map->z( x + 1 )->hat( y ) && map->z( x + 1 )->get( y ) == f )
        remove( x + 1, y );
    if( map->z( x ) && map->z( x )->hat( y - 1 ) && map->z( x )->get( y - 1 ) == f )
        remove( x, y - 1 );
    if( map->z( x ) && map->z( x )->hat( y + 1 ) && map->z( x )->get( y + 1 ) == f )
        remove( x, y + 1 );
}

// nicht constant
void Map::reset( Text *zOptionen )
{
    gameTime = 0;
    next = 0;
    beendet = 0;
    score = 0;
    scoreCheck = score * 11197;
    map->leeren();
    Text *tmp = zOptionen->getTeilText( zOptionen->positionVon( '=' ) + 1, zOptionen->positionVon( ',' ) );
    breite = *tmp;
    tmp->release();
    tmp = zOptionen->getTeilText( zOptionen->positionVon( '=', 1 ) + 1, zOptionen->positionVon( ',', 1 ) );
    h�he = *tmp;
    tmp->release();
    tmp = zOptionen->getTeilText( zOptionen->positionVon( '=', 2 ) + 1, zOptionen->positionVon( ',', 2 ) );
    bAnzahl = *tmp;
    tmp->release();
    tmp = zOptionen->getTeilText( zOptionen->positionVon( '=', 3 ) + 1, zOptionen->positionVon( ',', 3 ) );
    geschwindigkeit = *tmp;
    tmp->release();
    tmp = zOptionen->getTeilText( zOptionen->positionVon( '=', 4 ) + 1, zOptionen->positionVon( ',', 4 ) );
    bool fortsetzen = (int)*tmp != 0;
    tmp->release();
    if( rGen )
        rGen = rGen->release();
    if( fortsetzen && DateiExistiert( "data/Minigames/Bl�cke/data/game.save" ) && klient )
    {
        if( capture.istOffen() )
            capture.close();
        capture.setDatei( "data/Minigames/Bl�cke/data/game.mgc" );
        capture.open( Datei::Style::schreiben | Datei::Style::ende | Datei::Style::lesen );
        Datei *save = new Datei();
        save->setDatei( "data/Minigames/Bl�cke/data/game.save" );
        save->open( Datei::Style::lesen );
        int br = 0;
        int h� = 0;
        __int64 seed;
        save->lese( (char*)&seed, 8 );
        rGen = new RandomGenerator();
        rGen->setSeed( seed );
        save->lese( (char*)&gameTime, 8 );
        save->lese( (char*)&br, 4 );
        save->lese( (char*)&h�, 4 );
        if( br == breite && h� == h�he )
        {
            save->lese( (char*)&score, 4 );
            scoreCheck = score * 11197;
            for( int x = 0; x < breite; x++ )
            {
                map->set( new Array< int >(), x );
                for( int y = 0; y < h�he; y++ )
                {
                    int f = 0;
                    save->lese( (char*)&f, 4 );
                    map->z( x )->set( f, y );
                }
            }
            next = geschwindigkeit / 1000.0;
        }
        save->close();
        save->release();
        int xOff = 0;
        for( int x = 0; x < breite; x++ )
        {
            if( !map->z( x - xOff ) )
                continue;
            Array< int > *row = map->z( x - xOff );
            int yOff = 0;
            for( int y = 0; y < h�he; y++ )
            {
                if( row->hat( y - yOff ) && !row->get( y - yOff ) )
                {
                    row->remove( y - yOff );
                    yOff++;
                }
            }
            bool del = 1;
            for( int y = 0; y < h�he; y++ )
                del &= !row->hat( y - yOff ) || !row->get( y - yOff );
            if( del )
            {
                map->remove( x - xOff );
                xOff++;
            }
        }
    }
    else
    {
        rGen = new RandomGenerator();
        if( klient )
        {
            if( capture.istOffen() )
                capture.close();
            DateiRemove( "data/Minigames/Bl�cke/data/game.mgc" );
            capture.setDatei( "data/Minigames/Bl�cke/data/game.mgc" );
            capture.erstellen();
            capture.open( Datei::Style::schreiben );
            __int64 seed = rGen->getSeed();
            capture.schreibe( (char*)&seed, 8 );
        }
        else
        {
            tmp = zOptionen->getTeilText( zOptionen->positionVon( '=', 5 ) + 1 );
            rGen->setSeed( (__int64)*tmp );
            tmp->release();
        }
    }
}

void Map::doPublicMausEreignis( MausEreignis &me )
{
    if( beendet )
        return;
    cs.lock();
    if( me.id == ME_RRechts )
    {
        next = 0;
        if( klient )
        {
            capture.schreibe( (char*)&gameTime, 8 );
            capture.schreibe( "\x1", 1 );
        }
    }
    if( me.id != ME_RLinks )
    {
        cs.unlock();
        return;
    }
    if( klient )
    {
        capture.schreibe( (char*)&gameTime, 8 );
        capture.schreibe( "\x0", 1 );
        capture.schreibe( (char*)&me.mx, 4 );
        capture.schreibe( (char*)&me.my, 4 );
    }
    int x = (int)( me.mx / ( 800.0 / breite ) );
    int y = (int)( h�he - ( me.my / ( 500.0 / h�he ) ) );
    if( !map->z( x ) || !map->z( x )->hat( y ) || !map->z( x )->get( y ) )
    {
        cs.unlock();
        return;
    }
    bool remove = 0;
    int f = map->z( x )->get( y );
    if( map->z( x - 1 ) && map->z( x - 1 )->hat( y ) && map->z( x - 1 )->get( y ) == f )
        remove = 1;
    if( map->z( x + 1 ) && map->z( x + 1 )->hat( y ) && map->z( x + 1 )->get( y ) == f )
        remove = 1;
    if( map->z( x ) && map->z( x )->hat( y - 1 ) && map->z( x )->get( y - 1 ) == f )
        remove = 1;
    if( map->z( x ) && map->z( x )->hat( y + 1 ) && map->z( x )->get( y + 1 ) == f )
        remove = 1;
    if( !remove )
    {
        cs.unlock();
        return;
    }
    rend = 1;
    this->remove( x, y );
    int xOff = 0;
    for( int x = 0; x < breite; x++ )
    {
        if( !map->z( x - xOff ) )
            continue;
        Array< int > *row = map->z( x - xOff );
        int yOff = 0;
        for( int y = 0; y < h�he; y++ )
        {
            if( row->hat( y - yOff ) && !row->get( y - yOff ) )
            {
                row->remove( y - yOff );
                yOff++;
            }
        }
        bool del = 1;
        for( int y = 0; y < h�he; y++ )
            del &= !row->hat( y - yOff ) || !row->get( y - yOff );
        if( del )
        {
            score++;
            scoreCheck = score * 11197;
            map->remove( x - xOff );
            xOff++;
        }
    }
    cs.unlock();
}

void Map::doTastaturEreignis( TastaturEreignis &te )
{
    cs.lock();
    if( te.id == TE_Release )
    {
        next = 0;
        if( klient )
        {
            capture.schreibe( (char*)&gameTime, 8 );
            capture.schreibe( "\x1", 1 );
        }
    }
    cs.unlock();
}

bool Map::tick( double tickVal )
{
    bool ret = rend;
    rend = 0;
    if( beendet )
        return ret;
    cs.lock();
    gameTime += tickVal;
    next -= tickVal;
    if( next < 0 )
    {
        next = geschwindigkeit / 1000.0;
        Array< int > *n = new Array< int >();
        for( int i = 0; i < h�he; i++ )
        {
            int val = (int)( rGen->rand() * bAnzahl );
            switch( val )
            {
            case 0:
                n->set( 0xFFFF0000, i );
                break;
            case 1:
                n->set( 0xFF00FF00, i );
                break;
            case 2:
                n->set( 0xFF0000FF, i );
                break;
            case 3:
                n->set( 0xFFFFFF00, i );
                break;
            case 4:
                n->set( 0xFF00FFFF, i );
                break;
            case 5:
                n->set( 0xFFFF00FF, i );
                break;
            case 6:
                n->set( 0xFFFFFFFF, i );
                break;
            case 7:
                n->set( 0xFFFF5555, i );
                break;
            case 8:
                n->set( 0xFF55FF55, i );
                break;
            case 9:
                n->set( 0xFF5555FF, i );
                break;
            default:
                n->set( 0xFF000000, i );
                break;
            }
        }
        map->add( n, 0 );
        if( map->z( breite ) )
        {
            Array< int > *row = map->z( breite );
            for( int i = 0; i < h�he; i++ )
            {
                if( row->hat( i ) )
                    beendet |= row->get( i ) != 0;
            }
            beendet |= ( score * 11197 != scoreCheck );
            if( score * 11197 != scoreCheck )
            {
                score = 0;
                scoreCheck = 0;
            }
            if( beendet && klient )
            {
                capture.close();
                DateiRemove( "data/Minigames/Bl�cke/data/upload.mgc" );
                DateiUmbenennen( "data/Minigames/Bl�cke/data/game.mgc", "data/Minigames/Bl�cke/data/upload.mgc" );
                int tmpScore = score;
                KSGClient::MinigameServerClient *tmpKlient = klient->getThis();
                new AsynchronCall( [ tmpScore, tmpKlient ]()
                {
                    InitDatei *opd = new InitDatei( "data/Minigames/Bl�cke/data/optionen.ini" );
                    opd->laden();
                    if( !opd->wertExistiert( "Breite" ) )
                        opd->addWert( "Breite", "20" );
                    if( !opd->wertExistiert( "Height" ) )
                        opd->addWert( "Height", "12" );
                    if( !opd->wertExistiert( "Farben" ) )
                        opd->addWert( "Farben", "5" );
                    if( !opd->wertExistiert( "Geschwindigkeit" ) )
                        opd->addWert( "Geschwindigkeit", "4000" );
                    if( !opd->wertExistiert( "Fortsetzen" ) )
                        opd->addWert( "Fortsetzen", "0" );
                    Text optionen = "Width=";
                    optionen += opd->zWert( "Breite" )->getText();
                    optionen += ",Height=";
                    optionen += opd->zWert( "Height" )->getText();
                    optionen += ",Farben=";
                    optionen += opd->zWert( "Farben" )->getText();
                    optionen += ",Speed=";
                    optionen += opd->zWert( "Geschwindigkeit" )->getText();
                    opd->release();
                    Datei d;
                    d.setDatei( "data/Minigames/Bl�cke/data/upload.mgc" );
                    tmpKlient->reportEndOfGame( "Bloecke", optionen, tmpScore, &d );
                    DateiRemove( "data/Minigames/Bl�cke/data/upload.mgc" );
                    tmpKlient->release();
                } );
                KSGTDatei *stb = new KSGTDatei( "data/Minigames/Bl�cke/data/score.ksgt" );
                if( !stb->laden() )
                    DateiPfadErstellen( "data/Minigames/Bl�cke/data/score.ksgt" );
                RCArray< Text > *zeile = new RCArray< Text >();
                Zeit *zeit = getZeit();
                zeile->add( zeit->getZeit( "y-m-d h:i:s" ) );
                zeit->release();
                Text *scoreT = new Text();
                scoreT->append( score );
                zeile->add( scoreT );
                Text *breiteT = new Text();
                breiteT->append( breite );
                zeile->add( breiteT );
                Text *h�heT = new Text();
                h�heT->append( h�he );
                zeile->add( h�heT );
                Text *geschwindigkeitT = new Text();
                geschwindigkeitT->append( geschwindigkeit );
                zeile->add( geschwindigkeitT );
                Text *bAnzahlT = new Text();
                bAnzahlT->append( bAnzahl );
                zeile->add( bAnzahlT );
                stb->addZeile( 6, zeile );
                zeile->release();
                stb->speichern();
                stb->release();
                DateiRemove( "data/Minigames/Bl�cke/data/game.save" );
            }
        }
        map->remove( breite );
        ret = 1;
    }
    cs.unlock();
    return ret;
}

void Map::render( Bild &zRObj )
{
    for( int x = 0; x < breite; x++ )
    {
        if( !map->z( x ) )
            continue;
        Array< int > *row = map->z( x );
        for( int y = 0; y < h�he; y++ )
        {
            if( !row->hat( y ) )
                continue;
            int xs = (int)( x * ( 800.0 / breite ) );
            int ys = (int)( ( h�he - y - 1 ) * ( 500.0 / h�he ) );
            int xe = (int)( ( x + 1 ) * ( 800.0 / breite ) );
            int ye = (int)( ( h�he - y ) * ( 500.0 / h�he ) );
            zRObj.fillRegion( xs, ys, xe - xs, ye - ys, row->get( y ) );
        }
    }
}

void Map::speichern()
{
    if( !beendet && klient )
    {
        if( capture.istOffen() )
            capture.close();
        Datei *d = new Datei();
        d->setDatei( "data/Minigames/Bl�cke/data/game.save" );
        d->erstellen();
        d->open( Datei::Style::schreiben );
        __int64 seed = rGen->getSeed();
        d->schreibe( (char*)&seed, 8 );
        d->schreibe( (char*)&gameTime, 8 );
        d->schreibe( (char*)&breite, 4 );
        d->schreibe( (char*)&h�he, 4 );
        d->schreibe( (char*)&score, 4 );
        for( int x = 0; x < breite; x++ )
        {
            for( int y = 0; y < h�he; y++ )
            {
                int f = 0;
                if( map->z( x ) && map->z( x )->hat( y ) )
                    f = map->z( x )->get( y );
                d->schreibe( (char*)&f, 4 );
            }
        }
        d->close();
        d->release();
    }
    else if( klient )
        DateiRemove( "data/Minigames/Bl�cke/data/game.save" );
}

// constant
int Map::getScore() const
{
    return score;
}

bool Map::istBeendet() const
{
    return beendet;
}

// Reference Counting
Map *Map::getThis()
{
    ref++;
    return this;
}

Map *Map::release()
{
    ref--;
    if( !ref )
        delete this;
    return 0;
}