/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



#ifndef _RTL_REF_HXX_
#define _RTL_REF_HXX_

#include <sal/types.h>
#include <osl/diagnose.h>
#include <osl/interlck.h>

namespace rtl
{

/** Interface for a reference type.
*/
class IReference
{
public:
	/** @see osl_incrementInterlockedCount.
	 */
	virtual oslInterlockedCount SAL_CALL acquire() = 0;

	/** @see osl_decrementInterlockedCount.
	 */
	virtual oslInterlockedCount SAL_CALL release() = 0;
};


/** Template reference class for reference type derived from IReference.
*/
template <class reference_type>
class Reference
{
	/** The <b>reference_type</b> body pointer.
	 */
	reference_type * m_pBody;


public:
	/** Constructor...
	 */
	inline Reference()
		: m_pBody (0)
	{}


	/** Constructor...
	 */
	inline Reference (reference_type * pBody)
		: m_pBody (pBody)
	{
		if (m_pBody)
			m_pBody->acquire();
	}
	

	/** Copy constructor...
	 */
	inline Reference (const Reference<reference_type> & handle)
		: m_pBody (handle.m_pBody)
	{
		if (m_pBody)
			m_pBody->acquire();
	}


	/** Destructor...
	 */
	inline ~Reference()
	{
		if (m_pBody)
			m_pBody->release();
	}

	/** Set...
	 	Similar to assignment.
	 */
	inline Reference<reference_type> &
	SAL_CALL set (reference_type * pBody)
	{
		if (pBody)
			pBody->acquire();
        reference_type * const pOld = m_pBody;
		m_pBody = pBody;
		if (pOld)
			pOld->release();
		return *this;
	}

	/** Assignment.
	 	Unbinds this instance from its body (if bound) and
	 	bind it to the body represented by the handle.
	 */
	inline Reference<reference_type> &
	SAL_CALL operator= (const Reference<reference_type> & handle)
	{
        return set( handle.m_pBody );
	}

	/** Assignment...
	 */
	inline Reference<reference_type> &
	SAL_CALL operator= (reference_type * pBody)
	{
        return set( pBody );
	}

	/** Unbind the body from this handle.
	 	Note that for a handle representing a large body,
	 	"handle.clear().set(new body());" _might_
	 	perform a little bit better than "handle.set(new body());",
	 	since in the second case two large objects exist in memory
	 	(the old body and the new body).
	 */
	inline Reference<reference_type> & SAL_CALL clear()
	{
		if (m_pBody)
		{
            reference_type * const pOld = m_pBody;
			m_pBody = 0;
			pOld->release();
		}
		return *this;
	}


	/** Get the body. Can be used instead of operator->().
	 	I.e. handle->someBodyOp() and handle.get()->someBodyOp()
	 	are the same.
	 */
	inline reference_type * SAL_CALL get() const
	{
		return m_pBody;
	}


	/** Probably most common used: handle->someBodyOp().
	 */
	inline reference_type * SAL_CALL operator->() const
	{
		OSL_PRECOND(m_pBody, "Reference::operator->() : null body");
		return m_pBody;
	}


	/** Allows (*handle).someBodyOp().
	*/
	inline reference_type & SAL_CALL operator*() const
	{
		OSL_PRECOND(m_pBody, "Reference::operator*() : null body");
		return *m_pBody;
	}


	/** Returns True if the handle does point to a valid body.
	 */
	inline sal_Bool SAL_CALL is() const
	{
		return (m_pBody != 0);
	}


	/** Returns True if this points to pBody.
	 */
	inline sal_Bool SAL_CALL operator== (const reference_type * pBody) const
	{
		return (m_pBody == pBody);
	}


	/** Returns True if handle points to the same body.
	 */
	inline sal_Bool
	SAL_CALL operator== (const Reference<reference_type> & handle) const
	{
		return (m_pBody == handle.m_pBody);
	}


	/** Needed to place References into STL collection.
	 */
	inline sal_Bool
	SAL_CALL operator!= (const Reference<reference_type> & handle) const
	{
		return (m_pBody != handle.m_pBody);
	}


	/** Needed to place References into STL collection.
	 */
	inline sal_Bool
	SAL_CALL operator< (const Reference<reference_type> & handle) const
	{
		return (m_pBody < handle.m_pBody);
	}


	/** Needed to place References into STL collection.
	 */
	inline sal_Bool
	SAL_CALL operator> (const Reference<reference_type> & handle) const
	{
		return (m_pBody > handle.m_pBody);
	}
};

/** @internal
    Enables boost::mem_fn and boost::bind to recognize Reference.
*/
template <typename T>
inline T * get_pointer( Reference<T> const& r )
{
    return r.get();
}

} // namespace rtl

#endif /* !_RTL_REF_HXX_ */
