/***************************************************************************
                          Melody.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 MELODY_H
#define MELODY_H
#include "Types.h"
#include "BarTime.h"
#include <vector>

template <class T>
void dumpData(const char* text, const vector<T>& vec);


class Melody
{
   friend class File;

public:
   static  uint   nGramLength;
   static double major[12];
   static double minor[12];
   static uint sharps[6];
   static uint flats[6];

public:
//construction
   Melody():m_sName(""), m_pOut(&std::cout), m_quantLevel(24)
   {      
   }
   Melody(const char *name):m_sName(name? name:string("")), m_pOut(&std::cout),m_quantLevel(24), m_annotatedKey(0)
   {
   }
   ~Melody()
   {
      clear();
   }

   Melody(const Melody& melody):
      m_pitches(melody.m_pitches),
      m_durations(melody.m_durations),
      m_onsets(melody.m_onsets),
      m_phrases(melody.m_phrases),
      m_sName(melody.m_sName),
      m_signature(melody.m_signature),
      m_annotatedKey(melody.m_annotatedKey),
      m_timeLine(melody.m_timeLine),
      m_pOut(melody.m_pOut),
      m_quantLevel(melody.m_quantLevel)
   {
   }

//operation
   Melody& operator = (const Melody& melody)
   {
      m_pitches  = melody.m_pitches;
      m_durations= melody.m_durations;
      m_onsets   = melody.m_onsets;
      m_sName    = melody.m_sName;
      m_signature= melody.m_signature;
      m_annotatedKey = melody.m_annotatedKey;
      m_timeLine = melody.m_timeLine;
      m_phrases  = melody.m_phrases;
      m_pOut     = melody.m_pOut;      
      m_quantLevel=melody.m_quantLevel;
      return *this;
   }
   Melody clone()const{ return *this;}

//implementation
   ostream&       out() const { return *m_pOut;}
   void           setOStream(ostream* pOut){ m_pOut = pOut;}
   void           setName(const char * name){ m_sName = name;}
   string         getName() const { return m_sName;}
   uint           getQuantLevel() const { return m_quantLevel;}
   void           setQuantLevel(const uint quant) { m_quantLevel = quant;}
   void           getPitchRange(MIDIPitch &low, MIDIPitch &high) const;
   PitchSequence     getPitches(uint idx);
   data           getPitchesAsDouble(uint idx = 0) const;
   uint           getSize() const { return m_pitches.size() == m_durations.size()? m_pitches.size(): 0; }
   uint           getLength() const;
   uint           getTransformedSize(const uint mode, const uint submode) const;
   double         getTotalDuration()
   {
      if(m_onsets.size() == 0) return 0;
      return m_onsets[m_onsets.size()-1] - m_onsets[0];
   }
   durations         getDurations() const;
   durations         getTickDurations() const { return m_timeLine.getTickDurations();}
   data              getDurationsAsDoubles() const;
   vector<uint>      getDurationModus(uint &modFreq) const;
   vector<MIDIPitch> getPitchModus(uint &modFreq) const;
   
   double         getMeanPitch() const;
   double         getMeanPitch(const PitchSequence& pitches) const;
   data           getSyncopes(const int level) const;
   data           getOptiAccentsLinear(bool bThresholded = false) const;
   data           getOptiAccentsTree(bool bThresholded = false) const;
   Melody         normalize() const;
   Melody         stretch(uint duration) const;
   data           contourize(uint mode) const ;
   uint           writeNormalization(uint contourMode);
   uint           getHuronCode(const int i1, const int i2, bool bAggregated) const;
   uint           getHuronContour(bool bAggregated = false) const;
   Melody         intervals() const ;
   Melody&        transpose(const char semitone);
   Melody         getPhrase(const uint n) const;
   uint           getPhraseCount() const;
   MelodyList     getPhrases() const;
   double         similarity(const Melody& melody, const SimType& simTyoe, double &tmp )const;
   double         similarity(const Melody& melody, const CombiType& combiType, double &tmp)const;
   double         phraseSimilarity(const Melody& melody, const CombiType& combiType)const;
   data           getFeature(const FeatureDef &feature) const;

   void           clear()         { m_pitches.clear(); m_durations.clear(); m_onsets.clear(); m_timeLine.clear(); }
   void           insert(uint idx, MIDIPitch pitch, uint dur);
   void           append(MIDIPitch pitch, uint dur, uint count = 1);
   void           zeroPad(uint newLen, uint dur = 1);
   void           setRandom(const uint length, const double density, const MIDIPitch min, const MIDIPitch max);
   void           printInfo(ostream& out = std::cout) const;
   void           printMelody(ostream& out = std::cout) const;
   void           printRatio(ostream& out, const uint count, const uint denom) const;
   list<char>     getTranspositionHints(const Melody& melody);
   char           getBestTransposition(const Melody& melody);
   Signature&     getSignature(){ return m_signature;}
   void           setSignature(Signature sig){ m_signature = sig;}
   //BarTimeline&   getTimeLine(){ return m_timeLine;}
   void           printMelodyNames(ostream& out = std::cout) const { printMelodyNames(0, m_pitches.size(), out);} 
   void           printMelodyNames(uint start, uint end, ostream& out = std::cout) const;

   uint           getBarCount() const;
   uint           getNonEmptyBarCount() const;
   PitchSequence  getBar(const uint ndx) const;
   PitchSequence  getBars(const uint iStart, const uint nCount) const;
   data           getRawTonality(const uint barNdx, const uint mode, const bool dump = false) const;
   uint           getRawTonalityKrumhansl(const int barNdx, data& vec, const bool dump = false) const;
   data           getTonicsKrumhansl() const;
   bool           empty(){ return m_pitches.empty();}
   MIDIPitch      getPitch(const BarPosition& pos)const;
   double         compareBeats(const Melody& mel, const vector<uint>& beats, const uint tonality1, const uint tonality2, const uint mode);
   data           getTransformation(const uint mode)const;
   vector< pair<double, uint> > getAsOnsetPitchPairs() const;
protected:
   PitchSequence  m_pitches;
   durations      m_durations;
   data           m_onsets;
   iData          m_phrases;
   string         m_sName;
   Signature      m_signature;
   uint           m_annotatedKey;
   BarTimeline    m_timeLine;
   ostream*       m_pOut;
   uint           m_quantLevel;
   double         correlation(const data& pitches1,const data& pitches2, const uint MODE)const;
   double         getMaxCorrelation(const data& shortMel, const data& longMel, uint leftOffset, 
                                    double &tmp, const uint mode, const uint submode )const;

   void           getContourPoints(indizes& idx, PitchSequence& extrema,
                                    const PitchSequence &pitches, 
                                    const eContourMode contMode, 
                                    const bool bUseEndpoints) const;

   void           getContourPoints(indizes& idx, PitchSequence& extrema, 
                                    const eContourMode mode, 
                                    const bool bUseEndpoints = true, 
                                    const bool bRespectPhrases = false) const;


   double         getFuzzyVal(double val, INTERVALFEATURE ivalF) const;
   data           getFuzzyFeature(const double interval) const;
   durations      fuzzyRhythm(const durations &dur, const uint modus)const;
   double         fuzzyCorrelation(const data& pitches1,const data& pitches2)const;
   double         getFuzzyFeatureCorr(const data& feat1, const data& feat2)const;

   double         fuzzyRhythmCorr(Melody& mel, double & tmp, const uint mode, const uint submode)const;  

   int            getTonalityIdx(const int pitchClass, const int trans) const;
   void           countSigns(const PitchSequence& melody, vector<uint>& nFlats, vector<uint>& nSharps)const;
   int            getMajorBaseTone(const PitchSequence& melody);
   double         getHarmCorr(const Melody &mel1, const Melody &mel2, uint bar, uint mode);
   double         getHarmCorrKrumhansl(const Melody &mel, const uint mode, const uint submode);
   double         harmonicCorrelation(const Melody& mel, const uint mode, const uint submode, double &tmp);
   iData          getKeys(uint mode) const;
   string         getKeyName(const uint maxIdx) const;
   void           setAnnotatedKey(const uint key)  { m_annotatedKey = key;}
   data           realPS(const data& vec, data& ps, uint start, uint end, double factor);
   data           realFFT(data& dat, int isign);
   data           realFT(data& dat, int isign, data &ps)const;

   data           getRanks() const;
   data           getIntervals(bool bAbs, uint mode) const;
   data           getBarTimeAsPhases(const uint mode) const;
   data           getTonalPitchesAsPhases(const uint mode) const;

public:

   void           setPhraseBoundaries(){ if (m_phrases.empty()) m_phrases = getPhraseBoundaries();}
   iData          getPhraseBoundaries() const;
   uint           findPhraseEnd(uint pos) const;
   uint           findPhraseBegin(uint pos) const;
   
   void           setAccent(data &accents, const uint rules, const uint flag, const eAccentPos pos, const double acc) const;
   dataVec        getAccents(const uint rules, const double baseAcc, const double acc) const;
   double         getAccentSimilarity(const Melody& mel, const uint type, const uint rules, 
                                      const double baseAcc = 0.0, const double acc = 1.0) const;
   data           getAccentFeature(const uint type, const uint rules, const double baseAcc = 0.0, const double acc = 1.0) const;
   data           getJointAccents(const dataVec& accents) const;
   data           getJointAccents(const dataVec& accents, const data& weights) const;
   data           getAccentVector(const dataVec& accents, const uint accentType) const;
   double         getAccentCoherence(const uint rules, const double baseAcc, const double acc,  const uint mode) const;
   indizes        getAccentTypePositions(const uint rules) const;
   data           getThomassenAccents(const uint mode) const;
   double         getThomassenWeight( const uint v1, const uint v2, const uint v3, const uint type, const uint mode) const;

   double         getMeanAccentCorrelation(dataVec accents, const uint rules, const uint mode ) const;

   double         getNgramComplexity(const uint mode, const uint maxNgramLen, uint &maxLen) const;
   data           getJensenShannonDivergence(const data& vec, double& max, uint &idx) const;


   uint           getLeftOffset();
   double         absNorm(const data& pitches1,const data& pitches2);
   double         absNormNorm(const data& pitches1,const data& pitches2, const uint mode) const;
   PitchSequence  getRandomMelody(const uint length, const double density, const MIDIPitch min, const MIDIPitch max);

   void           scaleTimeLine(double factor);
   void           normalizeTimeLine();
   double         guessOnsetUnit() const;
   double         getZipfComplexity(const iData& vec) const;
};
typedef pair<uint, double> tonalityEntry;
typedef vector<tonalityEntry> tonalityVector;

bool tonEntryComp(const tonalityEntry& e1,const tonalityEntry& e2);
tonalityVector sortTonality(const tonalityVector& vec);
void  dumpTonVec(const char* header, const tonalityVector& vec, ostream& out = std::cout);
#endif
