#ifndef EBASICARRAY_H
#define EBASICARRAY_H

#include "eutils.h"

#include<vector>
#include<iostream>

using namespace std;

#include "ebasearray.h"
#include "ebasicarray_dec.h"

#include "evar_dec.h"
#include "estr.h"
#include "evarclass_dec.h"
#include "evartype.h"

#include "logger.h"

#include "evarclass.h"
/*
template <>
void ebasicarray<evar*>::addvar(evar& var);
template <>
evar ebasicarray<evar*>::getvar(int i) const;
*/

template <class T>
void ecbasicarray<T>::init(size_t count)
{
  size_t i;
  clear();
  vector<T>::reserve(count);
  for (i=0; i<count; ++i)
    vector<T>::push_back(T());
}

template <class T>
void ecbasicarray<T>::init(size_t count,const T& initval)
{
  size_t i;
  clear();
  vector<T>::reserve(count);
  for (i=0; i<count; ++i)
    vector<T>::push_back(initval);
}

template <class T>
void ecbasicarray<T>::serial(const efile& f) const
{
  serialuint(size(),f);
  if (size()==0) return;

  if (!getClasses().exists(typeid(T).name())) { lerror(estr("class not registered: ")+typeid(T).name()); return; }

  eclassBase *pClass=&getClasses().values(typeid(T).name());
  evarType<T> *pType=new evarType<T>((T&)at(0));
  pType->cleanup=false;
  pClass->fserialf(pType,f);
  size_t i;
  for (i=1; i<size(); ++i){
    pType->object=(T*)&at(i);
    pClass->fserialf(pType,f);
  }
  delete pType;
//    ::serial(at(i),data);
}
template <class T>
bool ecbasicarray<T>::unserial(const efile& f)
{
  unsigned int count;
  if (!unserialuint(count,f)) return(false);

  clear();

  T var;
  eclassBase *pClass=&getClasses().values(typeid(T).name());
  evarType<T> *pType=new evarType<T>((T&)var);
  pType->cleanup=false;

  while(count){
    if (!pClass->funserialf(pType,f)) { delete pType; return(false); }
    add(var);
    --count;
  }
  delete pType;

  if (count) return(false);

  return(true);
}


template <class T>
void ecbasicarray<T>::serial(estr& data) const
{
  serialuint(size(),data);
  if (size()==0) return;

  if (!getClasses().exists(typeid(T).name())) { lerror(estr("class not registered: ")+typeid(T).name()); return; }

  size_t ilen=data.len();

  eclassBase *pClass=&getClasses().values(typeid(T).name());
  evarType<T> *pType=new evarType<T>((T&)at(0));
  pType->cleanup=false;
  pClass->fserial(pType,data);

  if (data.len()-ilen>0){ data.reserve(ilen+ (data.len()-ilen)*size()); }

  size_t i;
  for (i=1; i<size(); ++i){
    pType->object=(T*)&at(i);
    pClass->fserial(pType,data);
  }
  delete pType;
//    ::serial(at(i),data);
}


//  evar var(value);
//  var.var->cleanup=false;
//  if (!getClasses().exists(var.getClass())) { lerror(estr("class not registered: ")+var.getClass()); return(i); }
//  return(getClasses().values(var.getClass()).funserial(var.var,data,i));


template <class T>
size_t ecbasicarray<T>::unserial(const estr& data,size_t i)
{
  clear();
  unsigned int count;
  T var;
  i=unserialuint(count,data,i);
  if (i==-1) return(-1);

  eclassBase *pClass=&getClasses().values(typeid(T).name());
  evarType<T> *pType=new evarType<T>((T&)var);
  pType->cleanup=false;

  while(count && i<data.len()){
    i=pClass->funserial(pType,data,i);
    if (i==-1) { delete pType; return(-1); }
    add(var);
    --count;
  }
  delete pType;

  if (count) return(-1);

  return(i);
}

template <class T>
void ecbasicarray<T>::addvar(evar& var)
{
  if (var.getTypeid()==typeid(T))
    add(var.get<T>());
  else {
    evar cvar(var.convert(typeid(T)));
    if (!cvar.isNull())
      add(cvar.get<T>());
    else
      lerror("unable to convert: "+estr(var.getTypeid().name())+" to: "+typeid(T).name());
  }
}
template <class T>
evar ecbasicarray<T>::getvar(size_t i) const
{
  return(evarRef((T&)at(i)));
}

template <>
void ecbasicarray<evar>::addvar(evar& var);
template <>
evar ecbasicarray<evar>::getvar(size_t i) const;



template <class T>
ostream &operator<<(ostream &stream,const ebasicarray<T> &var)
{
  stream<<"{ "<<endl;
  size_t i;

  if (var.size()){
    for (i=0; i<var.size()-1; ++i){
      stream<<var[i]<<", "<<endl;
    }
    stream<<var[i]<<endl;
  }
  stream<<" }";
  return(stream);
}


template <class T>
ecbasicarray<T>::ecbasicarray(): vector<T>()
{
}

template <class T>
ecbasicarray<T>::ecbasicarray(const ebasearray &arr)
{
  evar tmpv;
  for (int i=0; i<arr.size(); ++i){
    tmpv.set(arr.getvar(i));
    addvar(tmpv);
  }
}

template <class T>
ecbasicarray<T>::ecbasicarray(const ecbasicarray<T> &arr): vector<T>()
{
  size_t i;
  for (i=0; i<arr.size(); ++i)
    vector<T>::push_back(arr[i]);
}

template <class T> template <class... Tarr>
ecbasicarray<T>::ecbasicarray(const T& value,const Tarr&... values): vector<T>()
{
  add(value,values...);
//  vector<T>::push_back(value);
}
/*
template <class T>
ecbasicarray<T>::ecbasicarray(const T& v1,const T& v2): vector<T>()
{
  vector<T>::push_back(v1);
  vector<T>::push_back(v2);
}

template <class T>
ecbasicarray<T>::ecbasicarray(const T& v1,const T& v2,const T& v3): vector<T>()
{
  vector<T>::push_back(v1);
  vector<T>::push_back(v2);
  vector<T>::push_back(v3);
}

template <class T>
ecbasicarray<T>::ecbasicarray(const T& v1,const T& v2,const T& v3,const T& v4): vector<T>()
{
  vector<T>::push_back(v1);
  vector<T>::push_back(v2);
  vector<T>::push_back(v3);
  vector<T>::push_back(v4);
}

template <class T>
ecbasicarray<T>::ecbasicarray(const T& v1,const T& v2,const T& v3,const T& v4,const T& v5): vector<T>()
{
  vector<T>::push_back(v1);
  vector<T>::push_back(v2);
  vector<T>::push_back(v3);
  vector<T>::push_back(v4);
  vector<T>::push_back(v5);
}

template <class T>
ecbasicarray<T>::ecbasicarray(const T& v1,const T& v2,const T& v3,const T& v4,const T& v5,const T& v6): vector<T>()
{
  vector<T>::push_back(v1);
  vector<T>::push_back(v2);
  vector<T>::push_back(v3);
  vector<T>::push_back(v4);
  vector<T>::push_back(v5);
  vector<T>::push_back(v6);
}

template <class T>
ecbasicarray<T>::ecbasicarray(const T& v1,const T& v2,const T& v3,const T& v4,const T& v5,const T& v6,const T& v7): vector<T>()
{
  vector<T>::push_back(v1);
  vector<T>::push_back(v2);
  vector<T>::push_back(v3);
  vector<T>::push_back(v4);
  vector<T>::push_back(v5);
  vector<T>::push_back(v6);
  vector<T>::push_back(v7);
}
*/

template <class T>
ecbasicarray<T>::~ecbasicarray()
{
}

template <class T>
ebasicarray<T> &ecbasicarray<T>::operator=(const ecbasicarray<T> &arr)
{
  clear();
  *this += arr;
  return(static_cast<ebasicarray<T>&>(*this));
}

template <class T>
ebasicarray<T> &ecbasicarray<T>::operator+=(const ecbasicarray<T> &arr)
{
  size_t i;
  for (i=0; i<arr.size(); ++i)
    vector<T>::push_back(arr[i]);
  return(static_cast<ebasicarray<T>&>(*this));
}

template <class T>
ebasicarray<T> ecbasicarray<T>::operator[](const elongarray& arr) const
{
  ebasicarray<T> res;
  size_t i;
  for (i=0; i<arr.size(); ++i){
    if (arr[i]<0 || arr[i]>=size()) { ldwarn("element in array list out of bounds: "+estr(arr[i])); continue; }
    res.add(operator[](arr[i]));
  }
  return(res); 
}

template <class T>
ebasicarray<T> ecbasicarray<T>::operator[](const eintarray& arr) const
{
  ebasicarray<T> res;
  size_t i;
  for (i=0; i<arr.size(); ++i){
    if (arr[i]<0 || arr[i]>=size()) { ldwarn("element in array list out of bounds: "+estr(arr[i])); continue; }
    res.add(operator[](arr[i]));
  }
  return(res); 
}

template <class T>
ebasicarray<T> &ecbasicarray<T>::operator-=(const ecbasicarray<T> &arr)
{
  size_t ind,j;
  for (j=0; j<arr.size(); ++j){
    if (-1 != (ind = find(arr[j])))
      vector<T>::erase(vector<T>::begin()+ind);
  }
  return(static_cast<ebasicarray<T>&>(*this));
}

template <class T>
ebasicarray<T> ecbasicarray<T>::operator+(const ecbasicarray<T> &arr) const
{
  ebasicarray<T> tmpa;

  tmpa+=*this;
  tmpa+=arr;
  return(tmpa);
}

template <class T>
ebasicarray<T> ecbasicarray<T>::operator-(const ecbasicarray<T> &arr) const
{
  ebasicarray<T> tmpa;

  tmpa=*this;
  tmpa-=arr;
  return(tmpa);
}

/*
template <class T>
ecbasicarray<T> ecbasicarray<T>::operator[](const ebasicarray<int> &iarr)
{
  ecbasicarray<T> tmpa;

  int i;
  for (i=0; i<iarr.size(); ++i)
    tmpa += iarr[i];

  return(tmpa);
}
*/

template <class T>
ebasicarray<T>& ecbasicarray<T>::reverse()
{
  if (size()==0)
    return(static_cast<ebasicarray<T>&>(*this));
  for (size_t i=0; i<=size()/2; ++i)
    swap(i,size()-i-1);
  return(static_cast<ebasicarray<T>&>(*this));
}

template <class T>
ebasicarray<T> ecbasicarray<T>::subset(long i,long l) const
{
  ebasicarray<T> tmpa;
 
  if (i<0) i+=vector<T>::size();
  if (l<0) l+=vector<T>::size()-i+1;

  if (i<0 || l<0 || !vector<T>::size()) return(tmpa);

  l+=i;
  if (l>vector<T>::size()) l=vector<T>::size();
  for(;i<l;++i)
    tmpa+=(*this)[i];
  return(tmpa);
}

template <class T>
long ecbasicarray<T>::find(const T &value,long i,bool (*match)(const T &a, const T &b)) const
{
  if (i<0) i+=vector<T>::size();

  if (i<0) return(-1);

  for (;i<vector<T>::size(); ++i){
    if ( match( at(i) , value) )
      return(i);
  }
  return(-1);
}

/*
template <class T>
ecbasicarray<T> ecbasicarray<T>::afindall(const T &value,int i,bool (*match)(const T &a, const T &b)) const
{
  ecbasicarray<T> tmpai;

  if (i<0) i+=vector<T>::size();

  if (i<0) return(tmpai);

  for (;i<vector<T>::size(); ++i){
    if ( match( (*this)[i] , value) )
      tmpai += (*this)[i];
  }
  return(tmpai);
}
*/

template <class T>
ebasicarray<T> ecbasicarray<T>::afindall(const ecbasicarray<T> &arr,long i,bool (*match)(const T &a, const T &b)) const
{
  ebasicarray<T> tmpai;

  if (i<0) i+=vector<T>::size();

  if (i<0){ ldwarn("starting index below 0: "+estr(i)); return(tmpai); }
  for (;i<vector<T>::size(); ++i){
    if ( -1 != arr.find( (*this)[i] , 0 , match) )
      tmpai += (*this)[i];
  }
  return(tmpai);
}

template <class T>
eintarray ecbasicarray<T>::findall(const T &value,long i,bool (*match)(const T &a, const T &b)) const
{
  eintarray tmpai;

  if (i<0) i+=vector<T>::size();
  if (i<0) { ldwarn("starting index below 0: "+estr(i)); return(tmpai); }

  for (;i<vector<T>::size(); ++i){
    if ( match( (*this)[i] , value) )
      tmpai += i;
  }
  return(tmpai);
}

template <class T>
eintarray ecbasicarray<T>::findall(const ecbasicarray<T> &arr,long i,bool (*match)(const T &a, const T &b)) const
{
  eintarray tmpai;

  if (i<0) i+=vector<T>::size();
  if (i<0) { ldwarn("starting index below 0: "+estr(i)); return(tmpai); }

  for (;i<vector<T>::size(); ++i){
    if ( -1 != arr.find( (*this)[i] , 0 , match) )
      tmpai += i;
  }
  return(tmpai);
}

template <class T>
ebasicarray<T>::ebasicarray() {}

template <class T>
template <class... Tarr>
ebasicarray<T>::ebasicarray(const T& v,const Tarr&... values): ecbasicarray<T>(v,values...) {}

template <class T>
ebasicarray<T>::ebasicarray(const ebasicarray<T>& arr): ecbasicarray<T>(arr) {}


template <class T>
ebasicarray<T>::ebasicarray(const earray<evar>& varr)
{
  evar tmpv;
  for (int i=0; i<varr.size(); ++i){
//    ecbasicarray<T>::add(varr[i].get<T>());
    tmpv.set(varr[i]);
    ecbasicarray<T>::addvar(tmpv);
  }
}



#endif

