/***************************************************************************
                          EditDistance.h  -  description
                             -------------------
    begin                : Don Mai 8 2003
    copyright            : (C) 2003 by klaus frieler
    email                : kgf@omniversum.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 EDITDISTANCE_H
#define EDITDISTANCE_H
#include  "Types.h"
#include  "Melody.h"
#include  <cmath>

typedef double (*costFunc)(const double v1, const double v2);
//inline myfabs(const double a){ return a<0 ? -a:a;}

template<class T>
class EditDistance
{
public:
   static  double stdCost(const double v1, const double v2){
      return v1 == v2 ? 0.0: 1.0;
   }
   static  double diffCost(const double v1, const double v2){
      return fabs(v1 - v2);
   }
   
public:
   EditDistance():m_pCost(&EditDistance::stdCost){}
   EditDistance(costFunc pCost): m_pCost(pCost){}
   ~EditDistance(){}
   double calcDistance(const T s, const uint sLen, const T t, const uint tLen);
   double operator()  (const T s, const uint sLen, const T t, const uint tLen);

protected:
   costFunc m_pCost;
};

template<class T>
class Cost
{
public:
   Cost(){};
   virtual ~Cost(){}
   virtual double costDelete(const T& v) const = 0;
   virtual double costInsert(const T& v) const = 0;
   virtual double costSubstitute(const T& v1, const T& v2) const = 0;
};

template<class T>
class StdCost:public Cost<T>
{
public:
   StdCost(const double del = 1.0, const double subs = 1.0, const double match = 0.0):m_del(del), m_subs(subs), m_match(match){};
   ~StdCost(){}
   
   double costDelete(const T& v) const { return m_del;}
   double costInsert(const T& v) const { return m_del;}
   double costSubstitute(const T& v1, const T& v2) const{ return (v1 == v2) ? m_match : m_subs; }
protected:
   double   m_del, m_subs, m_match;      
};

template<class T>
class DiffCost:public Cost<T>
{
public:
   DiffCost(){};
   ~DiffCost(){}
   double costDelete(const T& v) const { return static_cast<double>(v);}
   double costInsert(const T& v) const { return static_cast<double>(v);}
   double costSubstitute(const T& v1, const T& v2) const { return fabs(static_cast<double>(v1-v2));}
};

template<class T>
class SpikeCost:public Cost<T>
{
public:
   SpikeCost(const double a = 1, const double b = 1):m_a(a), m_b(b){};
   ~SpikeCost(){}
   double costDelete(const T& v) const { return static_cast<double>(1);}
   double costInsert(const T& v) const { return static_cast<double>(1);}
   double costSubstitute(const T& v1, const T& v2) const { 
      double diff1 = fabs(static_cast<double>(v1.first)-static_cast<double>(v2.first));
      double diff2 = fabs(static_cast<double>(v1.second)-static_cast<double>(v2.second));
      return m_a * diff1 + m_b * diff2;
   }
private: 
   double m_a, m_b;
};

class SimilarityCost:public Cost<PMelody>
{
public:
   SimilarityCost(const CombiType& combiType, const double insdel = 1.0 ):m_combiType(combiType), m_insdel(insdel){}

   double costDelete(const PMelody& v) const
   {
      //cout<<"Sim("<<v->getName().c_str()<<") = "<<1.0<<endl;
      return m_insdel;
   }
   double costInsert(const PMelody& v) const
   {
      //cout<<"Sim("<<v->getName().c_str()<<") = "<<1.0<<endl;
      return m_insdel;
   }
   double costSubstitute(const PMelody& v1, const PMelody& v2) const
   {
      double dummy;
      double cost = v1->similarity(*v2, m_combiType, dummy);
      if(cost>1.) cost = 1.0;
      if(cost<0) cost = 0;
      //cout<<"Sim("<<v1->getName().c_str()<<", "<<v2->getName().c_str()<<") = "<<1.0-cost<<endl;
      return 1.0-cost;
   }
protected:
   CombiType   m_combiType;
   double      m_insdel;
};

template<class T>
class EditDistance2
{

public:
   EditDistance2(const Cost<T> *pCost): m_pCost(pCost), m_dMaxDist(0.0){}
   EditDistance2(const StdCost<T> *pCost): m_pCost(pCost), m_dMaxDist(0.0){}
   //EditDistance2(const DiffCost<T> *pCost): m_pCost(pCost), m_dMaxDist(0.0){}
   ~EditDistance2(){}
   double   calcDistance(const vector<T>& s, const vector<T>& t);
   double   calcDistance(const T* s, uint sLen, const T* t, uint tLen);
   double   operator()  (const vector<T>& s, const vector<T>& t){ return calcDistance(s, t);}
   double   operator()  (const T* s, uint sLen, const T* t, uint tLen){ return calcDistance(s, sLen, t, tLen);}
   double   getMaxDist() const { return m_dMaxDist;}
   
protected:
   const Cost<T>* m_pCost;
   double m_dMaxDist;
};

#endif
