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

#include "reference_counter_kernel_abstract.h"
#include "../algs.h"

namespace dlib
{

    template <
        typename T,
        typename copy = copy_functor<T>
        >
    class reference_counter_kernel_1 
    {

        /*!
                INITIAL VALUE
                    *data = item of type T with its initial value
                    *count = 1

                CONVENTION
                    *data = pointer to item of type T
                    *count = number of references to *data

                    if clear() threw an exception then count = 0 and data is not a 
                    valid pointer
        !*/

        public:

            typedef T type;


            reference_counter_kernel_1 (
            );

            inline reference_counter_kernel_1 ( 
                const reference_counter_kernel_1& item
            );

            virtual ~reference_counter_kernel_1 (
            ); 

            void clear (
            );

            T& modify (
            );

            inline const T& access (
            ) const;

            inline reference_counter_kernel_1& operator= (
                const reference_counter_kernel_1& rhs
            );

            inline void swap (
                reference_counter_kernel_1& item
            );


        private:

            T* data;
            unsigned long* count;
            mutable copy copy_item;
    };

    template <
        typename T,
        typename copy
        >
    inline void swap (
        reference_counter_kernel_1<T,copy>& a, 
        reference_counter_kernel_1<T,copy>& b 
    ) { a.swap(b); }   

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // member function definitions
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename copy
        >
    reference_counter_kernel_1<T,copy>::
    reference_counter_kernel_1 (
    ) 
    {
        data = new T;
        try { count = new unsigned long; }
        catch (...) { delete data; throw; }

        *count = 1;
    }
    
// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename copy
        >
    reference_counter_kernel_1<T,copy>::
    reference_counter_kernel_1 ( 
        const reference_counter_kernel_1<T,copy>& item
    ) : 
        data(item.data),
        count(item.count)
    {
        ++(*count);
    }
    
// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename copy
        >
    reference_counter_kernel_1<T,copy>::
    ~reference_counter_kernel_1 (
    )
    {
        if (*count > 1)
        {
            // if there are other references to this data
            --(*count);
        }
        else
        {
            // if there are no other references to this data
            delete count;
            delete data;
        }
    }
    
// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename copy
        >
    void reference_counter_kernel_1<T,copy>::
    clear (
    )
    {
        // if an exception was thrown last time clear() was called then do this
        if (count == 0)
        {
            data = new T;
            try { count = new unsigned long; }
            catch (...) { delete data; throw; }

            *count = 1;            
        }
        // if there are other references to the data then do this
        else if (*count > 1)
        {
            --(*count);

            try { data = new T; }               
            catch (...) { count = 0; throw; }

            try { count = new unsigned long; }  
            catch (...) { delete data; count = 0; throw; }

            *count = 1;

        }
        else
        {
            // if there are no other references to this data
            *count = 1;
            delete data;
            try { data = new T; } catch (...) { delete count; count = 0; throw; }
        }
    }
    
// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename copy
        >
    T& reference_counter_kernel_1<T,copy>::
    modify (
    )
    {
        // if this is not the only reference then make a new copy
        if ( *count > 1 )
        {
            T&              old_data    = *data;
            unsigned long&  old_count   = *count;


            // get memory for the new copy
            try { data = new T; }               
            catch (...) { data = &old_data; throw; }

            try { count = new unsigned long; }  
            catch (...) {delete data; data = &old_data; count = &old_count; throw;}

            // decrement the number of references to old_data
            --(old_count);

            *count = 1;

            // make a copy of the old data
            try { copy_item(old_data,*data); }  
            catch (...) 
            { delete data; delete count; data = &old_data; count = &old_count; }

        }

        return *data;

    }
    
// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename copy
        >
    const T& reference_counter_kernel_1<T,copy>::
    access (
    ) const
    {
        return *data;
    }
    
// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename copy
        >
    reference_counter_kernel_1<T,copy>& reference_counter_kernel_1<T,copy>::
    operator= (
        const reference_counter_kernel_1<T,copy>& rhs
    )
    {
        if (this == &rhs)
            return *this;

        // delete the current data if this is the last reference to it
        if (*count > 1)
        {
            // if there are other references to this data
            --(*count);
        }
        else
        {
            // if there are no other references to this data
            delete count;
            delete data;
        }        

        // copy the pointers
        count = (rhs.count);
        data = (rhs.data);
        ++(*count);

        return *this;

    }

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

    template <
        typename T,
        typename copy
        >
    void reference_counter_kernel_1<T,copy>::
    swap (
        reference_counter_kernel_1<T,copy>& item
    )
    {
        T* data_temp                = data;
        unsigned long* count_temp   = count;

        data    = item.data;
        count   = item.count;

        item.data   = data_temp;
        item.count  = count_temp;
    }

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

}

#endif // DLIB_REFERENCE_COUNTER_KERNEl_1_