/**
 * @file
 *
 * @copyright
 * @verbatim
   Copyright @ 2017 Audi Electronics Venture GmbH. All rights reserved.

       This Source Code Form is subject to the terms of the Mozilla
       Public License, v. 2.0. If a copy of the MPL was not distributed
       with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

   If it is not possible or desirable to put the notice in a particular file, then
   You may include the notice in a location (such as a LICENSE file in a
   relevant directory) where a recipient would be likely to look for such a notice.

   You may add additional accurate notices of copyright ownership.
   @endverbatim
*/

#include "ddlcontainer.h"
#include "ddl_type.h"

#include "ddlunit.h"
#include "ddlbaseunit.h"
#include "ddlprefix.h"
#include "ddldatatype.h"
#include "ddlenum.h"
#include "ddlcomplex.h"
#include "ddlstream.h"
#include "ddlstreammetatype.h"

namespace ddl
{

template <typename T>
struct LessCompare
{
    bool operator()(const T* obj1, const T* obj2)
    {
      return obj1->getName() < obj2->getName();
    }

    bool operator()(const T* obj, const std::string& name)
    {
      return obj->getName() < name;
    }

    bool operator()(const std::string& name, const T* obj)
    {
      return name < obj->getName();
    }
};

template <typename T>
DDLContainerNoClone<T>::DDLContainerNoClone(bool sorted): _sorted(sorted)
{
}

template <typename T>
void DDLContainerNoClone<T>::insert(T* elem, int pos)
{
    if (_sorted)
    {
        _pointers.insert(std::lower_bound(_pointers.begin(), _pointers.end(), elem, LessCompare<T>()), elem);
    }
    else
    {
        const typename std::vector<T*>::size_type sz_pos =
            static_cast<typename std::vector<T*>::size_type>(pos);
        if (0 <= sz_pos && sz_pos < _pointers.size())
        {
            _pointers.insert(_pointers.begin() + sz_pos, elem);
        }
        else
        {
            _pointers.push_back(elem);
        }
    }
}

template <typename T>
typename DDLContainerNoClone<T>::iterator DDLContainerNoClone<T>::findIt(const std::string& name)
{
  if (_sorted)
  {
      iterator it = std::lower_bound(this->begin(), this->end(), name, LessCompare<T>());
      if (this->end() == it || name != (*it)->getName())
      {
          // not found
          return this->end();
      }
      return it;
  }
  else
  {
      iterator it = std::find_if(this->begin(), this->end(), DDLCompareFunctor<>(name));
      if (this->end() == it)
      {
          // not found
          return this->end();
      }
      return it;
  }
}

template <typename T>
const T* DDLContainerNoClone<T>::find(const std::string& name) const
{
    if (_sorted)
    {
        return find_sorted(name);
    }
    return find_unsorted(name);
}

template <typename T>
T* DDLContainerNoClone<T>::find(const std::string& name)
{
    if (_sorted)
    {
        return find_sorted(name);
    }
    return find_unsorted(name);
}

template <typename T>
const T* DDLContainerNoClone<T>::find_unsorted(const std::string& name) const
{

    const_iterator it = std::find_if(this->begin(), this->end(), DDLCompareFunctor<>(name));
    if (this->end() == it)
    {
        // not found
        return NULL;
    }
    return *it;
}

template <typename T>
const T* DDLContainerNoClone<T>::find_sorted(const std::string& name) const
{

    const_iterator it = std::lower_bound(this->begin(), this->end(), name, LessCompare<T>());
    if (this->end() == it || name != (*it)->getName())
    {
        // not found
        return find_unsorted(name);
    }
    return *it;
}

template <typename T>
T* DDLContainerNoClone<T>::find_unsorted(const std::string& name)
{

    iterator it = std::find_if(this->begin(), this->end(), DDLCompareFunctor<>(name));
    if (end() == it)
    {
        // not found
        return NULL;
    }
    return *it;
}

template <typename T>
T* DDLContainerNoClone<T>::find_sorted(const std::string& name)
{
    iterator it = std::lower_bound(this->begin(), this->end(), name, LessCompare<T>());
    if (end() == it || name != (*it)->getName())
    {
        // not found
        auto obj = find_unsorted(name);
        if (obj != nullptr)
        {
            sort();
        }
        return obj;
    }
    return *it;
}

template <typename T>
void DDLContainerNoClone<T>::copyFromRef(DDLContainerNoClone& other)
{
  _pointers.resize(other.size());
  std::copy(other.begin(), other.end(), _pointers.begin());
  if (_sorted && !other._sorted)
  {
      sort();
  }
}



template <typename T>
void DDLContainerNoClone<T>::deleteAll()
{
  for (iterator it = begin(); it != end(); ++it)
  {
      DDL::deleteChild<T>(*it);
  }
  clear();
}

 template <typename T>
void DDLContainerNoClone<T>::sort()
{
  std::sort(_pointers.begin(), _pointers.end(), LessCompare<T>());
}

template <typename T>
bool DDLContainerNoClone<T>::isSorted() const
{
  return _sorted;
}

// Note on our iterators:
// we do not want to expose the std::vector and std::vector::iterator implementation to the user
// so we use the simplest kind of iterator, a pointer.
template <typename T>
typename DDLContainerNoClone<T>::iterator DDLContainerNoClone<T>::begin()
{
#if defined(WIN32) && ((_MSC_VER < 1600) || defined(_DEBUG))
    // have a look at http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#464 and
    // http://stackoverflow.com/questions/3829788/using-operator-on-empty-stdvector
    // to find out the reason for this special implementation (VS2008 and lower)
    if (_pointers.empty())
    {
        return &_empty_dummy;
    }
    else
    {
        return &*_pointers.begin();
    }
#else
    return &*_pointers.begin();
#endif
}

template <typename T>
typename DDLContainerNoClone<T>::const_iterator DDLContainerNoClone<T>::begin() const
{
#if defined(WIN32) && ((_MSC_VER < 1600) || defined(_DEBUG))
    // have a look at http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#464 and
    // http://stackoverflow.com/questions/3829788/using-operator-on-empty-stdvector
    // to find out the reason for this special implementation (VS2008 and lower)
    if (_pointers.empty())
    {
        return &_empty_dummy;
    }
    else
    {
        return &*_pointers.begin();
    }
#else
    return &*_pointers.begin();
#endif
}

template <typename T>
typename DDLContainerNoClone<T>::iterator DDLContainerNoClone<T>::end()
{
#if defined(WIN32) && ((_MSC_VER < 1600) || defined(_DEBUG))
    // have a look at http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#464 and
    // http://stackoverflow.com/questions/3829788/using-operator-on-empty-stdvector
    // to find out the reason for this special implementation (VS2008 and lower)
    if (_pointers.empty())
    {
        return &_empty_dummy;
    }
    else
    {
        return &*_pointers.begin() + _pointers.size();
    }
#else
    return &*_pointers.end();
#endif
}

template <typename T>
typename DDLContainerNoClone<T>::const_iterator DDLContainerNoClone<T>::end() const
{
#if defined(WIN32) && ((_MSC_VER < 1600) || defined(_DEBUG))
    // have a look at http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#464 and
    // http://stackoverflow.com/questions/3829788/using-operator-on-empty-stdvector
    // to find out the reason for this special implementation (VS2008 and lower)
    if (_pointers.empty())
    {
        return &_empty_dummy;
    }
    else
    {
        return &*_pointers.begin() + _pointers.size();
    }
#else
    return &*_pointers.end();
#endif
}

template <typename T>
void DDLContainerNoClone<T>::clear()
{
    _pointers.clear();
}

template <typename T>
typename DDLContainerNoClone<T>::iterator DDLContainerNoClone<T>::erase(iterator it)
{
    typename std::vector<T*>::iterator it_helper = _pointers.erase(_pointers.begin() + (it - begin()));
#if defined(WIN32) && ((_MSC_VER < 1600) || defined(_DEBUG))
    // have a look at http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#464 and
    // http://stackoverflow.com/questions/3829788/using-operator-on-empty-stdvector
    // to find out the reason for this special implementation (VS2008 and lower)
    if (it_helper == _pointers.end())
    {
        return &_empty_dummy;
    }
    else
    {
        return &*it_helper;
    }
#else
    return &*it_helper;
#endif
}

template <typename T>
typename DDLContainerNoClone<T>::iterator DDLContainerNoClone<T>::erase(iterator pos_first, iterator pos_last)
{
    if (pos_first == pos_last)
    {
        return end();
    }

#if defined(WIN32) && ((_MSC_VER < 1600) || defined(_DEBUG))
    // have a look at http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#464 and
    // http://stackoverflow.com/questions/3829788/using-operator-on-empty-stdvector
    // to find out the reason for this special implementation (VS2008 and lower)

    // another bug in MSVS Debug http://connect.microsoft.com/VisualStudio/feedback/details/557029/visual-c-iterator-debugging-incorrectly-raises-assertion-on-correct-use-of-return-value-of-std-vector-erase
    // prevents us from using the erase(start, end) version.
    typename std::vector<T*>::iterator it_helper = _pointers.begin() + (pos_first - begin());
    for (uint64_t nCount = pos_last - pos_first; nCount > 0; --nCount)
    {
        it_helper = _pointers.erase(it_helper);
    }

    if (it_helper == _pointers.end())
    {
        return &_empty_dummy;
    }
    else
    {
        return &*it_helper;
    }
#else
    typename std::vector<T*>::iterator it_helper = _pointers.erase(_pointers.begin() + (pos_first - begin()),
                                                                      _pointers.begin() + (pos_last - begin()));
    return &*it_helper;
#endif
}

template <typename T>
bool DDLContainerNoClone<T>::empty() const
{
    return _pointers.empty();
}

template <typename T>
unsigned int DDLContainerNoClone<T>::size() const
{
    return static_cast<unsigned int>(_pointers.size());
}

template <typename T>
T*& DDLContainerNoClone<T>::operator[] (unsigned int pos)
{
    return _pointers[pos];
}

template <typename T>
T* const& DDLContainerNoClone<T>::operator[] (unsigned int pos) const
{
    return _pointers[pos];
}

template <typename T>
T*& DDLContainerNoClone<T>::at(unsigned int pos)
{
    return _pointers.at(pos);
}

template <typename T>
T* const& DDLContainerNoClone<T>::at(unsigned int pos) const
{
    return _pointers.at(pos);
}

template <typename T>
DDLContainer<T>::DDLContainer(bool sorted): DDLContainerNoClone<T>(sorted)
{
}

template <typename T>
void DDLContainer<T>::cloneFrom(const DDLContainer& other)
{
    this->clear();
    for (typename DDLContainer::const_iterator it = other.begin(); it != other.end(); ++it)
    {
        this->insert(DDL::clone<T>(*it));
    }
}

template class DDLContainerNoClone<IDDL>;
template class DDLContainerNoClone<DDLUnit>;
template class DDLContainerNoClone<DDLBaseunit>;
template class DDLContainerNoClone<DDLPrefix>;
template class DDLContainerNoClone<DDLDataType>;
template class DDLContainerNoClone<DDLEnum>;
template class DDLContainerNoClone<DDLComplex>;
template class DDLContainerNoClone<DDLStream>;
template class DDLContainerNoClone<DDLStreamMetaType>;

#if defined(WIN32) && ((_MSC_VER < 1600) || defined(_DEBUG))
IDDL* DDLContainerNoClone<IDDL>::_empty_dummy = NULL;
DDLUnit* DDLContainerNoClone<DDLUnit>::_empty_dummy = NULL;
DDLBaseunit* DDLContainerNoClone<DDLBaseunit>::_empty_dummy = NULL;
DDLPrefix* DDLContainerNoClone<DDLPrefix>::_empty_dummy = NULL;
DDLDataType* DDLContainerNoClone<DDLDataType>::_empty_dummy = NULL;
DDLEnum* DDLContainerNoClone<DDLEnum>::_empty_dummy = NULL;
DDLComplex* DDLContainerNoClone<DDLComplex>::_empty_dummy = NULL;
DDLStream* DDLContainerNoClone<DDLStream>::_empty_dummy = NULL;
DDLStreamMetaType* DDLContainerNoClone<DDLStreamMetaType>::_empty_dummy = NULL;
#endif

template class DDLContainer<DDLUnit>;
template class DDLContainer<DDLBaseunit>;
template class DDLContainer<DDLPrefix>;
template class DDLContainer<DDLDataType>;
template class DDLContainer<DDLEnum>;
template class DDLContainer<DDLComplex>;
template class DDLContainer<DDLStream>;
template class DDLContainer<DDLStreamMetaType>;

}