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

#include "../interfaces/enumerable.h"
#include "../serialize.h"
#include "../algs.h"

namespace dlib
{

    template <
        typename T,
        typename mem_manager = default_memory_manager 
        >
    class array : public enumerable<T>
    {

        /*!
            REQUIREMENTS ON T
                T must have a default constructor.

            REQUIREMENTS ON mem_manager
                must be an implementation of memory_manager/memory_manager_kernel_abstract.h or
                must be an implementation of memory_manager_global/memory_manager_global_kernel_abstract.h or
                must be an implementation of memory_manager_stateless/memory_manager_stateless_kernel_abstract.h 
                mem_manager::type can be set to anything.

            POINTERS AND REFERENCES TO INTERNAL DATA
                front(), back(), swap(), max_size(), set_size(), and operator[] 
                functions do not invalidate pointers or references to internal data.
                All other functions have no such guarantee.

            INITIAL VALUE
                size() == 0    
                max_size() == 0

            ENUMERATION ORDER
                The enumerator will iterate over the elements of the array in the
                order (*this)[0], (*this)[1], (*this)[2], ...

            WHAT THIS OBJECT REPRESENTS
                This object represents an ordered 1-dimensional array of items, 
                each item is associated with an integer value.  The items are 
                numbered from 0 though size() - 1 and the operator[] functions 
                run in constant time.  

                Also note that unless specified otherwise, no member functions
                of this object throw exceptions.
        !*/
        
        public:

            typedef T type;
            typedef T value_type;
            typedef mem_manager mem_manager_type;

            array (
            );
            /*!
                ensures 
                    - #*this is properly initialized
                throws
                    - std::bad_alloc or any exception thrown by T's constructor
            !*/

            explicit array (
                size_t new_size
            );
            /*!
                ensures 
                    - #*this is properly initialized
                    - #size() == new_size
                    - #max_size() == new_size
                    - All elements of the array will have initial values for their type.
                throws
                    - std::bad_alloc or any exception thrown by T's constructor
            !*/

            ~array (
            ); 
            /*!
                ensures
                    - all memory associated with *this has been released
            !*/

            array(
                array&& item
            );
            /*!
                ensures
                    - move constructs *this from item.  Therefore, the state of item is
                      moved into *this and #item has a valid but unspecified state.
            !*/

            array& operator=(
                array&& item
            );
            /*!
                ensures
                    - move assigns *this from item.  Therefore, the state of item is
                      moved into *this and #item has a valid but unspecified state.
                    - returns a reference to #*this
            !*/

            void clear (
            );
            /*!
                ensures
                    - #*this has its initial value
                throws
                    - std::bad_alloc or any exception thrown by T's constructor
                        if this exception is thrown then the array object is unusable 
                        until clear() is called and succeeds
            !*/

            const T& operator[] (
                size_t pos
            ) const;
            /*!
                requires
                    - pos < size()
                ensures
                    - returns a const reference to the element at position pos
            !*/
            
            T& operator[] (
                size_t pos
            );
            /*!
                requires
                    - pos < size()
                ensures
                    - returns a non-const reference to the element at position pos
            !*/

            void set_size (
                size_t size
            );
            /*!
                requires
                    - size <= max_size()
                ensures
                    - #size() == size
                    - any element with index between 0 and size - 1 which was in the 
                      array before the call to set_size() retains its value and index.
                      All other elements have undetermined (but valid for their type) 
                      values.  (e.g. this object might buffer old T objects and reuse 
                      them without reinitializing them between calls to set_size())
                    - #at_start() == true
                throws
                    - std::bad_alloc or any exception thrown by T's constructor
                        may throw this exception if there is not enough memory and 
                        if it does throw then the call to set_size() has no effect    
            !*/

            size_t max_size(
            ) const;
            /*!
                ensures
                    - returns the maximum size of *this
            !*/

            void set_max_size(
                size_t max
            );
            /*!
                ensures
                    - #max_size() == max
                    - #size() == 0
                    - #at_start() == true
                throws
                    - std::bad_alloc or any exception thrown by T's constructor
                        may throw this exception if there is not enough 
                        memory and if it does throw then max_size() == 0    
            !*/

            void swap (
                array<T>& item
            );
            /*!
                ensures
                    - swaps *this and item
            !*/ 
            
            void sort (
            );
            /*!
                requires
                    - T must be a type with that is comparable via operator<
                ensures
                    - for all elements in #*this the ith element is <= the i+1 element
                    - #at_start() == true
                throws
                    - std::bad_alloc or any exception thrown by T's constructor
                        data may be lost if sort() throws
            !*/

            void resize (
                size_t new_size
            );
            /*!
                ensures
                    - #size() == new_size
                    - #max_size() == max(new_size,max_size())
                    - for all i < size() && i < new_size:
                        - #(*this)[i] == (*this)[i]
                          (i.e. All the original elements of *this which were at index
                          values less than new_size are unmodified.)
                    - for all valid i >= size():
                        - #(*this)[i] has an undefined value
                          (i.e. any new elements of the array have an undefined value)
                throws
                    - std::bad_alloc or any exception thrown by T's constructor.
                       If an exception is thrown then it has no effect on *this.
            !*/

            
            const T& back (
            ) const;
            /*!
                requires
                    - size() != 0
                ensures
                    - returns a const reference to (*this)[size()-1]
            !*/

            T& back (
            );
            /*!
                requires
                    - size() != 0
                ensures
                    - returns a non-const reference to (*this)[size()-1]
            !*/

            void pop_back (
                T& item
            );
            /*!
                requires
                    - size() != 0
                ensures
                    - #size() == size() - 1
                    - swaps (*this)[size()-1] into item
                    - All elements with an index less than size()-1 are 
                      unmodified by this operation.
            !*/

            void pop_back (
            );
            /*!
                requires
                    - size() != 0
                ensures
                    - #size() == size() - 1
                    - All elements with an index less than size()-1 are 
                      unmodified by this operation.
            !*/

            void push_back (
                T& item
            );
            /*!
                ensures
                    - #size() == size()+1
                    - swaps item into (*this)[#size()-1] 
                    - #back() == item
                    - #item has some undefined value (whatever happens to 
                      get swapped out of the array)
                throws
                    - std::bad_alloc or any exception thrown by T's constructor.
                       If an exception is thrown then it has no effect on *this.
            !*/

            void push_back (T&& item) { push_back(item); }
            /*!
                enable push_back from rvalues 
            !*/

            typedef T* iterator;
            typedef const T* const_iterator;

            iterator begin(
            );
            /*!
                ensures
                    - returns an iterator that points to the first element in this array or
                      end() if the array is empty.
            !*/

            const_iterator begin(
            ) const;
            /*!
                ensures
                    - returns a const iterator that points to the first element in this
                      array or end() if the array is empty.
            !*/

            iterator end(
            );
            /*!
                ensures
                    - returns an iterator that points to one past the end of the array.
            !*/

            const_iterator end(
            ) const;
            /*!
                ensures
                    - returns a const iterator that points to one past the end of the
                      array.
            !*/

        private:

            // restricted functions
            array(array<T>&);        // copy constructor
            array<T>& operator=(array<T>&);    // assignment operator        

    };

    template <
        typename T
        >
    inline void swap (
        array<T>& a, 
        array<T>& b 
    ) { a.swap(b); }
    /*!
        provides a global swap function
    !*/

    template <
        typename T
        >
    void serialize (
        const array<T>& item, 
        std::ostream& out 
    );   
    /*!
        provides serialization support 
    !*/

    template <
        typename T 
        >
    void deserialize (
        array<T>& item, 
        std::istream& in
    );   
    /*!
        provides deserialization support 
    !*/

}

#endif // DLIB_ARRAY_KERNEl_ABSTRACT_

