// 
//  Interp.h
//  Qdos2 - MultiJoin Generic Interpolation Class 
//
//  Janet Barnes
//  May 2000
//

#ifndef QDOS_INTERP_H
#define QDOS_INTERP_H

#include <string>
#include <map>
#include "Sequence.h"
#include "LinearInterp.h"
#include "Average.h"
#include "NNeighbour.h"

#define LINEAR "linear"
#define BOXCAR "boxcar average"
#define NEAREST_NEIGHBOUR "nearest neighbour"
#define CUBIC "cubic spline"

namespace QDOS{


class InterpException: public Exception
{
public:
    InterpException(ExceptionMsg m=""):Exception(m<<"[InterpException]"){;}
};
class InterpFailed: public InterpException
{
public:
    InterpFailed(ExceptionMsg m=""):\
        InterpException(m<<"[InterpFailed]"){;}
};
class InterpNotDefined: public InterpException
{
public:
    InterpNotDefined(ExceptionMsg m=""):\
        InterpException(m<<"[InterpNotDefined]"){;}
};

template<class Tx, class Ty>
class Interp
{
public:
// interpolate at a point 
virtual void operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, Tx& x_out,Ty& y_out) const
{throw InterpNotDefined();}

// remove gaps
virtual vector<bool> operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, NumericSequence<Tx>& x_out,\
    NumericSequence<Ty>& y_out, const Tx& gap_criterion) const
{throw InterpNotDefined();} 
    
// ignore gaps 
virtual vector<bool> operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, NumericSequence<Tx>& x_out,\
    NumericSequence<Ty>& y_out) const
{throw InterpNotDefined();}
    
// fill fixed gaps with fill value
virtual vector<bool> operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, NumericSequence<Tx>& x_out,\
    NumericSequence<Ty>& y_out, const Tx& gap_criterion, \
    const Ty& fill_value) const
{throw InterpNotDefined();}

// interpolate over gaps 
virtual vector<bool> operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, NumericSequence<Tx>& x_out,\
    NumericSequence<Ty>& y_out, const Tx& gap_criterion, \
    const Interp<Tx,Ty>* gap_interp) const
{throw InterpNotDefined();}

virtual string interp_srep() const=0;
    
};

template<class Tx, class Ty>
class LinearInterp:public Interp<Tx,Ty>
{
public:
// interpolate at a point 
virtual void operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, Tx& x_out,Ty& y_out) const
{ linear_interp(x_in,y_in,x_out,y_out);}

// remove gaps
virtual vector<bool> operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, NumericSequence<Tx>& x_out,\
    NumericSequence<Ty>& y_out, const Tx& gap_criterion) const
{ return series_linear_interp(x_in,y_in,x_out,y_out,gap_criterion);}

// ignore gaps
virtual vector<bool> operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, NumericSequence<Tx>& x_out,\
    NumericSequence<Ty>& y_out) const
{return series_linear_interp(x_in,y_in,x_out,y_out);}

// fill gaps
virtual vector<bool> operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, NumericSequence<Tx>& x_out,\
    NumericSequence<Ty>& y_out, const Tx& gap_criterion, \
    const Ty& fill_value) const
{ return series_linear_interp(x_in,y_in,x_out,y_out,gap_criterion,fill_value);} 

// interpolate over gaps 
virtual vector<bool> operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, NumericSequence<Tx>& x_out,\
    NumericSequence<Ty>& y_out, const Tx& gap_criterion, \
    const Interp<Tx,Ty>* gap_interp) const
{ 
    bool (*pinterp)(const NumericSequence<Tx>&,const NumericSequence<Ty>&,
      const Tx&,Ty&);

    if(gap_interp)
    {
      if(string(NEAREST_NEIGHBOUR)==gap_interp->interp_srep())
         pinterp=nneighbour;
      else 
         pinterp=linear_interp;
    }
    else
      pinterp=linear_interp;
    return series_linear_interp(x_in,y_in,x_out,y_out,gap_criterion,pinterp);
}

virtual string interp_srep() const
{ return string(LINEAR);}

};

template<class Tx, class Ty>
class Average:public Interp<Tx,Ty>
{
public:

// remove gaps
virtual vector<bool> operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, NumericSequence<Tx>& x_out,\
    NumericSequence<Ty>& y_out, const Tx& ave_width) const
{  return series_data_average(x_in,y_in,x_out,y_out,ave_width);}

// fill gaps
virtual vector<bool> operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, NumericSequence<Tx>& x_out,\
    NumericSequence<Ty>& y_out, const Tx& ave_width, \
    const Ty& fill_value) const
{  return series_data_average(x_in,y_in,x_out,y_out,ave_width,fill_value);}

// interpolate over gaps 
virtual vector<bool> operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, NumericSequence<Tx>& x_out,\
    NumericSequence<Ty>& y_out, const Tx& ave_width, \
    const Interp<Tx,Ty>* gap_interp) const
{ 
    bool (*pinterp)(const NumericSequence<Tx>&,const NumericSequence<Ty>&,
      const Tx&,Ty&);
    if(gap_interp)
    {
	if(string(NEAREST_NEIGHBOUR)==gap_interp->interp_srep())
	  pinterp=nneighbour;
	else
	  pinterp=linear_interp;
    }
    else
      pinterp=linear_interp;
    return series_data_average(x_in,y_in,x_out,y_out,ave_width,pinterp);
}
 
virtual string interp_srep() const
{ return string(BOXCAR);}

}; 

template<class Tx, class Ty>
class NNeighbour:public Interp<Tx,Ty>
{
public:
// interpolate at a point 
virtual void operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, Tx& x_out,Ty& y_out) const
{ nneighbour(x_in,y_in,x_out,y_out);}

// ignore gaps
virtual vector<bool> operator()(const NumericSequence<Tx>& x_in, \
    const NumericSequence<Ty>& y_in, NumericSequence<Tx>& x_out,\
    NumericSequence<Ty>& y_out) const
{return series_nneighbour(x_in,y_in,x_out,y_out);}

virtual string interp_srep() const
{ return string(NEAREST_NEIGHBOUR);}

};
  

template<class Tx,class Ty>
class InterpTypes
{
    map<string,Interp<Tx,Ty>*> _interp_types;
public:
    InterpTypes()
    { set_interp_type(LINEAR,new LinearInterp<Tx,Ty>()); 
      set_interp_type(BOXCAR, new Average<Tx,Ty>());
      set_interp_type(NEAREST_NEIGHBOUR,new NNeighbour<Tx,Ty>());}
    
    ~InterpTypes()
    { 
      for(typename map<string,Interp<Tx,Ty>*>::iterator itr=\
        _interp_types.begin();\
          itr!=_interp_types.end();itr++)
      {
              delete itr->second;
      }
    } 
    
    Interp<Tx,Ty>* get_interp_type(string str)
    { Interp<Tx,Ty>* ptr=_interp_types[str];
      if (ptr==0)
          throw InterpNotDefined();
      return ptr;
    }
    
    void set_interp_type(string str, Interp<Tx,Ty>* interp)
    { _interp_types[str]=interp;}
};  


}  // end namespace QDOS
#endif
