#include "Trigger.h"
#include "Gegenstand.h"


VarPointer::VarPointer( const char *name, Variable *var )
{
    this->name = name;
    this->var = var;
    ref = 1;
}

VarPointer::~VarPointer()
{
    if( var )
        var->release();
}

Text VarPointer::getName() const
{
    return name;
}

void VarPointer::setVariable( Variable *var )
{
    if( this->var )
        this->var->release();
    this->var = var;
}

Variable *VarPointer::getVariable() const
{
    return var ? var->getThis() : 0;
}

Variable *VarPointer::zVariable() const
{
    return var;
}

VarPointer::operator Variable *( ) const
{
    return var;
}

VarPointer *VarPointer::getThis()
{
    ref++;
    return this;
}

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


LocalMemory::LocalMemory()
{
    ref = 1;
}

LocalMemory::~LocalMemory()
{}

void LocalMemory::setVar( const char *name, Variable *var )
{
    for( auto v = vars.getIterator(); v; v++ )
    {
        if( v->getName().istGleich( name ) )
        {
            v->setVariable( var );
            return;
        }
    }
    vars.add( new VarPointer( name, var ) );
}

Variable *LocalMemory::getVariable( const char *name )
{
    for( auto v = vars.getIterator(); v; v++ )
    {
        if( v->getName().istGleich( name ) )
            return v->getVariable();
    }
    return 0;
}

Variable *LocalMemory::zVariable( const char *name )
{
    for( auto v = vars.getIterator(); v; v++ )
    {
        if( v->getName().istGleich( name ) )
            return v->zVariable();
    }
    return 0;
}

LocalMemory *LocalMemory::getThis()
{
    ref++;
    return this;
}

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


ProgramCounter::ProgramCounter()
{
    current.add( 0 );
    depth = 0;
    ref = 1;
}

ProgramCounter::~ProgramCounter()
{}

void ProgramCounter::stepIn()
{
    depth++;
    if( current.getEintragAnzahl() <= depth )
        current.add( 0 );
}

void ProgramCounter::count()
{
    current.set( current.get( depth ) + 1, depth );
    while( depth + 1 < current.getEintragAnzahl() )
        current.remove( depth + 1 );
}

void ProgramCounter::stepOut()
{
    depth--;
}

Text ProgramCounter::getUniqueString() const
{
    Text ret = "__";
    for( int i = 0; i < depth; i++ )
        ret += Text( current.get( i ) ) + "__";
    return ret;
}

int ProgramCounter::currentPosition() const
{
    return current.get( depth );
}

ProgramCounter *ProgramCounter::getThis()
{
    ref++;
    return this;
}

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


Bedingung::Bedingung( Aktion *expression )
{
    this->expression = expression;
    ref = 1;
}

Bedingung::~Bedingung()
{
    if( expression )
        expression->release();
}

void Bedingung::setExpression( Aktion *expr )
{
    if( expression )
        expression->release();
    expression = expr;
}

bool Bedingung::check( Spiel *zSpiel, Ereignis *zEreignis )
{
    if( !expression )
        return 1;
    double wait = 0;
    ProgramCounter c;
    LocalMemory m;
    while( !expression->runNext( zSpiel, zEreignis, &m, &c, wait ) )
        wait = 0;
    Variable *var = m.zVariable( "__return__" );
    return isTrue( var );
}

Bedingung *Bedingung::getThis()
{
    ref++;
    return this;
}

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


Trigger::Trigger( int id, const char *name, int ereignisAnzahl, EreignisTyp *ereignisse, RCArray< Bedingung > *bedingungen, RCArray< Aktion > *aktionen )
    : Variable( TRIGGER )
{
    this->id = id;
    this->name = name;
    this->ereignisAnzahl = ereignisAnzahl;
    this->ereignisse = ereignisse;
    this->bedingungen = bedingungen;
    this->aktionen = aktionen;
    aktiv = 1;
    runCount = 0;
}

Trigger::~Trigger()
{
    delete[]ereignisse;
    bedingungen->release();
    aktionen->release();
}

void Trigger::setAktiv( bool aktiv )
{
    this->aktiv = aktiv;
}

bool Trigger::hatEreignis( EreignisTyp typ ) const
{
    for( int i = 0; i < ereignisAnzahl; i++ )
    {
        if( ereignisse[ i ] == typ )
            return 1;
    }
    return 0;
}

int Trigger::getAktionAnzahl() const
{
    return aktionen->getEintragAnzahl();
}

Aktion *Trigger::zAktion( int index ) const
{
    return aktionen->z( index );
}

Aktion *Trigger::getAktion( int index ) const
{
    return aktionen->get( index );
}

// return: 0, falls die bedingungen nicht erf�llt sind
TriggerRun *Trigger::runTrigger( Ereignis *e, Spiel *zSpiel )
{
    if( !aktiv )
    {
        e->release();
        return 0;
    }
    for( auto b = bedingungen->getIterator(); b; b++ )
    {
        if( !b->check( zSpiel, e ) )
        {
            e->release();
            return 0;
        }
    }
    runCount++;
    return new TriggerRun( (Trigger *)getThis(), e, zSpiel );
}

int Trigger::getId() const
{
    return id;
}

int Trigger::getRuns() const
{
    return runCount;
}

const char *Trigger::getName() const
{
    return name.getText();
}

bool Trigger::istAktiv() const
{
    return aktiv;
}


TriggerRun::TriggerRun( Trigger *trig, Ereignis *e, Spiel *zSpiel )
{
    trigger = trig;
    ereignis = e;
    this->zSpiel = zSpiel;
    waitCount = 0;
    ref = 1;
}

TriggerRun::~TriggerRun()
{
    trigger->release();
    ereignis->release();
}

// gibt 0 zur�ck, wenn der Ausl�ser vollst�ndig durchgelaufen ist
bool TriggerRun::runNext( double t )
{
    if( waitCount > 0 )
        waitCount -= t;
    else
    {
        int current = counter.currentPosition();
        if( current >= trigger->getAktionAnzahl() )
            return 0;
        Aktion *ak = trigger->zAktion( current );
        if( !ak || ak->runNext( zSpiel, ereignis, &localMem, &counter, waitCount ) )
            counter.count();
        if( counter.currentPosition() >= trigger->getAktionAnzahl() )
            return 0;
    }
    return 1;
}

Trigger *TriggerRun::getTrigger() const
{
    return (Trigger *)trigger->getThis();
}

TriggerRun *TriggerRun::getThis()
{
    ref++;
    return this;
}

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