#include <bu/signals.h>
#include <bu/sio.h>
#include <bu/randomcmwc.h>

#include <time.h>

template<typename ret>
class SignalCallable
{
public:
    SignalCallable()
    {
    }

    virtual ~SignalCallable()
    {
    }

    virtual ret call()=0;
};

template<typename ret>
class SignalCall0 : public SignalCallable<ret>
{
public:
    SignalCall0( const Bu::Signal0<ret> &sRef ) :
        sRef( sRef )
    {
    }

    virtual ~SignalCall0()
    {
    }

    virtual ret call()
    {
        return sRef();
    }

private:
    Bu::Signal0<ret> sRef;
};

template<typename ret, typename p1t>
class SignalCall1 : public SignalCallable<ret>
{
public:
    SignalCall1( const Bu::Signal1<ret, p1t> &sRef, const p1t &p1 ) :
        sRef( sRef ), p1( p1 )
    {
    }

    virtual ~SignalCall1()
    {
    }

    virtual ret call()
    {
        return sRef( p1 );
    }

private:
    Bu::Signal1<ret, p1t> sRef;
    p1t p1;
};

template<typename ret, typename p1t, typename p2t>
class SignalCall2 : public SignalCallable<ret>
{
public:
    SignalCall2( const Bu::Signal2<ret, p1t, p2t> &sRef, const p1t &p1, const p2t &p2 ) :
        sRef( sRef ), p1( p1 ), p2( p2 )
    {
    }

    virtual ~SignalCall2()
    {
    }

    virtual ret call()
    {
        return sRef( p1, p2 );
    }

private:
    Bu::Signal2<ret, p1t, p2t> sRef;
    p1t p1;
    p2t p2;
};

template<typename ret, typename p1t, typename p2t>
class DeferredSignal2 : public Bu::Signal2<ret, p1t, p2t>
{
    typedef Bu::Signal2<ret, p1t, p2t> Parent;
public:
    DeferredSignal2() : Parent() { }
    DeferredSignal2( Bu::_Slot2<ret, p1t, p2t> *pCb ) : Parent( pCb ) { }
    DeferredSignal2( const DeferredSignal2<ret, p1t, p2t> &rSrc ) :
        Parent( (const Parent &)rSrc ) { }
    virtual ~DeferredSignal2() { }

    void operator()( p1t p1, p2t p2 )
    {
        lCalls.enqueue( SignalCall2<ret, p1t, p2t>( (Parent &)*this, p1, p2 ) );
    }

    DeferredSignal2<ret, p1t, p2t> &operator=( const Bu::Signal2<ret, p1t, p2t> &rhs )
    {
        ((Parent &)*this) = rhs;
        return *this;
    }

    bool operator==( const Bu::Signal2<ret, p1t, p2t> &rhs ) const
    {
        return ((Parent &)*this) == rhs;
    }

    bool hasNext() const
    {
        return !lCalls.isEmpty();
    }

    ret callNext()
    {
        return lCalls.dequeue().call();
    }

private:
    Bu::List< SignalCall2<ret, p1t, p2t> > lCalls;
};


//
// Slots
//
int area( int x, int y )
{
    return x*y;
}

int diff( int x, int y )
{
    return x-y;
}

int thing( int x, int y )
{
    return x*x-y*y;
}

int square( int x )
{
    return x*x;
}

int rand()
{
    Bu::RandomCmwc c;
    c.seed( time( NULL ) );
    return c.rand();
}

int main()
{
    // Direct use of SignalCall objects
    Bu::println("Testing SignalCall classes:");

    typedef Bu::List<SignalCallable<int> * > CallList;
    CallList calls;

    calls.append( new SignalCall2<int, int, int>( Bu::slot(&area), 5, 10 ) );
    calls.append( new SignalCall2<int, int, int>( Bu::slot(&diff), 5, 15 ) );
    calls.append( new SignalCall2<int, int, int>( Bu::slot(&thing), 10, 5 ) );
    calls.append( new SignalCall1<int, int>( Bu::slot(&square), 12 ) );
    calls.append( new SignalCall0<int>( Bu::slot(&rand)) );

    for( CallList::iterator i = calls.begin(); i; i++ )
    {
        Bu::println(" - Result: %1").arg( (*i)->call() );
    }

    // Deferred Signals:
    Bu::println("");
    Bu::println("Testing DeferredSignals:");

    DeferredSignal2<int, int, int> sigArea;
    sigArea = Bu::slot(&area);

    sigArea( 5, 5 );
    sigArea( 10, 10 );
    sigArea( 15, 15 );

    while( sigArea.hasNext() )
    {
        Bu::println(" - Result: %1").arg( sigArea.callNext() );
    }

    return 0;
}

