// 
// This file is part of MolMeccano, a cross-platform C++ chemical library.
//
// Copyright (C) 2001-2007 Alexey Nikitin
//
#ifndef OWN_H
#define OWN_H

/** \brief
It's not possible to create owns dynamically.
Owns can not hold arrays.

ERROR:

Own <A> *j = new Own <A>;

Own <A> j (new A[100]);

####################################
Own assignment:

Own <A> j;
j = Own <A> (new A( arg1, arg2));
j = own (new A (arg1, arg2));
*/

#ifndef FLAW_H
#include "Flaw.h"
#endif

#ifndef REF_H
#include "Ref.h"
#endif

#include <typeinfo>

namespace MM
{

template <class T>
class Own
{
    T                   *p_;

public:

    typedef T element_type;

                        Own ()       {p_ = 0;}
                       ~Own ()       {delete p_; p_ = 0;}
    explicit            Own (T * p) : p_(p) {invariant();}
                        Own (const Own<T> & p)
                        : 
                            p_(const_cast<Own &>(p).orphan())  {invariant();}

    /**
     Allow accept derived classes.
     */
    template <class Y>  Own( Own<Y> & p)
                        : p_(const_cast<Own<Y> &>(p).orphan()) {invariant();}

    const Own<T> & operator = (const Own< T > & p)
    {
        if (this != &p)
        {
            delete p_;
            T * tmp = const_cast <Own &> (p).orphan();

            if (p_ == tmp)
            {
                tmp = 0;
                p_ = 0;
                FLAW ("Two different janitors holds the same object");
            }
            
            p_ = tmp;
        }
        invariant();
        return *this;
    }

    T & operator () ()
    { 
        #ifndef NDEBUG
        invariant(); 
        #endif

        return *p_; 
    }

    const T & operator () () const
    { 
        #ifndef NDEBUG
        invariant(); 
        #endif

        return *p_; 
    }

    Ref <T> ref()
    { 
        return Ref<T> (*p_);
    }

    const Ref<T> ref() const
    { 
        return Ref<T> (*p_);
    }

    T * adopt (T * adoptee)
    {
        delete p_;
        p_ = adoptee;
        return p_;
    }

    T * orphan ()
    {
        invariant();
        T *tmp = p_;
        p_ = 0;
        return  tmp;
    }

    bool was_initialized    () const {return p_ != 0;}
    bool was_not_initialized() const {return p_ == 0;}
    bool is_valid           () const {return p_ != 0;}
    bool is_not_valid       () const {return p_ == 0;}

   /* bool operator == ( const T & item ) //fix test
    {
        return item == *p_;
    }

    bool operator != ( const T & item ) //fix test
    {
        return item != *p_;
    }//*/


private:
    void invariant() const
    {
        if (p_ == 0)
            //FLAW ("Class Own invariant violation: p_ == 0");
            FLAW (Text (typeid(*this).name()) + " invariant violation: not initialized.");
    }
};

template <class T>
inline Own<T> own (T* obj)
{
    return Own<T> (obj);
}

}//MM

#endif//JANITOR_H
