/*

0.0.46

Imebra: a C++ dicom library.
Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008  by Paolo Brandoli

This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 
 as published by the Free Software Foundation.

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 AFFERO GENERAL PUBLIC LICENSE Version 3 for more details.

You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE Version 3
 along with this program; If not, see http://www.gnu.org/licenses/

-------------------

If you want to use Imebra commercially then you have to buy the commercial
 license available at http://puntoexe.com
 
After you buy the commercial license then you can use Imebra according
 to the terms described in the Imebra Commercial License Version 1.
A copy of the Imebra Commercial License Version 1 is available in the 
 documentation pages.

Imebra is available at http://puntoexe.com

The author can be contacted by email at paolo@puntoexe.com or by mail at
 the following address:
 Paolo Brandoli
 Preglov trg 6
 1000 Ljubljana
 Slovenia


*/

/*! \file baseObject.cpp
    \brief Implementation of the baseObject class.

*/

#include "../include/baseObject.h"
#include "../include/exception.h"
#include <iostream>

namespace puntoexe
{

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// basePtr
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Default constructor
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
basePtr::basePtr() : object(0)
{
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Constructor with initialization
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
basePtr::basePtr(baseObject* pObject): object(pObject)
{
	addRef();
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Destructor
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
basePtr::~basePtr()
{
	release();
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Release
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
void basePtr::release()
{
	if(object != 0)
	{
		object->release();
		object = 0;
	}
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Increase reference counter
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
void basePtr::addRef()
{
	if(object != 0)
	{
		object->addRef();
	}
}




///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
//
// baseObject
//
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Default constructor
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
baseObject::baseObject():m_lockCounter(0), m_bValid(true), m_pExceptionsManager(exceptionsManager::getExceptionsManager())
{
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Constructs the object and set an external lock
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
baseObject::baseObject(ptr<baseObject> externalLock): m_externalLock(externalLock),
	m_lockCounter(0), m_bValid(true), m_pExceptionsManager(exceptionsManager::getExceptionsManager())
{
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Constructor with optional increase of the exceptions
//  manager's counter
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
baseObject::baseObject(bool bIncreaseExceptionsManager) : m_lockCounter(0), m_bValid(true)
{
	if(bIncreaseExceptionsManager)
	{
		m_pExceptionsManager = exceptionsManager::getExceptionsManager();
	}
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Return the object used to lock this one
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
baseObject* baseObject::getExternalLock()
{
	if(m_externalLock == 0)
	{
		return this;
	}
	return m_externalLock->getExternalLock();
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Return true if the object is referenced once.
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
bool baseObject::isReferencedOnce()
{
	lockCriticalSection lockThis(&m_criticalSection);
	return m_lockCounter == 1;
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Destructor
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
baseObject::~baseObject()
{
	m_bValid = false;
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Increase the references counter
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
void baseObject::addRef()
{
	if(this == 0)
	{
		return;
	}

	m_criticalSection.lock();
	++m_lockCounter;
	m_criticalSection.unlock();
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Decrease the references counter and delete the object
//  if the counter reaches 0
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
void baseObject::release()
{
	// Calling release on a non-existing object.
	// Simply return
	///////////////////////////////////////////////////////////
	if(this == 0)
	{
		return;
	}

	// Decrease the reference counter
	///////////////////////////////////////////////////////////
	{
		lockCriticalSection lockThis(&m_criticalSection);
		if(--m_lockCounter != 0)
		{
			return;
		}
	}

	if(!preDelete())
	{
		return;
	}

	delete this;
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// This function is called by release() just before
//  the object is deleted.
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
bool baseObject::preDelete()
{
	return true;
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Lock the object
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
void baseObject::lock()
{
	if(this == 0)
	{
		return;
	}
	if(m_externalLock != 0)
	{
		m_externalLock->lock();
		return;
	}
	m_criticalSection.lock();
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Unlock the object
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
void baseObject::unlock()
{
	if(this == 0)
	{
		return;
	}
	if(m_externalLock != 0)
	{
		m_externalLock->unlock();
		return;
	}
	
	m_criticalSection.unlock();
}



///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
//
// lockObject
//
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Lock the specified object
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
lockObject::lockObject(baseObject* pObject)
{
	m_pObject = pObject;
	if(m_pObject != 0)
	{
		m_pObject->lock();
	}
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Unlock the specified object
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
lockObject::~lockObject()
{
	unlock();
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Unlock the specified object
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
void lockObject::unlock()
{
	if( m_pObject != 0)
	{
		m_pObject->unlock();
		m_pObject = 0;
	}
}



///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
//
// lockMultipleObject
//
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Lock the specified objects
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
lockMultipleObjects::lockMultipleObjects(tObjectsList* pObjectsList)
{
	tCriticalSectionsList csList;
	for(tObjectsList::iterator scanObjects = pObjectsList->begin(); scanObjects != pObjectsList->end(); ++scanObjects)
	{
		if((*scanObjects) == 0)
		{
			continue;
		}
		ptr<baseObject> lockObject = (*scanObjects)->getExternalLock();
		csList.push_back(&( (*scanObjects)->m_criticalSection) );
	}
	m_pLockedCS.reset(puntoexe::lockMultipleCriticalSections(&csList));
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Unlock the locked objects
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
lockMultipleObjects::~lockMultipleObjects()
{
	unlock();
}


///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//
//
// Unlock the locked objects
//
//
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
void lockMultipleObjects::unlock()
{
	if(m_pLockedCS.get() == 0)
	{
		return;
	}
	puntoexe::unlockMultipleCriticalSections(m_pLockedCS.release());
}


} // namespace puntoexe
