/*
 * Copyright (C) 2007-2023 Xagasoft, All rights reserved.
 *
 * This file is part of the libbu++ library and is released under the
 * terms of the license contained in the file LICENSE.
 */
#include <bu/readwritemutex.h>
#include <bu/thread.h>
#include <bu/randomcmwc.h>
#include <bu/sio.h>

using namespace Bu;

#define CNT 15

char disp[CNT*2+1];

ReadWriteMutex mRW;
bool bRunning;

int rbase;

Mutex mDisp;

void dispUpd( char &ind, char val )
{
    mDisp.lock();
    ind = val;
    println(disp);
    mDisp.unlock();
}

class Writer : public Thread
{
public:
    Writer( int iId, char &ind ) :
        iId( iId ),
        rand( rbase+iId ),
        ind( ind )
    {
    }

    virtual ~Writer()
    {
    }

protected:
    virtual void run()
    {
        usleep( rand.rand(1000000) );
        while( bRunning )
        {
            dispUpd( ind, '.' );
            mRW.lockWrite();
            dispUpd( ind, 'W' );
//            println("Writer %1 locking.").arg( iId );
            usleep( rand.rand(1,10)*10000 );
//            println("Writer %1 unlocking.").arg( iId );
            dispUpd( ind, ' ' );
            mRW.unlockWrite();
            usleep( rand.rand(5,10)*10000 );
        }
    }

private:
    int iId;
    RandomCmwc rand;
    char &ind;
};

class Reader : public Thread
{
public:
    Reader( int iId, char &ind ) :
        iId( iId ),
        rand( rbase-iId ),
        ind( ind )
    {
    }

    virtual ~Reader()
    {
    }

protected:
    virtual void run()
    {
        usleep( rand.rand(1000000) );
        while( bRunning )
        {
            dispUpd( ind, '.' );
            mRW.lockRead();
            dispUpd( ind, 'R' );
//            println("Reader %1 locking.").arg( iId );
            usleep( rand.rand(1,10)*10000 );
//            println("Reader %1 unlocking.").arg( iId );
            dispUpd( ind, ' ' );
            mRW.unlockRead();
            usleep( rand.rand(5,10)*10000 );
        }
    }

private:
    int iId;
    RandomCmwc rand;
    char &ind;
};

int main()
{
    bRunning = true;
    rbase = time( NULL );

    memset( disp, 0x20, CNT*2+1 );
    disp[CNT*2] = 0;

    Thread **threads = new Thread*[CNT*2];
    for( int j = 0; j < CNT; j++ )
    {
        threads[j] = new Reader( j+1, disp[j+CNT] );
        threads[j+CNT] = new Writer( j+1, disp[j] );
    }

    for( int j = 0; j < CNT*2; j++ )
        threads[j]->start();

    sleep( 120 );
    bRunning = false;

    for( int j = 0; j < CNT*2; j++ )
    {
        threads[j]->join();
        delete threads[j];
    }

    delete[] threads;

    return 0;
}


