/* ============================================================
 *
 * This file is a part of digiKam
 *
 * Date        : 2017-08-08
 * Description : Base functions for dnn module, can be used for face recognition, 
 *               all codes are ported from dlib library (http://dlib.net/)
 *
 * Copyright (C) 2006-2016 by Davis E. King <davis at dlib dot net>
 * Copyright (C) 2017      by Yingjie Liu <yingjiewudi at gmail dot com>
 * Copyright (C) 2017-2018 by Gilles Caulier <caulier dot gilles at gmail dot com>
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation;
 * either version 2, or (at your option)
 * any later version.
 *
 * This program 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 General Public License for more details.
 *
 * ============================================================ */

#ifndef DLIB_STD_VECTOr_C_H_
#define DLIB_STD_VECTOr_C_H_

#include <vector>
#include <algorithm>
#include "dnn_assert.h"
#include "serialize.h"
#include "is_kind.h"



template <
    typename T,
    typename Allocator = std::allocator<T>
    >
class std_vector_c : public std::vector<T,Allocator>
{
    typedef typename std::vector<T,Allocator> base_type;
public:
    // types:
    typedef typename Allocator::reference         reference;
    typedef typename Allocator::const_reference   const_reference;
    typedef typename base_type::iterator          iterator;       // See 23.1
    typedef typename base_type::const_iterator    const_iterator; // See 23.1
    typedef typename base_type::size_type         size_type;      // See 23.1
    typedef typename base_type::difference_type   difference_type;// See 23.1
    typedef T                                     value_type;
    typedef Allocator                             allocator_type;
    typedef typename Allocator::pointer           pointer;
    typedef typename Allocator::const_pointer     const_pointer;
    typedef std::reverse_iterator<iterator>       reverse_iterator;
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator;


    // 23.2.4.1 construct/copy/destroy:
    explicit std_vector_c(const Allocator& alloc= Allocator()) : base_type(alloc) {}

    explicit std_vector_c(size_type n, const T& value = T(),
                          const Allocator& alloc= Allocator()) : base_type(n, value, alloc) {}

    template <typename InputIterator>
    std_vector_c(InputIterator first, InputIterator last,
                 const Allocator& alloc= Allocator()) : base_type(first,last,alloc) {}

    std_vector_c(const std::vector<T,Allocator>& x) : base_type(x) {}

    std_vector_c<T,Allocator>& operator=(const std::vector<T,Allocator>& x)
    {
        static_cast<base_type&>(*this) = x;
        return *this;
    }

    template <typename InputIterator>
    void assign(InputIterator first, InputIterator last)    { base_type::assign(first,last); }
    void assign(size_type n, const T& u)                    { base_type::assign(n,u); }
    allocator_type          get_allocator() const           { return base_type::get_allocator(); }
    // iterators:
    iterator                begin()                         { return base_type::begin(); }
    const_iterator          begin() const                   { return base_type::begin(); }
    iterator                end()                           { return base_type::end(); }
    const_iterator          end() const                     { return base_type::end(); }
    reverse_iterator        rbegin()                        { return base_type::rbegin(); }
    const_reverse_iterator  rbegin() const                  { return base_type::rbegin(); }
    reverse_iterator        rend()                          { return base_type::rend(); }
    const_reverse_iterator  rend() const                    { return base_type::rend(); }
    // 23.2.4.2 capacity:
    size_type               size() const                    { return base_type::size(); }
    size_type               max_size() const                { return base_type::max_size(); }
    void                    resize(size_type sz, T c = T()) { base_type::resize(sz,c); }
    size_type               capacity() const                { return base_type::capacity(); }
    bool                    empty() const                   { return base_type::empty(); }
    void                    reserve(size_type n)            { base_type::reserve(n); }

    // element access:
    const_reference         at(size_type n) const           { return base_type::at(n); }
    reference               at(size_type n)                 { return base_type::at(n); }


    // 23.2.4.3 modifiers:
    void     push_back(const T& x) { base_type::push_back(x); }
    void     swap(std_vector_c<T,Allocator>& x) { base_type::swap(x); }
    void     clear() { base_type::clear(); }


// ------------------------------------------------------
// Things that have preconditions that should be checked.
// ------------------------------------------------------

    reference operator[](
        size_type n
    ) 
    { 
        DLIB_CASSERT(n < size(),
            "\treference std_vector_c::operator[](n)"
            << "\n\tYou have supplied an invalid index"
            << "\n\tthis:   " << this
            << "\n\tn:      " << n 
            << "\n\tsize(): " << size()
        );
        return static_cast<base_type&>(*this)[n]; 
    }

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

    const_reference operator[](
        size_type n
    ) const 
    { 
        DLIB_CASSERT(n < size(),
            "\tconst_reference std_vector_c::operator[](n)"
            << "\n\tYou have supplied an invalid index"
            << "\n\tthis:   " << this
            << "\n\tn:      " << n 
            << "\n\tsize(): " << size()
        );
        return static_cast<const base_type&>(*this)[n]; 
    }

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

    reference front(
    ) 
    { 
        DLIB_CASSERT(size() > 0,
            "\treference std_vector_c::front()"
            << "\n\tYou can't call front() on an empty vector"
            << "\n\tthis:   " << this
        );
        return base_type::front(); 
    }

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

    const_reference front(
    ) const 
    {
        DLIB_CASSERT(size() > 0,
            "\tconst_reference std_vector_c::front()"
            << "\n\tYou can't call front() on an empty vector"
            << "\n\tthis:   " << this
        );
        return base_type::front(); 
    }

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

    reference back(
    ) 
    { 
        DLIB_CASSERT(size() > 0,
            "\treference std_vector_c::back()"
            << "\n\tYou can't call back() on an empty vector"
            << "\n\tthis:   " << this
        );
        return base_type::back(); 
    }

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

    const_reference back(
    ) const 
    { 
        DLIB_CASSERT(size() > 0,
            "\tconst_reference std_vector_c::back()"
            << "\n\tYou can't call back() on an empty vector"
            << "\n\tthis:   " << this
        );
        return base_type::back(); 
    }

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

    void pop_back(
    ) 
    { 
        DLIB_CASSERT(size() > 0,
            "\tconst_reference std_vector_c::pop_back()"
            << "\n\tYou can't call pop_back() on an empty vector"
            << "\n\tthis:   " << this
        );
        base_type::pop_back(); 
    }

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

    iterator insert(
        iterator position, 
        const T& x
    ) 
    { 
        DLIB_CASSERT( begin() <= position && position <= end(), 
            "\titerator std_vector_c::insert(position,x)"
            << "\n\tYou have called insert() with an invalid position"
            << "\n\tthis:   " << this
        );
        return base_type::insert(position, x); 
    }

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

    void insert(
        iterator position, 
        size_type n, 
        const T& x
    ) 
    { 
        DLIB_CASSERT( begin() <= position && position <= end(), 
            "\tvoid std_vector_c::insert(position,n,x)"
            << "\n\tYou have called insert() with an invalid position"
            << "\n\tthis:   " << this
        );
        base_type::insert(position, n, x); 
    }

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

    template <typename InputIterator>
    void insert(
        iterator position,
        InputIterator first, 
        InputIterator last
    ) 
    { 
        DLIB_CASSERT( begin() <= position && position <= end(), 
            "\tvoid std_vector_c::insert(position,first,last)"
            << "\n\tYou have called insert() with an invalid position"
            << "\n\tthis:   " << this
        );
        base_type::insert(position, first, last); 
    }

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

    iterator erase(
        iterator position
    ) 
    { 
        DLIB_CASSERT( begin() <= position && position < end(), 
            "\titerator std_vector_c::erase(position)"
            << "\n\tYou have called erase() with an invalid position"
            << "\n\tthis:   " << this
        );
        return base_type::erase(position); 
    }

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

    iterator erase(
        iterator first, 
        iterator last
    ) 
    { 
        DLIB_CASSERT( begin() <= first && first <= last && last <= end(),
            "\titerator std_vector_c::erase(first,last)"
            << "\n\tYou have called erase() with an invalid range of iterators"
            << "\n\tthis:   " << this
        );
        return base_type::erase(first,last); 
    }

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


};

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

// Add these swaps just to make absolutely sure the specialized swap always gets called even
// if the compiler is crappy and would otherwise mess it up.
template <typename T, typename Allocator>
void swap(std_vector_c<T,Allocator>& x, std_vector_c<T,Allocator>& y) { x.swap(y); }

template <typename T, typename Allocator>
void swap(std::vector<T,Allocator>& x, std_vector_c<T,Allocator>& y) { x.swap(y); }

template <typename T, typename Allocator>
void swap(std_vector_c<T,Allocator>& x, std::vector<T,Allocator>& y) { y.swap(x); }

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

template <typename T, typename alloc>
void serialize (
    const std_vector_c<T,alloc>& item,
    std::ostream& out
)
{
    try
    { 
        const unsigned long size = static_cast<unsigned long>(item.size());

        serialize(size,out); 
        for (unsigned long i = 0; i < item.size(); ++i)
            serialize(item[i],out);
    }
    catch (serialization_error& e)
    { throw serialization_error(e.info + "\n   while serializing object of type std_vector_c"); }
}

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

template <typename T, typename alloc>
void deserialize (
    std_vector_c<T, alloc>& item,
    std::istream& in
)
{
    try 
    { 
        unsigned long size;
        deserialize(size,in); 
        item.resize(size);
        for (unsigned long i = 0; i < size; ++i)
            deserialize(item[i],in);
    }
    catch (serialization_error& e)
    { throw serialization_error(e.info + "\n   while deserializing object of type std_vector_c"); }
}

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

template <typename T, typename alloc> 
struct is_std_vector<std_vector_c<T,alloc> >        { const static bool value = true; };

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



#endif // DLIB_STD_VECTOr_C_H_

