// Copyright (C) 2007  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_SCOPED_PTr_
#define DLIB_SCOPED_PTr_ 

#include <algorithm>
#include "../noncopyable.h"
#include "../algs.h"
#include "scoped_ptr_abstract.h"

namespace dlib
{

// ----------------------------------------------------------------------------------------

    template <typename T>
    struct default_deleter
    {
        void operator() (T* item) const
        {
            delete item;
        }
    };

    template <typename T>
    struct default_deleter<T[]>
    {
        void operator() (T* item) const
        {
            delete [] item;
        }
    };

// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename deleter = default_deleter<T>
        > 
    class scoped_ptr : noncopyable 
    {
        /*!
            CONVENTION
                - get() == ptr
        !*/

    public:
        typedef T element_type;
        typedef deleter deleter_type;

        explicit scoped_ptr (
            T* p = 0
        ) : ptr(p) { }

        ~scoped_ptr() 
        { 
            if (ptr) 
            {
                deleter del;
                del(ptr); 
            }
        }

        void reset (
            T* p = 0
        ) 
        { 
            if (ptr) 
            {
                deleter del;
                del(ptr); 
            }

            ptr = p;
        }

        T& operator*() const
        {
            DLIB_ASSERT(get() != 0,
                        "\tscoped_ptr::operator*()"
                        << "\n\tget() can't be null if you are going to dereference it"
                        << "\n\tthis: " << this
            );

            return *ptr;
        }

        T* operator->() const
        {
            DLIB_ASSERT(get() != 0,
                        "\tscoped_ptr::operator*()"
                        << "\n\tget() can't be null"
                        << "\n\tthis: " << this
            );

            return ptr;
        }

        T* get() const
        {
            return ptr;
        }

        operator bool() const
        {
            return (ptr != 0);
        }

        void swap(
            scoped_ptr& b
        )
        {
            std::swap(ptr,b.ptr);
        }

    private:

        T* ptr;
    };

// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename deleter 
        > 
    class scoped_ptr<T[],deleter> : noncopyable 
    {
        /*!
            CONVENTION
                - get() == ptr
        !*/

    public:
        typedef T element_type;

        explicit scoped_ptr (
            T* p = 0
        ) : ptr(p) { }

        ~scoped_ptr() 
        { 
            if (ptr) 
            {
                deleter del;
                del(ptr); 
            }
        }

        void reset (
            T* p = 0
        ) 
        { 
            if (ptr) 
            {
                deleter del;
                del(ptr); 
            }
            ptr = p;
        }

        T& operator[] (
            unsigned long idx
        ) const
        {
            DLIB_ASSERT(get() != 0,
                        "\tscoped_ptr::operator[]()"
                        << "\n\tget() can't be null if you are going to dereference it"
                        << "\n\tthis: " << this
            );

            return ptr[idx];
        }

        T* get() const
        {
            return ptr;
        }

        operator bool() const
        {
            return (ptr != 0);
        }

        void swap(
            scoped_ptr& b
        )
        {
            std::swap(ptr,b.ptr);
        }

    private:

        T* ptr;
    };

// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename deleter
        > 
    void swap(
        scoped_ptr<T,deleter>& a, 
        scoped_ptr<T,deleter>& b
    )
    {
        a.swap(b);
    }

// ----------------------------------------------------------------------------------------

}

#endif // DLIB_SCOPED_PTr_