//
//  DataBuffer.h
//
//  Qdos2 - data interfacing
//
//  David Burgess
//  June 1999
//

#ifndef Q_DATABUFFER_H
#define Q_DATABUFFER_H

#include <vector>
#include <string>
#include <math.h>

#include "Var.h"
#include "Naming.h"
#include "QdTime.h"

namespace QDOS {

typedef enum BufferedDataMajorityE
 { DBUFFER_NIL_MAJOR, DBUFFER_ROW_MAJOR, DBUFFER_COL_MAJOR }
   BufferedDataMajority;

// +++++++++++++++++++++++++++++++++++++++++++++++++ class DataBufferDescriptor
// ----------------------------------------------------------------------------
class DataBufferDescriptor : public VarTarget {
  int              _num_item_elts; // number of elements per item
  int              _num_items;
  vector<int>      _dims;
  BufferedDataMajority _majority;
public:
  DataBufferDescriptor() 
    : _num_items(0), _num_item_elts(1), _majority(DBUFFER_NIL_MAJOR) {;}
  DataBufferDescriptor( const DataBufferDescriptor& a )
    : _num_items(0), _majority(a._majority) {set_dims(a._dims);}
  
  void clear(void)  { _num_items=0; _dims.clear(); _num_item_elts=1; }

  void set_dims( const vector<int>& d );
  void set_num_items( int n ) { _num_items = n; }
  void set_majority( BufferedDataMajority m ) { _majority = m; }
  void calc_num_items( int n_elts ) { _num_items = n_elts/_num_item_elts;}

  void get_dims( vector<int>& d );
  int  num_item_elts(void) const { return _num_item_elts; }
  int  dims_size(void) const { return _dims.size(); }
  int  num_items(void) const { return _num_items; }
  BufferedDataMajority majority( void ) { return _majority; }

  void diag_display(void) const;
};

typedef Var<DataBufferDescriptor> DataBufferDescriptor_var;

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class DataBuffer
// ----------------------------------------------------------------------------
class DataBuffer : public VarTarget {
public:
  virtual ~DataBuffer() {;}
  virtual string get_type_srep(void) const = 0;
  virtual void diag_print(void) const = 0;
};

// +++++++++++++++++++++++++++++++++++++++++++++++++++++ class SimpleDataBuffer
// ----------------------------------------------------------------------------
class SimpleDataBuffer : public DataBuffer {
public:
  virtual DataBufferDescriptor_var descriptor(void) = 0;
  virtual void rewind(void) = 0;
  virtual void clear(void) = 0;
  virtual int num_items(void) const = 0;
  virtual int num_elts(void) const = 0;  
  virtual int num_item_elts(void) const = 0;  
};

// ++++++++++++++++++++++++++++++++++++++++++++++++++++ class NumericDataBuffer
// ----------------------------------------------------------------------------
class NumericDataBuffer : public SimpleDataBuffer {
public:
  virtual bool get_data( int n, vector<double>& db, bool append=false )=0;
};

// +++++++++++++++++++++++++++++++++++++++++++++++++++++ class StringDataBuffer
// ----------------------------------------------------------------------------
class StringDataBuffer : public SimpleDataBuffer {
public:
  virtual bool get_data( int n, vector<string>& db, bool append=false )=0;
};


typedef Var<DataBuffer>        DataBuffer_var;
typedef Var<NumericDataBuffer> NumericDataBuffer_var;
typedef Var<StringDataBuffer>  StringDataBuffer_var;

// ++++++++++++++++++++++++++++++++++++++++++++++++++ class StringPtrDataBuffer
// ----------------------------------------------------------------------------
class DataPtrBuffer : public DataBuffer {
public:
  DataBuffer_var data;
  StringDataBuffer_var data_ptr_name;
  string get_type_srep(void) const { return "DataPtrBuffer"; }
  void diag_print(void) const
  { data->diag_print(); data_ptr_name->diag_print(); };

};

typedef Var<DataPtrBuffer>        DataPtrBuffer_var;

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class TSDataBuffer
// ----------------------------------------------------------------------------
class TSDataBuffer : public DataBuffer {
public:
  DataBuffer_var data;
  DataBuffer_var ttags;
  TimeInterval time_interval;

  string get_type_srep(void) const { return "TSDataBuffer"; }
  void diag_print(void) const
   { cout << "TSDataBuffer: interval: "<< time_interval << endl;
     data->diag_print(); ttags->diag_print(); };

};

typedef Var<TSDataBuffer>  TSDataBuffer_var;

// ++++++++++++++++++++++++++++++++++++++++++++++++ class BasicVectorDataBuffer
// ----------------------------------------------------------------------------
template<class T> class BasicVectorDataBuffer {
public:
  typedef T value_type;
protected:
  DataBufferDescriptor_var desc;
  vector<T> v;
  unsigned int pos;
public:
  BasicVectorDataBuffer(void) : pos(0), desc( new DataBufferDescriptor() ) {;}
  BasicVectorDataBuffer( const DataBufferDescriptor& dbdesc )
    : pos(0), desc( new DataBufferDescriptor( dbdesc ) ) {;}

//  ~BasicVectorDataBuffer(void) { cout << "~BasicVectorDataBuffer"<<endl;}
  
  bool get_data( int n, vector<T>& db, bool append=false)
    {
      if( !append ) db.clear();// clear the receiving vector if append not requested
      if( pos >= v.size() ) return false;  // nothing to return
      int i=0;  // number of items packed
      while( i<n && pos < v.size() )
      { db.push_back( v[pos++] ); i++; }
      return pos < v.size();// return true if more to come
    }

  DataBufferDescriptor_var descriptor(void) { return desc; }
  void rewind(void) { pos=0; }
  void clear(void) { v.clear(); pos=0; desc->clear(); }

  void push_back( T x ) { v.push_back(x); desc->calc_num_items(v.size()); }
  void push_back( const vector<T>& x )
    { for(int i=0;i<x.size();i++) v.push_back(x[i]);
      desc->calc_num_items(v.size()); }
  void push_back( const BasicVectorDataBuffer<T>& x )
    { for(int i=0;i<x.v.size();i++) v.push_back(x.v[i]);
      desc->calc_num_items(v.size()); }

  void reserve( size_t n ) { v.reserve(n);}
  
  void set_dims( const vector<int>& d ) { desc->set_dims( d ); }
  void set_majority( BufferedDataMajority m ) { desc->set_majority( m ); }

  int num_elts(void)  const {return v.size();}
  int num_items(void) const {return desc->num_items();}
  int num_item_elts(void) const {return desc->num_item_elts();}
  int size(void) { return v.size(); }
  
  T operator[](int i) const { return v[i]; }
  
  bool item_approx_equal( int item, const vector<T>& a,
                          double precision ) const // a is treated cyclically!
    { bool ok=true;
      int elt0 = item * desc->num_item_elts();
      int elt1 = elt0 + desc->num_item_elts();
      for( int i=elt0; i<elt1; i++ )
        { int j = i%a.size();
          if( fabs( v[i] - a[j] ) > fabs( v[i]*precision ) )
          { ok = false; break; }
        }
      return ok; }
      
  void delete_item( int item )
    {  v.erase( v.start() + item*(desc->num_item_elts()),
                v.start() + (item+1)*(desc->num_item_elts()) ); }

};


// +++++++++++++++++++++++++++++++++++++++++++++++++++ class NumericVDataBuffer
// ----------------------------------------------------------------------------
class NumericVDataBuffer
 : public BasicVectorDataBuffer<double>, public NumericDataBuffer {

public:
  NumericVDataBuffer(void) {;}
  NumericVDataBuffer( const DataBufferDescriptor& dbdesc )
    : BasicVectorDataBuffer<double>( dbdesc ) {;}

  DataBufferDescriptor_var descriptor(void)
    { return BasicVectorDataBuffer<double>::descriptor(); }
  void rewind(void) { BasicVectorDataBuffer<double>::rewind(); }
  void clear(void)  { BasicVectorDataBuffer<double>::clear(); }
  bool get_data( int n, vector<double>& db, bool append );
  string get_type_srep(void) const { return "NumericVDataBuffer"; }
  void diag_print(void) const;
  int num_items(void) const;
  int num_elts(void) const;  
  int num_item_elts(void) const;
};


// ++++++++++++++++++++++++++++++++++++++++++++++++++++ class StringVDataBuffer
// ----------------------------------------------------------------------------
class StringVDataBuffer
 : public BasicVectorDataBuffer<string>, public StringDataBuffer {

public:
  StringVDataBuffer(void) {;}
  StringVDataBuffer( const DataBufferDescriptor& dbdesc )
    : BasicVectorDataBuffer<string>( dbdesc ) {;}

  DataBufferDescriptor_var descriptor(void);
  void rewind(void);
  void clear(void);
  bool get_data( int n, vector<string>& db, bool append );
  string get_type_srep(void) const;
  void diag_print(void) const;
  int num_items(void) const;
  int num_elts(void) const;  
  int num_item_elts(void) const;  
 };

typedef Var<NumericVDataBuffer> NumericVDataBuffer_var;
typedef Var<StringVDataBuffer> StringVDataBuffer_var;

} // namespace QDOS


#endif //  #ifndef Q_DATABUFFER_H
