/*
    author: Martin Robinson
    title:  Smart Pointer
    about:  following Modern C++ book, implementing
            linked list smart pointer

*/

#pragma once
#ifndef _SMARTPTR
#define _SMARTPTR

#include "autoPointer.hpp"

/*
class
*/

template <typename T>
class smartPtr{
    T* _pointee;
    smartPtr<T> * _back,
                * _next;
    //private method
    bool _wasTheLast();
    void _joinThePool(smartPtr<T>&);
public:
    //constructor
    smartPtr();
    explicit smartPtr(T*);
    //copy
    smartPtr(const smartPtr<T>&);
    smartPtr(smartPtr<T>&);
    smartPtr(autoPtr<T>&);
    smartPtr<T>& operator=(smartPtr<T>&);
    smartPtr<T>& operator=(autoPtr<T>&);
    //destructor
    ~smartPtr();
    //operator
    T& operator*()const;
    T* operator->()const;
    //comparator
    operator bool()const;
};

/*
method
*/

//private
//remove from pool and test if it was the last one
template <typename T>
bool smartPtr<T>::_wasTheLast()
{
    if(_back)
    {
        if(_next)
        {
            _back->_next = _next;
            _next->_back = _back;
        }
        else
            _back->_next = 0;
    }
    else if(_next)
        _next->_back = 0;
    else
        return true;
    return false;
}

//private
//join the pool of smart pointer that point the same pointee
template <typename T>
void smartPtr<T>::_joinThePool(smartPtr<T> & sp)
{
    _back = sp._back;
    _next = &sp;
    if (_back)
        _back->_next = this;
    _next->_back = this;
}

//constructor
template <typename T>
smartPtr<T>::smartPtr()
{
    _pointee = 0;
    _back = _next = 0;
}

template <typename T>
smartPtr<T>::smartPtr(T * pointee)
{
    _pointee = pointee;
    _back = _next = 0;
}

//copy
template <typename T>
smartPtr<T>::smartPtr(const smartPtr<T> & sp)
{
    /*this weird one dummy is needed
    this make the non const one used 
    in return by value. strange behavior
    of the default copy method
    */
}

template <typename T>
smartPtr<T>::smartPtr(smartPtr<T> & sp)
{
    _pointee = sp._pointee;
    _joinThePool(sp);
}

template <typename T>
smartPtr<T>::smartPtr(autoPtr<T> & sp)
{
    _pointee = sp._pointee;
    sp._pointee = 0;        //take ownership
}

template <typename T>
smartPtr<T>& smartPtr<T>::operator=(smartPtr<T>& sp)
{
    if (this != &sp)
    {
        if (_pointee && _wasTheLast())
            delete _pointee;
        _pointee = sp._pointee;
        _joinThePool(sp);
    }
    return *this;
}

template <typename T>
smartPtr<T>& smartPtr<T>::operator=(autoPtr<T>& sp)
{
    if(this != &sp)
    {
        if(_pointee && _wasTheLast())
            delete _pointee;
        _pointee = sp._pointee;
        sp._pointee=0;
    }
    return *this;
}

//destructor
template <typename T>
smartPtr<T>::~smartPtr()
{
    if(_wasTheLast())
        delete _pointee;
}

//operator act as a pointer
template <typename T>
T& smartPtr<T>::operator*()const
{
    return *_pointee;
}

template <typename T>
T* smartPtr<T>::operator->()const
{
    return _pointee;
}

//operator comparator
template <typename T>
smartPtr<T>::operator bool()const
{
    return _pointee!=0;
}


#endif
