/***************************************************************************
                          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 BARTIME_H
#define BARTIME_H
#include "Types.h"
#include "VectorManip.h"

struct Signature{
   Signature()
      : m_nCount(4), m_nDenom(4), m_nTicks(1024), 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 = (count+0.0)/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;
   }
   void scale(uint denom)
   {
      uint factor = denom>m_nDenom ? denom/m_nDenom : m_nDenom/denom;
      m_nCount *= factor;
      m_nTicks *= factor;
      m_nDenom = denom;
   }
   void setFraction()
   {
      m_dFrac = (m_nCount+0.0)/m_nDenom;
   }
   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 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;
   }

   uint getBarTicks() const { return m_nCount * m_nTicks;}
   
   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{
      cerr<<m_nCount<<"/"<<m_nDenom<<" ("<<m_nTicks<<" ticks/beat)"<<endl;
   }

   uint     m_nCount;
   uint     m_nDenom;
   uint     m_nTicks;
   double   m_dFrac;
};
static   Signature sig44(4,4,3092);
class BarPosition
{
public:
   BarPosition(): m_nBar(1), m_nBeat(1), m_nTicks(0), m_nBeatDivision(1), m_pSig(&sig44){}
   BarPosition(const Signature *pSig) : m_nBar(1), m_nBeat(1), m_nTicks(0), m_nBeatDivision(1), m_pSig(pSig){}
   BarPosition(uint bar, uint beat, uint ticks, const Signature *pSig)
      : m_nBar(bar), m_nBeat(beat), m_nTicks(ticks), m_nBeatDivision(1), m_pSig(pSig){}
   BarPosition(const BarPosition& bar)
      : m_nBar(bar.m_nBar), m_nBeat(bar.m_nBeat), m_nTicks(bar.m_nTicks), m_nBeatDivision(1),m_pSig(bar.m_pSig){}

   const uint getBar()   const   { return m_nBar;  }
   const uint getBeat()  const   { return m_nBeat; }
   const uint getTicks() const   { return m_nTicks;}
   const uint getTicksInBar() const   { return (m_nBeat-1)*m_pSig->m_nTicks + m_nTicks;}
   const uint getBeatDivision() const   { return m_nBeatDivision;}
   const Signature& getSignature() const { return *m_pSig;   }
   uint  getTotalTicks()  const   { return getBarTicks() * m_nBar + (m_nBeat-1)*m_pSig->m_nTicks + m_nTicks;}
   uint  getBarTicks() const { return m_pSig->m_nTicks* m_pSig->m_nCount;}
   double getAsPhase(const double extraTicks = 1.0) const { 
      //uint ticks = getTotalTicks();
      double ticks = getTicksInBar();
      double tot = getBarTicks();
      return tot>1e-4  ? 2*PI*(ticks/tot) : 0;
   }
   double getDivisionAsPhase()const
   {
      double ticks = getTicks();
      double tot = getSignature().m_nTicks;
      return tot>1e-4  ? 2*PI*ticks/tot : 0;
   
   }
   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  setBeatDivision(const uint nDiv) { m_nBeatDivision= nDiv;}
   //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_nBeatDivision = bar.m_nBeatDivision;
      m_pSig    = bar.m_pSig;
      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(const uint quant);
   bool  isQuantized(const uint quant);

   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{ cerr<<m_nBar<<"."<<m_nBeat<<"."<<m_nTicks<<" ("<<m_nBeatDivision<<")"<<endl;}
   void           dumpAll() const{ cerr<<m_nBar<<"."<<m_nBeat<<"."<<m_nTicks<<" <-"; m_pSig->dump();}
   uint           getTicksToNextBeat();
   BarPosition    nextFullBeat()const;
   BarPosition    nextPrimaryBeat()const;
   int            getBeatLevel(const uint nTimeBase)const; 
   uint           getUnitTicks(const uint nTimeBase)const;

protected:
   uint        m_nBar;
   uint        m_nBeat;
   uint        m_nTicks;
   uint        m_nBeatDivision;
   const Signature*  m_pSig;

   uint getNearestTick(const uint quant);

};

//typedef vector<BarPosition>      TimeLine;
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(const uint quant, indizes& removed);
   bool           isQuantized(const uint quant);
   void           dump();
   uint           getTimebase() const ;
   durations      getTickDurations() const;
   void           setTimebase(){ m_nTimeBase = getTimebase(); }
   void           rescaleTicks(uint factor);
   int            getBeatLevel(const uint idx)const; 
   uint           isSyncope(const uint idx, const int level)const;
   uint           diffTicks(const uint idx1, const uint idx2) const;
   uint           getBeatDivision(const uint i, uint& nCount )const;
   void           setBeatDivisions();
protected:
   uint           m_nTimeBase;
};
#endif
