#include "MiniGames.h"
#include <Punkt.h>
#include <Rahmen.h>
#include <Datei.h>
#include <InitDatei.h>
#include <KSGTDatei.h>
#include <KSGNetwork.h>

class MSCWrapper : public KSGClient::MinigameServerClient
{
private:
    int ref;
public:
    MSCWrapper() noexcept
    {
        ref = 1;
    }
    // verbindet sich mit dem zugewiesenen Minigame Server
    //  Gibt 1 zurück, falls der Vorgang erfolgreich ist, 0 sonnst
    bool verbinde()
    {
        return 1;
    }
    // Ermittelt die liste mit allen Optionen zu einem Minigame zurück, zu denen es Welt beste Scores gibt
    //  mName: Der Name des Minigames
    //  zOptionList: Enthält nach erfolgreichem Aufruf eine Liste mit Optionen
    //  Gibt die Anzahl der Optionen zurück
    int getMinigameOptionList( char *mName, Framework::RCArray< Framework::Text > *zOptionList )
    {
        return 0;
    }
    // Ermittelt eine Liste mit den Weltbesten Scores zurück
    //  mName: Der Name des Minigames
    //  zScore: Enthält nach erfolgreichem Aufruf eine Liste mit Scores
    //  zPlayerList: Enthält nach erfolgreichem Aufruf eine Liste mit angezeigten Account Namen, die die Scores erreicht haben.
    //  zOptionList: Enthält nach erfolgreichem Aufruf eine Liste mit Optionen, die beim erreichen der Scores aktiv waren.
    //  Gibt die Anzahl der Bestscores zurück
    int getMinigameBestscoreList( char *mName, Framework::Array< int > *zScore, Framework::RCArray< Framework::Text > *zPlayerList, Framework::RCArray< Framework::Text > *zOptionList )
    {
        return 0;
    }
    // Gibt den Welt bestscore zu einem Bestimmten Minigame mit bestimmten Optionen zurück.
    //  mName: Der Name des Minigames
    //  oName: Die Optionen
    //  zPlayer: Enthält nach erfolgreichem Aufruf den Angezeigten Namen des Accounts, der den Score erreicht hat
    int getMinigameOptionBestscore( char *mName, char *oName, Framework::Text *zPlayer )
    {
        return 0;
    }
    // Meldet die Beendigung eines Minigames
    //  mName: Der Name des Minigames
    //  oName: Die Optionen mit denen gespielt wurde
    //  score: Der Erreichte Score
    //  zCapture: Ein Zeiger auf eine Datei mit der Spielaufzeichnung
    //  Gibt 0 zurück wenn eines Fehler aufgetreten ist, 1 wenn der Forgang erfolgreich war
    bool reportEndOfGame( char *mName, char *oName, int score, Framework::Datei *zCapture )
    {
        return 1;
    }
    // Lädt ein Game Capture herunter und speichert sie unter data/tmp/minigames/wb.mgc
    //  mName: Der Name des Minigames
    //  oName: Die Optionen
    //  Gibt die Datei mit dem Capture zurück
    Framework::Datei *downloadGameCapture( char *mName, char *oName )
    {
        return 0;
    }
    // Erhält die Verbindung aufrecht
    //  Gibt 1 zurück, falls der Vorgang erfolgreich ist, 0 sonnst
    //  Sollte während einer bestehenden Verbindung etwa einmal alle 60 Sekunden aufgerufen werden, da sonst der Router die Verbindung automatisch trennt
    bool keepAlive()
    {
        return 1;
    }
    // Trennt die Verbindung zum Server
    //  Gibt 1 zurück, falls der Vorgang erfolgreich ist, 0 sonnst
    //  Sollte erst nach einem erfolgreichen Aufruf von verbinde aufgerufen werden
    bool trenne( bool abmelden )
    {
        return 1;
    }
    // Gibt 1 zurück, falls der Client verbunden ist, 0 sonst
    bool istVerbunden() const
    {
        return 1;
    }
    // gibt den Letzten Fehlertext zuück
    //  sollte erst aufgerufen werden, nachdem eine andere aufgerufene Methode fehlgeschlagen ist
    char *getLetzterFehler() const
    {
        return "";
    }
    // Erhöht den Reference Counter um 1 un gibt this zurück
    MinigameServerClient *getThis()
    {
        ref++;
        return this;
    }
    // Verringert den Reference Counter um 1 und gibt 0 zurück.
    //  Falls der Reference Counter nach dem Aufruf auf 0 ist löscht sich das Objekt selbst 
    MinigameServerClient *release()
    {
        if( !--ref )
            delete this;
        return 0;
    }
};

typedef MiniGameV *( *GetMiniGame )( );

// Inhalt der MGLaden Klasse aus MiniGames.h
// Konstruktor
MGSuchen::MGSuchen( MiniGames *mGames )
{
    this->mGames = mGames;
    start();
}

// Destruktor
MGSuchen::~MGSuchen()
{
    mGames->release();
}

// nicht constant
void MGSuchen::thread()
{
    Datei *d = new Datei();
    d->setDatei( "data/Minigames" );
    if( !d->existiert() )
        DateiPfadErstellen( "data/MiniGames/" );
    RCArray< Text > *list = d->getDateiListe();
    if( list )
    {
        for( int i = 0; i < list->getEintragAnzahl(); i++ )
        {
            MiniGame *mg = new MiniGame( list->z( i )->getText() );
            if( !mg->istOk() )
            {
                mg->release();
                continue;
            }
            mGames->addMiniGame( mg );
        }
        list->release();
    }
    d->release();
    delete this;
}


// Inhalt der MGLaden Klasse aus MiniGameV.h
// Konstruktor
MGLaden::MGLaden( char *name, DLLDateien *zDLLs )
{
    this->name = new Text( name );
    game = 0;
    ref = 1;
    dLLs = zDLLs->getThis();
    start();
}

// Destruktor
MGLaden::~MGLaden()
{
    if( game )
    {
        game->release();
        dLLs->releaseDLL( name->getText() );
    }
    name->release();
    dLLs->release();
}

// nicht constant
void MGLaden::thread()
{
    Text *pfad = new Text( "data/Minigames/" );
    pfad->append( name->getText() );
    if( !DateiExistiert( pfad->getThis() ) )
    {
        pfad->release();
        run = 0;
        return;
    }
    pfad->append( "/mg.ini" );
    if( !DateiExistiert( pfad->getThis() ) )
    {
        pfad->release();
        run = 0;
        return;
    }
    InitDatei *mgIni = new InitDatei( pfad );
    if( !mgIni->laden() )
    {
        mgIni->release();
        run = 0;
        return;
    }
    if( !mgIni->wertExistiert( "DllPfad" ) )
    {
        mgIni->release();
        run = 0;
        return;
    }
    Text *dllPfad = new Text( "data/Minigames/" );
    dllPfad->append( name->getText() );
    dllPfad->append( "/" );
    dllPfad->append( mgIni->zWert( "DllPfad" )->getText() );
    mgIni->release();
    if( !DateiExistiert( dllPfad->getThis() ) )
    {
        dllPfad->release();
        run = 0;
        return;
    }
    HMODULE dll = dLLs->ladeDLL( name->getText(), dllPfad->getText() );
    dllPfad->release();
    if( !dll )
    {
        run = 0;
        return;
    }
    GetMiniGame getMiniGame = (GetMiniGame)GetProcAddress( dll, "GetMiniGame" );
    if( !getMiniGame )
    {
        dLLs->releaseDLL( name->getText() );
        run = 0;
        return;
    }
    game = getMiniGame();
    if( !game )
    {
        dLLs->releaseDLL( name->getText() );
        run = 0;
        return;
    }
    game->setMinigameClientZ( new MSCWrapper() );
    if( !game->laden() )
    {
        game = game->release();
        dLLs->releaseDLL( name->getText() );
    }
    run = 0;
}

// constant
bool MGLaden::fertig() const
{
    return !isRunning();
}

MiniGameV *MGLaden::zGame() const
{
    return game;
}

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

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


// Inhalt der MiniGames Klasse aus MiniGames.h
// Konstruktor
MiniGames::MiniGames( Schrift *zSchrift, DLLDateien *zDLLs, Bildschirm *screen )
    : Zeichnung()
{
    this->screen = screen->getThis();
    dLLs = zDLLs->getThis();
    schrift = zSchrift->getThis();
    bildschirmGröße = BildschirmGröße();
    rahmen = new LRahmen();
    setSize( 800, 500 );
    rahmen->setFarbe( 0xFFFFFFFF );
    alpha2 = 0;
    tickVal = 0;
    games = new RCArray< MiniGame >();
    mgl = 0;
    ref = 1;
    new MGSuchen( getThis() );
}

// Destruktor
MiniGames::~MiniGames()
{
    if( schrift )
        schrift->release();
    rahmen->release();
    games->release();
    if( mgl )
        mgl->release();
    dLLs->release();
    screen->release();
}

// nicht constant
void MiniGames::addMiniGame( MiniGame *mg )
{
    games->add( mg );
    int i = games->getEintragAnzahl() - 1;
    games->z( i )->setPosition( 10 + 10 * ( i % 3 ) + 250 * ( i % 3 ), 50 + 10 * ( i / 3 ) + 100 * ( i / 3 ) );
    int anz = games->getEintragAnzahl();
    bool *fertig = new bool[ anz ];
    for( int i = 0; i < anz; i++ )
        fertig[ i ] = 0;
    for( int i = 0; i < anz; i++ )
    {
        int p = -1;
        for( int j = 0; j < anz; j++ )
        {
            if( !fertig[ j ] )
            {
                p = j;
                games->z( j )->setSichtbar( 1 );
            }
        }
        if( p < 0 )
            break;
        fertig[ p ] = 1;
        games->z( p )->setPosition( 10 + 10 * ( i % 3 ) + 250 * ( i % 3 ), 50 + 10 * ( i / 3 ) + 100 * ( i / 3 ) );
    }
    delete[] fertig;
}

void MiniGames::doMausEreignis( MausEreignis &me )
{
    me.mx -= pos.x;
    me.my -= pos.y;
    if( alpha2 )
    {
        int anz = games->getEintragAnzahl();
        for( int i = 0; i < anz; i++ )
        {
            bool vera = me.verarbeitet;
            games->z( i )->doMausEreignis( me );
            if( !vera && me.verarbeitet && me.id == ME_RLinks )
            { // spiel starten
                if( mgl )
                    mgl = mgl->release();
                mgl = new MGLaden( games->z( i )->zName()->getText(), dLLs );
            }
        }
    }
    if( mgl && mgl->zGame() )
        mgl->zGame()->doMausEreignis( me );
    me.mx += pos.x;
    me.my += pos.y;
}

void MiniGames::doTastaturEreignis( TastaturEreignis &te )
{
    if( mgl && mgl->zGame() )
        mgl->zGame()->doTastaturEreignis( te );
}

bool MiniGames::tick( double z )
{
    if( mgl && mgl->fertig() )
    {
        if( !mgl->zGame() )
        {
            mgl = mgl->release();
        }
        else
        {
            mgl->zGame()->setSchriftZ( schrift->getThis() );
            mgl->zGame()->setBildschirmZ( screen->getThis() );
        }
    }
    if( mgl && mgl->zGame() && !alpha2 )
    {
        rend |= mgl->zGame()->tick( z );
        if( mgl->zGame()->istEnde() )
            mgl = mgl->release();
    }
    if( alpha2 )
    {
        int anz = games->getEintragAnzahl();
        for( int i = 0; i < anz; i++ )
            rend |= games->z( i )->tick( z );
    }
    tickVal += z * 150;
    int val = (int)tickVal;
    if( val < 1 )
    {
        bool ret = rend;
        rend = 0;
        return ret;
    }
    tickVal -= val;
    if( mgl && alpha2 )
    {
        alpha2 -= val;
        if( alpha2 < 0 )
            alpha2 = 0;
        rend = 1;
    }
    if( !mgl && alpha2 != 255 )
    {
        alpha2 += val;
        if( alpha2 > 255 )
            alpha2 = 255;
        rend = 1;
    }
    bool ret = rend;
    rend = 0;
    return 1;
}

void MiniGames::render( Bild &zRObj )
{
    rahmen->setPosition( pos );
    rahmen->setSize( gr );
    rahmen->render( zRObj );
    if( !zRObj.setDrawOptions( pos.x + 1, pos.y + 1, gr.x - 2, gr.y - 2 ) )
        return;
    int rbr = rahmen->getRBreite();
    zRObj.setAlpha( (unsigned char)alpha2 );
    int anz = games->getEintragAnzahl();
    for( int i = 0; i < anz; i++ )
        games->z( i )->render( zRObj );
    zRObj.releaseAlpha();
    if( mgl && mgl->fertig() && mgl->zGame() )
        mgl->zGame()->render( zRObj );
    zRObj.releaseDrawOptions();
}

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

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