/*
 * 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
