/****************************************************************************
**
** https://www.qxorm.com/
** Copyright (C) 2013 XDL Team (ic-east.com)
**
** This file is part of the QxOrm library
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any
** damages arising from the use of this software
**
** Commercial Usage
** Licensees holding valid commercial QxOrm licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and XDL Team
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file 'license.gpl3.txt' included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met : http://www.gnu.org/copyleft/gpl.html
**
** If you are unsure which license is appropriate for your use, or
** if you have questions regarding the use of this file, please contact :
** ic-east.com
**
****************************************************************************/

namespace qx
{

   template <typename Key, typename Value>
   QxCollection<Key, Value>::QxCollection() : IxCollection(), m_batch(false)
   {
   }

   template <typename Key, typename Value>
   QxCollection<Key, Value>::QxCollection(const QxCollection<Key, Value> &other) : IxCollection(), m_batch(false)
   {
      cloneCollection(this, other);
   }

   template <typename Key, typename Value>
   QxCollection<Key, Value>::~QxCollection()
   {
   }

   template <typename Key, typename Value>
   QxCollection<Key, Value> &QxCollection<Key, Value>::operator=(const QxCollection<Key, Value> &other)
   {
      if (this != (&other))
      {
         cloneCollection(this, other);
      }
      return (*this);
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::operator==(const QxCollection<Key, Value> &other) const
   {
      return isSameCollection(this, other);
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::operator!=(const QxCollection<Key, Value> &other) const
   {
      return (!isSameCollection(this, other));
   }

   template <typename Key, typename Value>
   void QxCollection<Key, Value>::cloneCollection(QxCollection<Key, Value> *pClone, const QxCollection<Key, Value> &pRef)
   {
      if (!pClone)
      {
         return;
      }
      if (pClone == (&pRef))
      {
         return;
      }
      QMutexLocker locker1(&pRef.m_mutex);
      QMutexLocker locker2(&pClone->m_mutex);
      qAssert(pRef.m_list.size() == pRef.m_hash.size());
      pClone->m_list = pRef.m_list;
      pClone->m_hash = pRef.m_hash;
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::isSameCollection(const QxCollection<Key, Value> *p1, const QxCollection<Key, Value> &p2) const
   {
      if (!p1)
      {
         return false;
      }
      if (p1 == (&p2))
      {
         return true;
      }
      if (p1->size() != p2.size())
      {
         return false;
      }
      QMutexLocker locker1(&p2.m_mutex);
      QMutexLocker locker2(&p1->m_mutex);
      qAssert(p2.m_list.size() == p2.m_hash.size());
      return ((p1->m_list == p2.m_list) && (p1->m_hash == p2.m_hash));
   }

   template <typename Key, typename Value>
   void QxCollection<Key, Value>::updateHashPosition(long from /* = 0 */, long to /* = -1 */, bool check /* = false */)
   {
      if (m_batch)
      {
         return;
      }
      QMutexLocker locker(&m_mutex);
      qAssert(m_list.size() == m_hash.size());
      if (to == -1)
      {
         to = (m_list.size() - 1);
      }
      if ((from < 0) || (to >= m_list.size()) || (from > to))
      {
         return;
      }
      for (long idx = from; idx <= to; idx++)
      {
         const Key &key = m_list.at(idx).first;
         m_hash.insert(key, idx);
      }
      if (check)
      {
         qAssert(m_list.size() == m_hash.size());
      }
   }

   template <typename Key, typename Value>
   typename QxCollection<Key, Value>::iterator QxCollection<Key, Value>::begin()
   {
      QMutexLocker locker(&m_mutex);
      return m_list.begin();
   }

   template <typename Key, typename Value>
   typename QxCollection<Key, Value>::iterator QxCollection<Key, Value>::end()
   {
      QMutexLocker locker(&m_mutex);
      return m_list.end();
   }

   template <typename Key, typename Value>
   typename QxCollection<Key, Value>::const_iterator QxCollection<Key, Value>::begin() const
   {
      QMutexLocker locker(&m_mutex);
      return m_list.begin();
   }

   template <typename Key, typename Value>
   typename QxCollection<Key, Value>::const_iterator QxCollection<Key, Value>::end() const
   {
      QMutexLocker locker(&m_mutex);
      return m_list.end();
   }

#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))

   template <typename Key, typename Value>
   typename QxCollection<Key, Value>::reverse_iterator QxCollection<Key, Value>::rbegin()
   {
      QMutexLocker locker(&m_mutex);
      return m_list.rbegin();
   }

   template <typename Key, typename Value>
   typename QxCollection<Key, Value>::reverse_iterator QxCollection<Key, Value>::rend()
   {
      QMutexLocker locker(&m_mutex);
      return m_list.rend();
   }

   template <typename Key, typename Value>
   typename QxCollection<Key, Value>::const_reverse_iterator QxCollection<Key, Value>::rbegin() const
   {
      QMutexLocker locker(&m_mutex);
      return m_list.rbegin();
   }

   template <typename Key, typename Value>
   typename QxCollection<Key, Value>::const_reverse_iterator QxCollection<Key, Value>::rend() const
   {
      QMutexLocker locker(&m_mutex);
      return m_list.rend();
   }

#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))

   template <typename Key, typename Value>
   void QxCollection<Key, Value>::reserve(long size)
   {
      if (size <= 0)
      {
         return;
      }
      QMutexLocker locker(&m_mutex);
      qAssert(m_list.size() == m_hash.size());
      m_list.reserve(size);
      m_hash.reserve(size);
   }

   template <typename Key, typename Value>
   void QxCollection<Key, Value>::reverse()
   {
      {
         QMutexLocker locker(&m_mutex);
         qAssert(m_list.size() == m_hash.size());
         std::reverse(m_list.begin(), m_list.end());
      }
      updateHashPosition();
   }

   template <typename Key, typename Value>
   void QxCollection<Key, Value>::clear()
   {
      QMutexLocker locker(&m_mutex);
      m_hash.clear();
      m_list.clear();
   }

   template <typename Key, typename Value>
   long QxCollection<Key, Value>::count() const
   {
      QMutexLocker locker(&m_mutex);
      qAssert(m_list.size() == m_hash.size());
      return static_cast<long>(m_list.size());
   }

   template <typename Key, typename Value>
   long QxCollection<Key, Value>::size() const
   {
      return this->count();
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::contains(const Key &key) const
   {
      QMutexLocker locker(&m_mutex);
      qAssert(m_list.size() == m_hash.size());
      return (m_hash.contains(key));
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::exist(const Key &key) const
   {
      return this->contains(key);
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::empty() const
   {
      QMutexLocker locker(&m_mutex);
      qAssert(m_list.size() == m_hash.size());
      return m_list.isEmpty();
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::push_back(const Key &key, const Value &value)
   {
      return this->insert(key, value);
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::push_front(const Key &key, const Value &value)
   {
      return this->insert(0, key, value);
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::insert(const Key &key, const Value &value)
   {
      qAssert(!exist(key));
      QMutexLocker locker(&m_mutex);
      qAssert(m_list.size() == m_hash.size());
      m_list.append(qMakePair(key, value));
      m_hash.insert(key, (m_list.size() - 1));
      return true;
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::insert(long index, const Key &key, const Value &value)
   {
      qAssert(!exist(key));
      if (index < 0)
      {
         index = 0;
      }
      if (index >= size())
      {
         return this->insert(key, value);
      }

      {
         QMutexLocker locker(&m_mutex);
         qAssert(m_list.size() == m_hash.size());
         m_list.insert(index, qMakePair(key, value));
         m_hash.insert(key, index);
      }

      updateHashPosition(index);
      return true;
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::insert(const QxCollection<Key, Value> &other)
   {
      {
         if (this == (&other))
         {
            return false;
         }
         QMutexLocker locker1(&m_mutex);
         QMutexLocker locker2(&other.m_mutex);
         m_list.append(other.m_list);
         m_hash.unite(other.m_hash);
      }

      updateHashPosition(0, -1, true);
      return true;
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::insert(long index, const QxCollection<Key, Value> &other)
   {
      if (index < 0)
      {
         index = 0;
      }
      if ((index >= size()) && (index != 0))
      {
         index = (size() - 1);
      }
      if (this == (&other))
      {
         return false;
      }
      QMutexLocker locker1(&other.m_mutex);

      {
         QMutexLocker locker2(&m_mutex);
         qAssert(m_list.size() == m_hash.size());
         m_batch = true;
      }

      for (long l = 0; l < other.size(); l++)
      {
         const type_pair_key_value &pair = other.m_list.at(l);
         this->insert((index + l), pair.first, pair.second);
      }

      {
         QMutexLocker locker3(&m_mutex);
         m_batch = false;
      }
      updateHashPosition(index);
      return true;
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::replace(long index, const Key &key, const Value &value)
   {
      qAssert(!exist(key));
      QMutexLocker locker(&m_mutex);
      m_hash.remove(m_list.at(index).first);
      m_list.replace(index, qMakePair(key, value));
      m_hash.insert(key, index);
      qAssert(m_list.size() == m_hash.size());
      return true;
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::swap(long index1, long index2)
   {
      if (index1 < 0 || index1 >= size())
      {
         return false;
      }
      if (index2 < 0 || index2 >= size())
      {
         return false;
      }
      if (index1 == index2)
      {
         return true;
      }

      QMutexLocker locker(&m_mutex);
      const Key &key1 = m_list.at(index1).first;
      const Key &key2 = m_list.at(index2).first;
      m_hash.insert(key1, index2);
      m_hash.insert(key2, index1);
      m_list.swap(index1, index2);
      qAssert(m_list.size() == m_hash.size());
      return true;
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::move(long indexFrom, long indexTo)
   {
      return swap(indexFrom, indexTo);
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::removeByKey(const Key &key)
   {
      qAssert(exist(key));
      long pos = 0;

      {
         QMutexLocker locker(&m_mutex);
         pos = m_hash.value(key, -1);
         if ((pos < 0) || (pos >= m_list.size()))
         {
            return false;
         }
         qAssert(m_list.at(pos).first == key);
         m_hash.remove(key);
         m_list.removeAt(pos);
      }

      updateHashPosition(pos, -1, true);
      return true;
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::removeByIndex(long index)
   {
      if (index < 0 || index >= size())
      {
         return false;
      }

      {
         QMutexLocker locker(&m_mutex);
         const Key &key = m_list.at(index).first;
         qAssert(m_hash.value(key, -1) == index);
         m_hash.remove(key);
         m_list.removeAt(index);
      }

      updateHashPosition(index, -1, true);
      return true;
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::removeByIndex(long first, long last)
   {
      if (first < 0 || first >= size())
      {
         return false;
      }
      if (last < 0 || last >= size())
      {
         return false;
      }
      if (first > last)
      {
         return false;
      }
      {
         QMutexLocker locker(&m_mutex);
         m_batch = true;
      }
      for (long idx = first; idx <= last; idx++)
      {
         removeByIndex(idx);
      }
      {
         QMutexLocker locker(&m_mutex);
         m_batch = false;
      }
      updateHashPosition(first);
      return true;
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::removeFirst()
   {
      return this->removeByIndex(0);
   }

   template <typename Key, typename Value>
   bool QxCollection<Key, Value>::removeLast()
   {
      return this->removeByIndex(size() - 1);
   }

   template <typename Key, typename Value>
   typename QxCollection<Key, Value>::const_reference_value QxCollection<Key, Value>::getByKey(const Key &key) const
   {
      qAssert(exist(key));
      QMutexLocker locker(&m_mutex);
      qAssert(m_list.size() == m_hash.size());
      const type_pair_key_value &pair = m_list.at(m_hash.value(key, -1));
      qAssert(pair.first == key);
      return pair.second;
   }

   template <typename Key, typename Value>
   typename QxCollection<Key, Value>::const_reference_value QxCollection<Key, Value>::getByIndex(long index) const
   {
      QMutexLocker locker(&m_mutex);
      qAssert(m_list.size() == m_hash.size());
      qAssert((index >= 0) && (index < static_cast<long>(m_list.size())));
      qAssert(m_hash.value(m_list.at(index).first, -1) == index);
      return m_list.at(index).second;
   }

   template <typename Key, typename Value>
   typename QxCollection<Key, Value>::const_reference_value QxCollection<Key, Value>::getFirst() const
   {
      qAssert(!empty());
      QMutexLocker locker(&m_mutex);
      qAssert(m_list.size() == m_hash.size());
      return m_list.at(0).second;
   }

   template <typename Key, typename Value>
   typename QxCollection<Key, Value>::const_reference_value QxCollection<Key, Value>::getLast() const
   {
      qAssert(!empty());
      QMutexLocker locker(&m_mutex);
      qAssert(m_list.size() == m_hash.size());
      return m_list.at(m_list.size() - 1).second;
   }

   template <typename Key, typename Value>
   typename QxCollection<Key, Value>::const_reference_key QxCollection<Key, Value>::getKeyByIndex(long index) const
   {
      QMutexLocker locker(&m_mutex);
      qAssert(m_list.size() == m_hash.size());
      qAssert((index >= 0) && (index < static_cast<long>(m_list.size())));
      qAssert(m_hash.value(m_list.at(index).first, -1) == index);
      return m_list.at(index).first;
   }

   template <typename Key, typename Value>
   void QxCollection<Key, Value>::sortByKey(bool bAscending /* = true */)
   {
      if (bAscending)
      {
         QMutexLocker locker(&m_mutex);
         std::sort(m_list.begin(), m_list.end(), (&compareKeyValue < std::is_pointer<Key>::value || qx::trait::is_smart_ptr<Key>::value, 0 > ::compareByKeyAscending));
      }
      else
      {
         QMutexLocker locker(&m_mutex);
         std::sort(m_list.begin(), m_list.end(), (&compareKeyValue < std::is_pointer<Key>::value || qx::trait::is_smart_ptr<Key>::value, 0 > ::compareByKeyDescending));
      }
      updateHashPosition(0, -1, true);
   }

   template <typename Key, typename Value>
   void QxCollection<Key, Value>::sortByValue(bool bAscending /* = true */)
   {
      if (bAscending)
      {
         QMutexLocker locker(&m_mutex);
         std::sort(m_list.begin(), m_list.end(), (&compareKeyValue < std::is_pointer<Value>::value || qx::trait::is_smart_ptr<Value>::value, 0 > ::compareByValueAscending));
      }
      else
      {
         QMutexLocker locker(&m_mutex);
         std::sort(m_list.begin(), m_list.end(), (&compareKeyValue < std::is_pointer<Value>::value || qx::trait::is_smart_ptr<Value>::value, 0 > ::compareByValueDescending));
      }
      updateHashPosition(0, -1, true);
   }

} // namespace qx

template <typename Key, typename Value>
QDataStream &operator<<(QDataStream &stream, const qx::QxCollection<Key, Value> &t)
{
   long lCount = t.count();
   stream << (qint32)(lCount);

   for (long l = 0; l < lCount; l++)
   {
      stream << t.getKeyByIndex(l);
      stream << t.getByIndex(l);
   }

   return stream;
}

template <typename Key, typename Value>
QDataStream &operator>>(QDataStream &stream, qx::QxCollection<Key, Value> &t)
{
   qint32 lCount = 0;
   stream >> lCount;
   t.clear();
   t.reserve(lCount);

   for (qint32 l = 0; l < lCount; l++)
   {
      Key key;
      stream >> key;
      Value value;
      stream >> value;
      t.insert(key, value);
   }

   return stream;
}
