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

#import <Foundation/NSLock.h>
#import <Foundation/NSDate.h>

#define MY_MAGIC 0xdeabbeaf

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


            mCondLock = [[ NSConditionLock alloc ] initWithCondition:0 ];
    
            mLockOwner   = (pthread_t) 0;
            mLockCounter = 0;

        }
        
        ~IOObjectPrivate() {

            [ mCondLock release ];
        }
        
	string            mName;
        string            mType;   
        int               mUseCount;  
        NSConditionLock * mCondLock;   
        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 {

        [ mPrivate->mCondLock lock ];
        
        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;
        
        [ mPrivate->mCondLock unlock ];
    }
}

void IOObject::signal()
{ 
    [ mPrivate->mCondLock unlockWithCondition:1 ];
}

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

        [ mPrivate->mCondLock lockWhenCondition:1 ];
        
    }
    else {
    
        NSDate * limit = [ NSDate dateWithTimeIntervalSinceNow:1 ];
        
        [ mPrivate->mCondLock lockWhenCondition:1 beforeDate:limit ];
        
        [ limit release ];
    }
    
    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;
}

