#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;
    }
    // Gibt die Id des aktuell spielenden Accounts zur�ck
    int getAccountId()
    {
        return 1;
    }
    // Gibt den Anzeigenamen eines bestimmten Accounts zur�ck
    //  id: Die id des Accounts
    Text *getAccountName( int id )
    {
        return new Text( "Test" );
    }
    // Gibt eine geheime zeichenkette die nur der client kennt zur�ck (ist bei spielstart)
    Text *getSecret()
    {
        return new Text( "ec6e2d97b2a32423082c6385699ace1067c3a3719debe6f761b5aff1159c9342b6eeba932b2ea3db8d48c0e53e0e8dcefa56723de7488d49e9bccd197d334276" );
    }
    // 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 )
    : Thread()
{
    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();
    release();
}


// Inhalt der MGLaden Klasse aus MiniGameV.h
// Konstruktor
MGLaden::MGLaden( char *name, DLLDateien *zDLLs, API *api )
    : Thread()
{
    this->api = api;
    this->name = new Text( name );
    game = 0;
    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();
        return;
    }
    pfad->append( "/mg.ini" );
    if( !DateiExistiert( pfad->getThis() ) )
    {
        pfad->release();
        return;
    }
    InitDatei *mgIni = new InitDatei( pfad );
    if( !mgIni->laden() )
    {
        mgIni->release();
        return;
    }
    if( !mgIni->wertExistiert( "DllPfad" ) )
    {
        mgIni->release();
        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();
        return;
    }
    HMODULE dll = dLLs->ladeDLL( name->getText(), dllPfad->getText() );
    dllPfad->release();
    if( !dll )
        return;
    GetMiniGame getMiniGame = (GetMiniGame)GetProcAddress( dll, "GetMiniGame" );
    if( !getMiniGame )
    {
        dLLs->releaseDLL( name->getText() );
        return;
    }
    game = getMiniGame();
    if( !game )
    {
        dLLs->releaseDLL( name->getText() );
        return;
    }
    game->setMinigameClientZ( new MSCWrapper() );
    game->setMinigameAPI( api );
    if( !game->laden() )
    {
        game = game->release();
        dLLs->releaseDLL( name->getText() );
    }
}

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

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


// Inhalt der MiniGames Klasse aus MiniGames.h
// Konstruktor
MiniGames::MiniGames( Schrift *zSchrift, DLLDateien *zDLLs, Bildschirm *screen, API *api )
    : Zeichnung()
{
    this->api = api;
    this->screen = screen;
    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;
    new MGSuchen( (MiniGames*)getThis() );
}

// Destruktor
MiniGames::~MiniGames()
{
    if( schrift )
        schrift->release();
    rahmen->release();
    games->release();
    if( mgl )
        mgl->release();
    dLLs->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 = (MGLaden*)mgl->release();
                mgl = new MGLaden( games->z( i )->zName()->getText(), dLLs, api );
            }
        }
    }
    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 = (MGLaden*)mgl->release();
        }
        else
        {
            mgl->zGame()->setSchriftZ( schrift->getThis() );
            mgl->zGame()->setBildschirmZ( screen );
        }
    }
    if( mgl && mgl->zGame() && !alpha2 )
    {
        rend |= mgl->zGame()->tick( z );
        if( mgl->zGame()->istEnde() )
            mgl = (MGLaden*)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();
    zRObj.releaseDrawOptions();
    if( mgl && mgl->fertig() && mgl->zGame() )
        mgl->zGame()->render( zRObj );
}