// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
//
// This file is part of GNU uCommon C++.
//
// GNU uCommon C++ is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU uCommon C++ is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with GNU uCommon C++.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Generic templates for C++.  These are templates that do not depend
 * on any ucommon classes.  They can be used for generic C++ programming.
 * @file ucommon/generics.h
 */

#ifndef _UCOMMON_GENERICS_H_
#define _UCOMMON_GENERICS_H_

#ifndef _UCOMMON_CPR_H_
#include <ucommon/cpr.h>
#endif

#include <stdlib.h>
#include <string.h>

#ifdef  NEW_STDLIB
#include <stdexcept>
#endif

#if defined(NEW_STDLIB) || defined(OLD_STDLIB)
#define THROW(x)    throw x
#define THROWS(x)   throw(x)
#define THROWS_ANY  throw()
#else
#define THROW(x)    ::abort()
#define THROWS(x)
#define THROWS_ANY
#endif

NAMESPACE_UCOMMON

/**
 * Generic smart pointer class.  This is the original Common C++ "Pointer"
 * class with a few additions.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
template <typename T>
class pointer
{
protected:
    unsigned *counter;
    T *object;

public:
    inline void release(void) {
        if(counter && --(*counter)==0) {
            delete counter;
            delete object;
        }
        object = NULL;
        counter = NULL;
    }

    inline void retain(void) {
        if(counter)
            ++*counter;
    }

    inline void set(T* ptr) {
        if(object != ptr) {
            release();
            counter = new unsigned;
            *counter = 1;
            object = ptr;
        }
    }

    inline void set(const pointer<T> &ref) {
        if(object == ref.object)
            return;

        if(counter && --(*counter)==0) {
            delete counter;
            delete object;
        }
        object = ref.object;
        counter = ref.counter;
        if(counter)
            ++(*counter);
    }

    inline pointer() {
        counter = NULL;
        object = NULL;
    }

    inline explicit pointer(T* ptr = NULL) : object(ptr) {
        if(object) {
            counter = new unsigned;
            *counter = 1;
        }
        else
            counter = NULL;
    }

    inline pointer(const pointer<T> &ref) {
        object = ref.object;
        counter = ref.counter;
        if(counter)
            ++(*counter);
    }

    inline pointer& operator=(const pointer<T> &ref) {
        this->set(ref);
        return *this;
    }

    inline pointer& operator=(T *ptr) {
        this->set(ptr);
        return *this;
    }

    inline ~pointer()
        {release();}

    inline T& operator*() const
        {return *object;};

    inline T* operator->() const
        {return object;};

    inline bool operator!() const
        {return (counter == NULL);};

    inline operator bool() const
        {return counter != NULL;};
};

/**
 * Generic smart array class.  This is the original Common C++ "Pointer" class
 * with a few additions for arrays.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
template <typename T>
class array_pointer
{
protected:
    unsigned *counter;
    T *array;

public:
    inline void release(void) {
        if(counter && --(*counter)==0) {
            delete counter;
            delete[] array;
        }
        array = NULL;
        counter = NULL;
    }

    inline void retain(void) {
        if(counter)
            ++*counter;
    }

    inline void set(T* ptr) {
        if(array != ptr) {
            release();
            counter = new unsigned;
            *counter = 1;
            array = ptr;
        }
    }

    inline void set(const array_pointer<T> &ref) {
        if(array == ref.array)
            return;

        if(counter && --(*counter)==0) {
            delete counter;
            delete[] array;
        }
        array = ref.array;
        counter = ref.counter;
        if(counter)
            ++(*counter);
    }

    inline array_pointer() {
        counter = NULL;
        array = NULL;
    }

    inline explicit array_pointer(T* ptr = NULL) : array(ptr) {
        if(array) {
            counter = new unsigned;
            *counter = 1;
        }
        else
            counter = NULL;
    }

    inline array_pointer(const array_pointer<T> &ref) {
        array = ref.array;
        counter = ref.counter;
        if(counter)
            ++(*counter);
    }

    inline array_pointer& operator=(const array_pointer<T> &ref) {
        this->set(ref);
        return *this;
    }

    inline array_pointer& operator=(T *ptr) {
        this->set(ptr);
        return *this;
    }

    inline ~array_pointer()
        {release();}

    inline T* operator*() const
        {return array;};

    inline T& operator[](size_t offset) const
        {return array[offset];};

    inline T* operator()(size_t offset) const
        {return &array[offset];};

    inline bool operator!() const
        {return (counter == NULL);};

    inline operator bool() const
        {return counter != NULL;};
};

/**
 * Manage temporary object stored on the heap.  This is used to create a
 * object on the heap who's scope is controlled by the scope of a member
 * function call.  Sometimes we have data types and structures which cannot
 * themselves appear as auto variables.  We may also have a limited stack
 * frame size in a thread context, and yet have a dynamic object that we
 * only want to exist during the life of the method call.  Using temporary
 * allows any type to be created from the heap but have a lifespan of a
 * method's stack frame.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
template <typename T>
class temporary
{
protected:
    T *object;
public:
    /**
     * Construct a temporary object, create our stack frame reference.
     */
    inline temporary()
        {object = NULL;};

    /**
     * Disable copy constructor.
     */
    temporary(const temporary<T>&)
        {::abort();};

    /**
     * Construct an assigned pointer.
     */
    inline temporary(T *ptr)
        {object = ptr;};

    /**
     * Assign a temporary object.  This adds a pointer to an existing
     * type to the current temporary pointer.  If the temporary was
     * already assigned, then it is deleted.
     * @param temp object to assign.
     */
    inline T& operator=(T *temp) {
        if(object)
            delete object;
        object = temp;
        return *this;
    }

    /**
     * Assign a temporary object.  This adds a pointer to an existing
     * type to the current temporary pointer.  If the temporary was
     * already assigned, then it is deleted.
     * @param temp object to assign.
     */
    inline void set(T *temp) {
        if(object)
            delete object;
        object = temp;
    }

    /**
     * Access heap object through our temporary directly.
     * @return reference to heap resident object.
     */
    inline T& operator*() const
        {return *object;};

    /**
     * Access members of our heap object through our temporary.
     * @return member reference of heap object.
     */
    inline T* operator->() const
        {return object;};

    inline operator bool() const
        {return object != NULL;};

    inline bool operator!() const
        {return object == NULL;};

    inline ~temporary() {
        if(object)
            delete object;
        object = NULL;
    }
};

/**
 * Manage temporary array stored on the heap.   This is used to create an
 * array on the heap who's scope is controlled by the scope of a member
 * function call.  Sometimes we have data types and structures which cannot
 * themselves appear as auto variables.  We may also have a limited stack
 * frame size in a thread context, and yet have a dynamic object that we
 * only want to exist during the life of the method call.  Using temporary
 * allows any type to be created from the heap but have a lifespan of a
 * method's stack frame.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
template <typename T>
class temp_array
{
protected:
    T *array;
    size_t size;

public:
    /**
     * Construct a temporary object, create our stack frame reference.
     */
    inline temp_array(size_t s)
        {array =  new T[s]; size = s;};

    /**
     * Construct a temporary object with a copy of some initial value.
     * @param initial object value to use.
     */
    inline temp_array(const T& initial, size_t s) {
        array = new T[s];
        size = s;
        for(size_t p = 0; p < s; ++p)
            array[p] = initial;
    }

    inline void reset(size_t s)
        {delete[] array; array = new T[s]; size = s;};

    inline void reset(const T& initial, size_t s) {
        if(array)
            delete[] array;
        array = new T[s];
        size = s;
        for(size_t p = 0; p < s; ++p)
            array[p] = initial;
    }

    inline void set(const T& initial) {
        for(size_t p = 0; p < size; ++p)
            array[p] = initial;
    }

    /**
     * Disable copy constructor.
     */
    temp_array(const temp_array<T>&)
        {::abort();};

    inline operator bool() const
        {return array != NULL;};

    inline bool operator!() const
        {return array == NULL;};

    inline ~temp_array() {
        if(array)
            delete[] array;
        array = NULL;
        size = 0;
    }

    inline T& operator[](size_t offset) const {
        crit(offset < size, "array out of bound");
        return array[offset];
    }

    inline T* operator()(size_t offset) const {
        crit(offset < size, "array out of bound");
        return &array[offset];
    }
};

/**
 * Save and restore global objects in function call stack frames.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
template<typename T>
class save_restore
{
private:
    T *original;
    T temp;

public:
    /**
     * Save object into local copy and keep reference to the original object.
     * @param object to save.
     */
    inline save_restore(T& object)
        {original = &object; temp = object;};

    /**
     * Restore original when stack frame is released.
     */
    inline ~save_restore()
        {*original = temp;};
};

/**
 * Convenience function to validate object assuming it is castable to bool.
 * @param object we are testing.
 * @return true if object valid.
 */
template<class T>
inline bool is(T& object)
    {return object.operator bool();}

/**
 * Convenience function to test pointer object.  This solves issues where
 * some compilers get confused between bool and pointer operators.
 * @param object we are testing.
 * @return true if object points to NULL.
 */
template<typename T>
inline bool isnull(T& object)
    {return (bool)(object.operator*() == NULL);}

/**
 * Convenience function to test pointer-pointer object.  This solves issues
 * where some compilers get confused between bool and pointer operators.
 * @param object we are testing.
 * @return true if object points to NULL.
 */
template<typename T>
inline bool isnullp(T *object)
    {return (bool)(object->operator*() == NULL);}

/**
 * Convenience function to duplicate object pointer to heap.
 * @param object we are duping.
 * @return heap pointer instance.
 */
template<typename T>
inline T* dup(const T& object)
    {return new T(object);}

template<typename T>
inline void dupfree(T object)
    {delete object;}

template<>
inline char *dup<char>(const char& object)
    {return strdup(&object);}

template<>
inline void dupfree<char*>(char* object)
    {::free(object);}

/**
 * Convenience function to reset an existing object.
 * @param object type to reset.
 */
template<typename T>
inline void reset_unsafe(T& object)
    {new((caddr_t)&object) T;}

/**
 * Convenience function to zero an object and restore type info.
 * @param object to zero in memory.
 */
template<typename T>
inline void zero_unsafe(T& object)
    {memset((void *)&object, 0, sizeof(T)); new((caddr_t)&object) T;}

/**
 * Convenience function to copy class.
 * @param target to copy into.
 * @param source to copy from.
 */
template<typename T>
inline void copy_unsafe(T* target, const T* source)
    {memcpy((void *)target, (void *)source, sizeof(T));}

/**
 * Convenience function to store object pointer into object.
 * @param target to copy into.
 * @param source to copy from.
 */
template<typename T>
inline void store_unsafe(T& target, const T* source)
    {memcpy((void *)&target, (void *)source, sizeof(T));}

/**
 * Convenience function to swap objects.
 * @param o1 to swap.
 * @param o2 to swap.
 */
template<typename T>
inline void swap(T& o1, T& o2)
    {cpr_memswap(&o1, &o2, sizeof(T));}

/**
 * Convenience function to return max of two objects.
 * @param o1 to check.
 * @param o2 to check.
 * @return max object.
 */
template<typename T>
inline T& (max)(T& o1, T& o2)
{
    return o1 > o2 ? o1 : o2;
}

/**
 * Convenience function to return min of two objects.
 * @param o1 to check.
 * @param o2 to check.
 * @return min object.
 */
template<typename T>
inline T& (min)(T& o1, T& o2)
{
    return o1 < o2 ? o1 : o2;
}

/**
 * Convenience macro to range restrict values.
 * @param value to check.
 * @param low value.
 * @param high value.
 * @return adjusted value.
 */
template<typename T>
inline T& (limit)(T& value, T& low, T& high)
{
    return (value < low) ? low : ((value > high) ? high : value);
}

END_NAMESPACE

#endif
