/***************************************************************************
                          BarTime.h  -  description
                             -------------------
    begin                : Fre Jul 23 2004
    copyright            : (C) 2004 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 _LIBMELODY_BARTIME_H
#define _LIBMELODY_BARTIME_H
#include "types.h"

class Signature
{
public:
   Signature(): m_nCount(4), m_nDenom(4), m_nTicks(192), m_dFrac(1){}
   Signature(uint count, uint denom, uint ticks): m_nCount(count), m_nDenom(denom), m_nTicks(ticks), m_dFrac(0)
   { 
      if(denom) m_dFrac = double(count)/double(denom);
   }
   Signature(const Signature& sig): m_nCount(sig.m_nCount), m_nDenom(sig.m_nDenom), m_nTicks(sig.m_nTicks), m_dFrac(sig.m_dFrac)
   {}
   
   Signature& operator=(const Signature& sig)
   {
      m_nCount = sig.m_nCount;
      m_nDenom = sig.m_nDenom;
      m_nTicks = sig.m_nTicks;
      m_dFrac  = sig.m_dFrac;
      return *this;
   }
   uint     count()const{ return m_nCount;}
   uint     denom()const{ return m_nDenom;}
   uint     ticks()const{ return m_nTicks;}
   double   fraction()const{ return m_dFrac;}

   void setCount(const uint count)
   {
      m_nCount = count; setFraction();
   }
   MRESULT setDenom(const uint denom)
   {
      if(denom){
         m_nDenom = denom; 
         setFraction();
         return SUCC_OK;
      }
      return ERR_INVALIDARG;
   }
   void setTicks(const uint ticks){m_nTicks = ticks;}

   void convert(const uint newDenom)
   {
      uint factor = newDenom>m_nDenom ? newDenom/m_nDenom : m_nDenom/newDenom;
      m_nCount *= factor;
      m_nTicks *= factor;
      m_nDenom = newDenom;
   }

   MRESULT setFraction()
   {
      if(!m_nDenom){
         return ERR_FAIL;
      }
      m_dFrac = double(m_nCount)/double(m_nDenom);
      return SUCC_OK; 
   }

   string  toString()
   {
      char buf[16] = "";
      sprintf(buf, "%d/%d", m_nCount, m_nDenom);
      return string(buf);
   }
   uint  getTicksPerBar()const { return m_nCount * m_nTicks;}
   bool isCompatible(const Signature& sig, double& scale) const
   {
      scale = 0;
      if(!isDivisible(m_nCount, sig.m_nCount)) return false;
      if(operator<=(sig)) scale = sig.operator/(*this);
      else          scale = -operator/(sig);
      return true;
   }

   bool operator<(const Signature& sig) const
   {
      return m_dFrac<sig.m_dFrac;
   }

   bool operator>(const Signature& sig) const
   {
      return m_dFrac>sig.m_dFrac;
   }

   bool operator==(const Signature& sig)const
   {
      return m_dFrac==sig.m_dFrac;
   }

   bool operator!=(const Signature& sig)const
   {
      return m_dFrac != sig.m_dFrac;
   }

   bool operator<=(const Signature& sig)const
   {
      return m_dFrac<= sig.m_dFrac;
   }

   bool operator>=(const Signature& sig)const
   {
      return m_dFrac>= sig.m_dFrac;
   }

   double operator/(const Signature& sig)const
   {
      return sig.m_dFrac? m_dFrac/sig.m_dFrac : 0;
   }

   void dump() const{ cout<<m_nCount<<"/"<<m_nDenom<<" ("<<m_nTicks<<" ticks/beat)"<<endl;}

private:
   uint     m_nCount;
   uint     m_nDenom;
   uint     m_nTicks;
   double   m_dFrac;

   bool isDivisible(const uint a, const uint b) const
   {
      uint max = a>b ? a : b;
      uint min = a>b ? b : a;
      if(min == 0) return false;
      return max % min == 0;
   }
};

class BarPosition
{
public:
   static uint getNearestTick(const uint ticks, const uint division, const uint quant);

public:
   BarPosition()
      : m_nBar(nopos), m_nBeat(nopos), m_nTicks(nopos), m_sig(4,4,192)
   {}
   BarPosition(const Signature &sig)
      : m_nBar(nopos), m_nBeat(nopos), m_nTicks(nopos), m_sig(sig)
   {}
   BarPosition(const uint bar, const uint beat, const uint ticks, const Signature &sig) 
      : m_nBar(bar), m_nBeat(beat), m_nTicks(ticks), m_sig(sig)
   {}
   BarPosition(const BarPosition& bar) 
      : m_nBar(bar.m_nBar), m_nBeat(bar.m_nBeat), m_nTicks(bar.m_nTicks), m_sig(bar.m_sig)
   {}

   const uint& getBar()   const   { return m_nBar;  }
   const uint& getBeat()  const   { return m_nBeat; }
   const uint& getTicks() const   { return m_nTicks;}
   const Signature& getSignature() const { return m_sig;   }
   uint  getTicksPerBar()const    { return m_sig.count() * m_sig.ticks();}
   uint  getTotalTicks()  const   { return (m_nBeat-1)*m_sig.ticks() + m_nTicks;}
   bool  isSet()const             { return m_nBar != nopos && m_nBeat != nopos && m_nTicks != nopos;}
   bool  isValid()const           { return m_nBeat <= m_sig.denom() && m_nTicks < m_sig.ticks();}

   void  setBar(const uint   nBar) { m_nBar = nBar;    }
   void  setBeat(const uint  nBeat) { m_nBeat = nBeat; }
   void  setTicks(const uint nTicks){ m_nTicks= nTicks;}
   void  setSignature(const Signature& sig){ m_sig = sig;}
   
   void  set(const uint   nBar, const uint   nBeat, const uint   nTicks)
   {
      m_nBar   = nBar;
      m_nBeat  = nBeat;
      m_nTicks = nTicks;
   }
   
   BarPosition& operator=(const BarPosition& bar)
   {
      m_nBar   = bar.m_nBar;
      m_nBeat  = bar.m_nBeat;
      m_nTicks = bar.m_nTicks;
      m_sig    = bar.m_sig;
      return *this;
   }

   bool operator>(const BarPosition& bar)const
   {
      if(m_nBar>bar.m_nBar) return true;
      if(m_nBar==bar.m_nBar){
         if(m_nBeat>bar.m_nBeat) return true;
         if(m_nBeat==bar.m_nBeat){
            if(m_nTicks>bar.m_nTicks) return true;
         }
      }
      return false;
   }

   bool operator<(const BarPosition& bar)const
   {
      if(m_nBar<bar.m_nBar) return true;
      if(m_nBar==bar.m_nBar){
         if(m_nBeat<bar.m_nBeat) return true;
         if(m_nBeat==bar.m_nBeat){
            if(m_nTicks<bar.m_nTicks) return true;
         }
      }
      return false;
   }

   bool operator==(const BarPosition& bar)const
   {
      if( m_nBar  == bar.m_nBar  &&
         m_nBeat  == bar.m_nBeat &&
         m_nTicks == bar.m_nTicks) return true;
      return false;
   }

   bool operator!=(const BarPosition& bar)const
   {
      return !operator==(bar);
   }

   bool operator<=(const BarPosition& bar)const
   {
      return operator<(bar) || operator==(bar);
   }

   bool operator>=(const BarPosition& bar)const
   {
      return operator>(bar) || operator==(bar);
   }

   int   quantize(uint quantLevel = 24);

   void  addTicks(const uint ticks);
   void  addBeats(const uint beats);
   void  addBars(const uint bars);
   bool  addNth(const uint reci, const uint count);
   
   void  dump()    const
   { 
      if(isSet()) cout<<m_nBar<<"."<<m_nBeat<<"."<<m_nTicks<<endl;
      else cout<<"(N/A)"<<endl;
   }
   void  dumpAll() const
   { 
      if(isSet()){
         cout<<m_nBar<<"."<<m_nBeat<<"."<<m_nTicks<<" <-"; 
         m_sig.dump();
      }
      else cout<<"(N/A)"<<endl;
   }
   uint  getTicksToNextBeat();

protected:
   uint        m_nBar;
   uint        m_nBeat;
   uint        m_nTicks;
   Signature   m_sig;

   uint getNearestTick(const uint quant);

};
static BarPosition invalidBarPosition;

typedef vector<BarPosition>            tBarTimeline;
typedef tBarTimeline::iterator         barIter;
typedef tBarTimeline::const_iterator   const_barIter;

class BarTimeline: public tBarTimeline
{
public:

   BarTimeline():m_nTimeBase(0){};
   BarTimeline(const BarTimeline& btl):tBarTimeline(btl), m_nTimeBase(btl.m_nTimeBase)
   {
   };
   ~BarTimeline(){};

   BarTimeline& operator=(const BarTimeline& btl);
   
   void           normalize();
   BarTimeline    quantize(indizes& removed, uint quantLevel = 24);
   bool           isQuantized();
   void           dump();
   uint           getTimebase() const ;
   duration       getTickDurations() const;

   void           setTimebase(){ m_nTimeBase = getTimebase(); }

protected:
   uint m_nTimeBase;
};
uint ggT(uint m, uint n);
uint kgV(uint m, uint n);


#endif
