/***************************************************************************
                          Melody.h  -  description
                             -------------------
    begin                : Die Nov 25 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 _LIBMELODY_MELODY_H
#define _LIBMELODY_MELODY_H
#include "types.h"
#include "Tone.h"
#include "BarTime.h"
#include "Vector.h"

#define DUMMY_PITCH  128

class Melody: public ToneList
{
public:
   static double major[12];
   static double minor[12];
   static double tonics[12];

public:
   Melody(): m_key(0), m_firstOne(0), m_nDurationUnit(1), m_nUnitIOI(1), m_bChecked()
   {
      addTempo(500.0);
   }
   Melody(const Melody& mel):
      ToneList(mel),
      m_key(mel.m_key),
      m_tempo(mel.m_tempo),
      m_signature(mel.m_signature),
      m_firstOne(mel.m_firstOne),
      m_nDurationUnit(mel.m_nDurationUnit),
      m_nUnitIOI(mel.m_nUnitIOI),
      m_bChecked(mel.m_bChecked),
      m_segments(mel.m_segments)
      {}
      
   ~Melody(){}

   //TO BE DONE
   //Melody&     operator=(const Melody& mel){return *this;}
   Melody      subMelody(const uint start, const uint end);

   //setter 
   void        setKey(uint key){ m_key = key;}
   void        clearTempi(){ m_tempo.clear(); }
   void        setTempo(double t){ m_tempo.clear(); m_tempo.push_back(t);}
   void        addTempo(double t){ m_tempo.push_back(t);}
   void        setTicks(uint ticks){ m_signature.setTicks(ticks);}
   void        setSignature(const Signature& sig){ m_signature = sig;}
   void        setSignature(const uint count, const uint denom, const uint ticks){ m_signature = Signature(count, denom, ticks); }
   void        setFirstOneIdx(uint idx){ m_firstOne = idx;}
   void        setDurationUnit(const uint unit){m_nDurationUnit = unit;}
   void        setIOIUnit(const uint unit){m_nUnitIOI = unit;}

   //getter
   uint        getKey() const { return  m_key;}   
   uint        getTicks() const { return  m_signature.ticks();}
   double      getTempo() const { return m_tempo[0];}
   Signature   getSignature() const { return m_signature;}
   uint        getFirstOneIdx() const {return m_firstOne;}
   uint        getFirstOneUnits() const;
   uint        getStartUnits() const;
   uint        getStartTicks() const;
   uint        getTicksOfFirstBar() const;
   uint        getIOIUnit() const { return m_nUnitIOI;}
   uint        getDurationUnit()  const { return m_nDurationUnit;}
   double      getMsPerTick()   const { return m_signature.ticks() ? getTempo()/m_signature.ticks() : 0;}
   double      getTicksPerMs()   const { return !m_tempo.empty() ? double(m_signature.ticks())/getTempo() : 0;}
   uint        getTicksPerBar() const { return  m_signature.getTicksPerBar();}
   uint        getUnitsPerBar() const { return  m_nUnitIOI ? m_signature.getTicksPerBar()/m_nUnitIOI  : 0;}

   //analysis
   bool        hasMeterInformation() const;
   uint        calculateKey(const uint mode = 1)const;   
   uint        calculateKeyKS()const;   
   uint        calculateKeyMarkov()const;   
   //pitch statistics
   uint        getMinPitch() const;
   uint        getMaxPitch() const;
   uint        getAmbitus() const;
   double      getMeanPitch() const;
   uint        getModusPitch() const;
   uint        getMedianPitch()  const;
   void        getPitchList(iData &pitches) const;
   void        getPitchHistogram(iData &pitches, iData &frequencies) const;

   //IOI/duration statistics, you should use checkXXXunit before hand
   uint        getMaxIOITicks() const;
   uint        getMaxDurationTicks() const;
   uint        getMaxIOIUnits() const;
   uint        getMaxDurationUnits() const;
   uint        getMinIOITicks() const;
   uint        getMinDurationTicks() const;
   uint        getMinIOIUnits() const;
   uint        getMinDurationUnits() const;
   double      getTotalLength() const;
   uint        getLengthTicks() const;

   //various exports
   iData       getPitches() const;
   dData       getOnsets() const;
   dData       getDurations() const;
   dData       getIOIs() const;
   dData       getOOIs() const;
   BarTimeline getBarPositions() const;

   iData       getOnsetTicks() const;
   iData       getIOITicks()   const;
   iData       getDurationTicks() const;
   iData       getIOIsInUnits() const;
   iData       getDurationsInUnits() const;
   iData       getIOIsInUnits(uint &minIOI) const;
   iData       getDurationsInUnits(uint &minDur) const;
      
   //we need it
   void        push_back(const uint ticks, const double t, const uint pitch, const double dur = 0.0, BarPosition pos = invalidBarPosition){ ToneList::push_back(Tone(ticks, t, pitch, dur, pos));}
   void        push_back(const Tone& tone){ ToneList::push_back(tone);}
   
   void        createOnsets(double start = 0);
   void        createIOIs();
   
   //adjust parameters
   void        check(uint quantLevel = 0);
   void        checkTicks();
   void        checkDurationUnit();
   void        checkIOIUnit();
   void        calcBarPositions(uint quantLevel = 24);
   void        calcTicks();
   void        removeNoteOverlaps();
   //public helpers
   uint        ggT(uint t1, uint t2) const;
   uint        divRound(const uint x, const uint y) const;
   uint        divRoundError(const uint x, const uint y, double& err) const;

   //debug
   void        dump();
   void        dumpStatistics();

   void        setSegments(const Vector& segments){ m_segments = segments;}
   Vector      getSegments()const{ return m_segments;}

protected:
   uint           m_key;
   vector<double> m_tempo;
   Signature   m_signature;
   uint        m_firstOne;
   uint        m_nDurationUnit;
   uint        m_nUnitIOI;
   bool        m_bChecked;
   Vector      m_segments; //to be removed
   //internal helper
   uint        getMinDivErrFactor(const uint d, const uint unit, const uint maxN) const;
   uint        getMinDivErrFactor2or3(const uint d, const uint unit) const;
   double      diffToNearestMultiple(const uint v, const uint unit) const;
   uint        getTicksFiller()const;
   bool        validPitch(const uint pitch)const;
   uint        getNextValidPitchIdx(const uint start)const;
};

#endif
