/*
 * 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 _LOG4CXX_HELPERS_OBJECT_PTR_H
#define _LOG4CXX_HELPERS_OBJECT_PTR_H

#include <log4cxx/log4cxx.h>

//
//   Helgrind (race detection tool for Valgrind) will complain if pointer
//   is not initialized in an atomic operation.  Static analysis tools
//   (gcc's -Weffc++, for example) will complain if pointer is not initialized
//   in member initialization list.  The use of a macro allows quick
//   switching between the initialization styles.
//
#if LOG4CXX_HELGRIND
	#define _LOG4CXX_OBJECTPTR_INIT(x) : ObjectPtrBase() { exchange(x);
#else
	#define _LOG4CXX_OBJECTPTR_INIT(x) : ObjectPtrBase(), p(x) {
#endif

namespace log4cxx
{
namespace helpers
{
class Class;

class LOG4CXX_EXPORT ObjectPtrBase
{
	public:
		ObjectPtrBase();
		virtual ~ObjectPtrBase();
		static void checkNull(const int& null);
		static void* exchange(void** destination, void* newValue);
		virtual void* cast(const Class& cls) const = 0;
};


/** smart pointer to a Object descendant */
template<typename T> class ObjectPtrT : public ObjectPtrBase
{
	public:
		ObjectPtrT(const int& null)
		_LOG4CXX_OBJECTPTR_INIT(0)
		ObjectPtrBase::checkNull(null);
}

ObjectPtrT()
_LOG4CXX_OBJECTPTR_INIT(0)
}

ObjectPtrT(T* p1)
_LOG4CXX_OBJECTPTR_INIT(p1)

if (this->p != 0)
{
	this->p->addRef();
}
}


ObjectPtrT(const ObjectPtrT& p1)
_LOG4CXX_OBJECTPTR_INIT(p1.p)

if (this->p != 0)
{
	this->p->addRef();
}
}

ObjectPtrT(const ObjectPtrBase& p1)
_LOG4CXX_OBJECTPTR_INIT(reinterpret_cast<T*>(p1.cast(T::getStaticClass())))

if (this->p != 0)
{
	this->p->addRef();
}
}

ObjectPtrT(ObjectPtrBase& p1)
_LOG4CXX_OBJECTPTR_INIT(reinterpret_cast<T*>(p1.cast(T::getStaticClass())))

if (this->p != 0)
{
	this->p->addRef();
}
}


~ObjectPtrT()
{
	if (p != 0)
	{
		p->releaseRef();
	}
}

ObjectPtrT& operator=(const ObjectPtrT& p1)
{
	T* newPtr = p1.p;

	if (newPtr != 0)
	{
		newPtr->addRef();
	}

	T* oldPtr = exchange(newPtr);

	if (oldPtr != 0)
	{
		oldPtr->releaseRef();
	}

	return *this;
}

ObjectPtrT& operator=(const int& null)   //throw(IllegalArgumentException)
{
	//
	//   throws IllegalArgumentException if null != 0
	//
	ObjectPtrBase::checkNull(null);
	T* oldPtr = exchange(0);

	if (oldPtr != 0)
	{
		oldPtr->releaseRef();
	}

	return *this;
}

ObjectPtrT& operator=(T* p1)
{
	if (p1 != 0)
	{
		p1->addRef();
	}

	T* oldPtr = exchange(p1);

	if (oldPtr != 0)
	{
		oldPtr->releaseRef();
	}

	return *this;
}


ObjectPtrT& operator=(ObjectPtrBase& p1)
{
	T* newPtr = reinterpret_cast<T*>(p1.cast(T::getStaticClass()));
	return operator=(newPtr);
}

ObjectPtrT& operator=(const ObjectPtrBase& p1)
{
	T* newPtr = reinterpret_cast<T*>(p1.cast(T::getStaticClass()));
	return operator=(newPtr);
}

bool operator==(const ObjectPtrT& p1) const
{
	return (this->p == p1.p);
}
bool operator!=(const ObjectPtrT& p1) const
{
	return (this->p != p1.p);
}
bool operator<(const ObjectPtrT& p1) const
{
	return (this->p < p1.p);
}
bool operator==(const T* p1) const
{
	return (this->p == p1);
}
bool operator!=(const T* p1) const
{
	return (this->p != p1);
}
bool operator<(const T* p1) const
{
	return (this->p < p1);
}
T* operator->() const
{
	return p;
}
T& operator*() const
{
	return *p;
}
operator T* () const
{
	return p;
}



private:
T* p;
virtual void* cast(const Class& cls) const
{
	if (p != 0)
	{
		return const_cast<void*>(p->cast(cls));
	}

	return 0;
}
T* exchange(const T* newValue)
{
	// Avoid GCC strict aliasing warnings
	union
	{
		T** in;
		void** out;
	} temp = { &p };
	return static_cast<T*>(ObjectPtrBase::exchange(
				temp.out,
				const_cast<T*>(newValue)));
}

};


}
}

#endif //_LOG4CXX_HELPERS_OBJECT_PTR_H
