/*
 Copyright (C) 2002 Andreas Thiede ( a.thiede@berlin.de )

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <IO/Manager/IOObject.hpp>
#include <pthread.h>

#define MY_MAGIC 0xdeabbeaf

class IOObjectPrivate 
{
    public:
    
        IOObjectPrivate( const string & aName, const string & aType ) 
         : mName( aName ), mType( aType ), mUseCount(1) {


            assert( pthread_mutex_init( &mMutex, NULL) == 0 );

            assert( pthread_cond_init( &mCondition, NULL) == 0 );

            mLockOwner   = (pthread_t) 0;
            mLockCounter = 0;

        }
        
        ~IOObjectPrivate() {

            pthread_mutex_destroy( &mMutex );
        
            pthread_cond_destroy( &mCondition );
        }
        
	string            mName;
        string            mType;   
        int               mUseCount;     
        pthread_mutex_t   mMutex;
        pthread_cond_t    mCondition;
        pthread_t         mLockOwner;
        int               mLockCounter;
};

IOObject::IOObject( const string & aName, const string & aType )
    : mPrivate( new IOObjectPrivate( aName, aType ) ), mMagic( MY_MAGIC )
{
}

IOObject::~IOObject()
{
    delete mPrivate;
    
    mPrivate = 0;
}

void IOObject::setName( const string & aName )
{
    mPrivate->mName = aName;
}

void IOObject::setType( const string & aType )
{
    mPrivate->mType = aType;
}

int IOObject::useCount()
{
    lock();

    int result = mPrivate->mUseCount;
    
    unlock();
    
    return result;
}

bool IOObject::isA( const string & aType ) const
{
    return aType == mPrivate->mType;
}

void IOObject::retain()
{
    lock();
    
    mPrivate->mUseCount++;

    //printf("%s:%s: useCount(%d)\n", getType().c_str(), getName().c_str(), mPrivate->mUseCount );

    unlock();
}

#if 0 

void IOObject::release()
{
    // situation is: two or more threads wants to release this object
    // after first object deletes this, no other thread can work with this, even do an unlock() - operation;
        
    if( mMagic == MY_MAGIC ) {
    
        lock();

        //printf("IOObject::release %s:%s: found magic\n", getType().c_str(), getName().c_str() );

        if( mMagic == MY_MAGIC ) {

            if( mPrivate->mUseCount > 0 ) {

                //printf("IOObject::release %s:%s: useCount(%d)\n", getType().c_str(), getName().c_str(), mPrivate->mUseCount );
        
                mPrivate->mUseCount--;

                if( mPrivate->mUseCount == 0 ) {

                    mMagic = 0;
                    
                    //puts("IObject delete");

                    unlock();
                    
                    delete this;
                    return;
                }
            }            
            unlock();
        }
    }
}

#else

void IOObject::release()
{
    lock();

    if( mMagic == MY_MAGIC ) {
 
        if( mPrivate->mUseCount > 0 ) {

            mPrivate->mUseCount--;

            if( mPrivate->mUseCount == 0 ) {

                mMagic = 0;
                
                delete this;

                return;
            }
        }
    }
    
    unlock();
}
#endif
void IOObject::lock()
{
    if( mPrivate->mLockOwner == pthread_self() ) {
        mPrivate->mLockCounter++;
        
    }
    else {

        pthread_mutex_lock ( &mPrivate->mMutex );

        mPrivate->mLockOwner   = pthread_self();
        mPrivate->mLockCounter = 1;
    }
}

void IOObject::unlock()
{
    if( mPrivate->mLockCounter > 0 ) {
        mPrivate->mLockCounter--;
    }
    
    if( mPrivate->mLockCounter == 0 ) {
    
        mPrivate->mLockOwner = (pthread_t) 0;
        
        pthread_mutex_unlock( &mPrivate->mMutex );
    }
}

void IOObject::signal()
{ 
    pthread_cond_signal( &mPrivate->mCondition );
}

void IOObject::wait( int milisecs )
{ 
    assert( mPrivate->mLockCounter > 0 && mPrivate->mLockOwner == pthread_self() );
    
    int safedCounter = mPrivate->mLockCounter;
    
    if( milisecs == -1 ) {
        
        pthread_cond_wait( &mPrivate->mCondition, &mPrivate->mMutex );

    }
    else {
    
        struct timeval now;
        
        /*int rc = */  gettimeofday( &now, 0 );
        
        //printf( "rc of gettimeofday = %d\n", rc );
        
        struct timeval delta;

        delta.tv_sec  = 0;
        delta.tv_usec = 1000 * milisecs;
        
        struct timeval until;
        
        timeradd( &now, &delta, &until );
        
        struct timespec ts_until;
        
        TIMEVAL_TO_TIMESPEC( &until, &ts_until );
        
        pthread_cond_timedwait( &mPrivate->mCondition, &mPrivate->mMutex, &ts_until );
        
    }
    
    mPrivate->mLockOwner   = pthread_self();
    mPrivate->mLockCounter = safedCounter;
}

const string & IOObject::getName() const
{
    return mPrivate->mName;
}

const string & IOObject::getType() const
{
    return mPrivate->mType;
}

bool IOObject::command( const string & command, const string & param1, const string & param2, const string & param3, const string & param4 )
{
    return false;
}

string IOObject::getValue( const string & valueName )
{
    return "";
}
