/***************************************************************************

    source::worx xtree
    Copyright © 2024-2025 c.holzheuer
    christoph.holzheuer@gmail.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 of the License, or
    (at your option) any later version.

***************************************************************************/


#ifndef XQMAPTOR_H
#define XQMAPTOR_H

#include <xqmapindex.h>
#include <xqexception.h>


/**
 * @brief map + vector = XQMaptor, a template storage class whose data
 * items can be accessed via string keys and int indices.
 */

template<typename T>
class XQMaptor
{

public:

  XQMaptor()
  {
  }


  XQMaptor( int itemsize )
  {
    if( itemsize )
      _data.resize( itemsize );
  }


  XQMaptor( const XQMaptor& src )
  {
    *this=src;
  }


  virtual ~XQMaptor()
  {

  }

  XQMaptor& operator=( const XQMaptor& src )
  {
    if( this == &src )
      return *this;
    _data  = src._data;
    _index = src._index;
    return *this;
  }

  // STL-like iterators
  auto begin()
  {
    return _data.begin();
  }

  auto end()
  {
    return _data.end();
  }

  auto begin() const
  {
    return _data.begin();
  }

  auto end() const
  {
    return _data.end();
  }

  inline int size() const
  {
    return (int) _data.size();
  }



  inline bool isEmpty() const
  {
    return (_data.size()==0);
  }

  inline bool contains( int index ) const
  {
    return index < size() && index > -1;
  }

  inline bool contains( const QString& key ) const
  {
    return mapIndex().contains(key);
  }

  inline const XQMapIndex& mapIndex() const
  {
    return _index;
  }

  int indexOf( const QString& key ) const
  {   
    return mapIndex().indexOf(key);
  }

  int indexOf(const T& entry) const
  {
    for (int i=0; i<_data.size(); ++i)
    {
      if (_data[i] == entry)
        return i;
    }
    return -1;
  }

  virtual QString keyOf( int index ) const
  {
    return mapIndex().key( index );
  }

  T& operator[]( int index )
  {
    if( contains(index) )
      return _data[index];
    throw XQException("XQMaptor operator[ int index ]: out of range");
  }

  const T& operator[]( int index ) const
  {
    if ( contains(index) )
      return _data[index];
    throw XQException("XQMaptor const operator[ int index ]: out of range");
  }

  T& at( int index )
  {
    return (*this)[index];
  }

  const T& at( int index ) const
  {
    return (*this)[index];
  }

  T& operator[]( const QString& key )
  {
    if( key.isEmpty() || !contains(key) )
      throw XQException("XQMaptor operator[]: key empty || not found: " + key);
    return _data[ _index[key] ];
  }

  const T& operator[]( const QString& key ) const
  {
    if (key.isEmpty() || !contains(key))
      throw XQException("XQMaptor operator[]: key empty || not found: " + key);
    return _data[_index[key]];
  }

  T& at( const QString& key )
  {
    return (*this)[key];
  }

  const T& at( const QString& key ) const
  {
    return (*this)[key];
  }

  const T& last() const
  {
    if(_data.isEmpty())
      throw XQException( "XQMaptor last: is empty!" );
    return _data.last();
  }

  const T& first() const
  {
    if(_data.isEmpty())
      throw XQException( "XQMaptor first: is empty!" );
    return _data.first();
  }

  T& last()
  {
    if(_data.isEmpty())
      throw XQException( "XQMaptor last: is empty!" );
    return _data.last();
  }

  T& first()
  {
    if(_data.isEmpty())
      throw XQException( "XQMaptor first: is empty!" );
    return _data.first();
  }


  virtual int add( const T& item )
  {
    _data.push_back( item );
    return _data.size()-1;
  }

  virtual void addAtIndex( int index, const T& item  )
  {
    if(contains(index))
      throw XQException( "XQMaptor add at index: index out of range!" );
    _data[index] = item;
  }

  // convenience method to mimic QMap<T,QString>
  virtual void insert( const T& item, const QString& key )
  {
    addAtKey(key, item );
  }

  virtual void addAtKey( const QString& key, const T& item )
  {
    XQMapIndex::iterator pos = _index.find( key );
    if( pos == _index.end() )
    {
      _data.push_back( item );
      _index[key] = _data.size()-1;
    }
    else
    {
      _data[pos.value()] = item;
    }
  }

  bool addAlias( const QString& key, const QString& alias )
  {
    // look for 'original' key
    int key_idx = indexOf(key);
    // quit if not found
    if( key_idx < 0 )
      return false;
    // look for alias
    int alias_idx = indexOf(alias);
    // quit if found: don't overwrite anything
    if( alias_idx > -1 )
      return false;
    // store alias
    _index[ alias ] = key_idx;
    return true;
  }

  void addKey( const QString& key, int index )
  {
    _index.addKey( key, index );
  }

  virtual void clear()
  {
    _data.clear();
    _index.clear();
  }

  virtual bool killEntry( const QString& key )
  {
    int idx = indexOf( key );
    if( idx<0 )
      return false;
    return killEntry( (int) idx );
  }

  virtual bool killEntry( int index )
  {
    if( index >= this->_data.size() )
      return false;
    // eintrag lschen
    this->_data.erase( this->_data.begin()+index );
    // index updaten
    _index.update( index );
    return true;
  }

  virtual QString toString() const
  {
    return join( ";" );
  }

  virtual void dump() const
  {
    throw XQException("XQMaptor: dump not implemented!" );
  }

  virtual QString join( const QString& sep, int from=0, int to=-1) const
  {
    Q_UNUSED(sep)
    Q_UNUSED(from)
    Q_UNUSED(to)
    throw XQException("XQMaptor: join not implemented!" );
    return "--";
  }

  int replaceKey( const QString& oldkey, const QString& newkey )
  {
    int idx = indexOf( oldkey );
    if( idx<0 || oldkey == newkey )
      return idx;
    _index.remove( oldkey );
    _index[ newkey ] = idx;
    return idx;
  }

protected:

  QVector<T> 	_data;
  XQMapIndex 	_index;

};


#endif // XQMAPTOR_H
