#include "Types.h"
#include "chords.h"
using namespace std;

void ThirdsTree::Add(ThirdsTree *pNode, eThirdType type)
{
   if(!pNode){ return;}
   switch(type)
   {
      case eThirdType_minor:
         m_minor = pNode;
         m_minorLen = 1+ pNode->m_maxLength; 
         break;
      case eThirdType_major:
         m_major = pNode;
         m_majorLen = 1+ pNode->m_maxLength; 
         break;
      default:
         break;
   }
   m_maxLength =  m_majorLen > m_minorLen ? m_majorLen : m_minorLen;
}

uint ThirdsTree::GetCount()const
{
   uint sum = 0;
   if(m_minor){
      sum += m_minor->GetCount();
   }
   if(m_major){
      sum += m_major->GetCount();
   }
   return sum;
}

stringlist ThirdsTree::GetLongestSubTrees() const
{
   return GetSubTrees(m_maxLength);
}
stringlist ThirdsTree::GetSubTrees(const uint len) const
{
   stringlist ret;
   stringlist tmp = ToStringList();
   for(uint i = 0; i<tmp.size(); ++i){
      //cout<<tmp[i].size()<<" "<<m_maxLength-1<<endl;
      if(tmp[i].size() == len) ret.push_back(tmp[i]);
   }
   return ret;
}

stringlist ThirdsTree::ToStringList() const
{
   vector<string> ret;
   if(m_minor){
      vector<string> left = m_minor->ToStringList();
      for(uint i = 0; i<left.size(); ++i){
         left[i] = string("3") + left[i];
      }
      ret.insert(ret.end(), left.begin(), left.end()); 
   }
   if(m_major){
      vector<string> right = m_major->ToStringList();
      for(uint i = 0; i<right.size(); ++i){
         right[i] = string("4") + right[i];
      }
      ret.insert(ret.end(), right.begin(), right.end()); 
   }
   if(!m_major && ! m_minor){
      ret.push_back(string(""));
   }
   return ret;
}

string ThirdsTree::ToString() const
{
   string ret;
   stringlist list = ToStringList();
   for(uint i = 0; i <list.size(); ++i){
      ret +=list[i]+string(";");
   }
   return ret;
}

void PitchSetPreprocessor::Init(const PitchSet& ps)
{
   m_weight       =1;
   m_maxWeight    = 2;
   m_wps.clear();
   for(uint i = 0; i<ps.size(); ++i){
      bool bFound = false;
      for(uint j = 0; j<m_wps.size(); ++j){
         if(ps[i] == m_wps[j].first && m_wps[j].second<m_maxWeight){
            m_wps[j].second += m_weight;
            bFound = true;
            break;
         }
      }
      if(!bFound){
         m_wps.push_back(pair<int, double>(ps[i], m_weight));
      }
   }

}
string PitchSetPreprocessor::ToString()
{
   string ret;
   for(uint i = 0; i<m_wps.size(); ++i){
      char buf[128];
      sprintf(buf, "Pitch: %d, Weight: %f\n", m_wps[i].first, m_wps[i].second); 
      ret += string(buf);
   }
   return ret;
}

void ChordsAnalyzer::setCollection(const WeightedPitchSet& pitches)
{
   clearCollection();
   m_bass = 128;
   for(uint i = 0; i<pitches.size(); ++i){
      uint pc = pitches[i].first % 12;
      uint octave = 1<<(pitches[i].first / 12);
      m_collection[pc] |= octave;
      if(pitches[i].first<=m_bass) m_bass = pc;
   }
}

ChordList ChordsAnalyzer::Analyze(const WeightedPitchSet& wps)
{
   ChordsAnalyzerResult res ;
   ChordList list;
   setCollection(wps);
   uint maxLen = 0;
   vector< pair<ThirdsTree*, uint> > thirdChains;
   for(uint i = 0; i<12; ++i){
      ThirdsTree* pTree = getThirdsChains(m_collection, i);
      if(pTree){
         string buf = pTree->ToString();
         uint tmp = pTree->GetMaxLength();
         cout<<"Pitchclass "<<i<<endl;
         cout<<buf<<endl;
         cout<<"Max len = "<<tmp<<endl;
         if(tmp>maxLen) maxLen = tmp;
         thirdChains.push_back(pair<ThirdsTree*, uint>(pTree, i));
      }
   }
   stringlist finalList;
   for(uint i = 0; i<thirdChains.size(); ++i){
      if(thirdChains[i].first->GetMaxLength() < maxLen) continue;
      stringlist tmp = thirdChains[i].first->GetLongestSubTrees();
      finalList.insert(finalList.end(), tmp.begin(), tmp.end()); 
      for(uint j = 0; j<tmp.size(); ++j){
         ChordsAnalyzerResult res;
         PitchSet extensions = getUpperStructure(tmp[j], thirdChains[i].second);
         res.Init(tmp[j], extensions, thirdChains[i].second, m_bass, 1);
         list.push_back(res);
      }
   }
   for(uint i = 0; i< finalList.size(); ++i){
      cout<<finalList[i].c_str()<<endl;
   }
   for(uint i = 0; i< list.size(); ++i){
      string sChord = list[i].ToString();
      cout<<sChord.c_str()<<endl;
   }
   return list;
}

ThirdsTree* ChordsAnalyzer::getThirdsChains(uint collection[12], uint basePitch)
{
   ThirdsTree* pTree = 0;
   uint pc = basePitch % 12;
   uint minor3 = (pc + 3) % 12;
   uint major3 = (pc + 4) % 12;
   /*
   cout<<"Check Node ("<<pc<<","<<collection[pc]<<")"<<endl;
   for(uint i = 0; i<12; ++i){
      cout<<i<<":"<<collection[i]<<" ";
   }
   cout<<endl;
   */
   if(collection[pc] == 0) return pTree;
   //cout<<"Found"<<endl;
   pTree = new ThirdsTree(pc, collection[pc]);
   if(!pTree) return 0;

   if(collection[minor3]>0){
      //cout<<"Check "<<minor3<<endl;
      uint colminor[12];
      memcpy(colminor, collection, 12*sizeof(uint));
      colminor[pc] = 0;
      ThirdsTree *pMinTree = getThirdsChains(colminor, minor3);
      pTree->Add(pMinTree, eThirdType_minor);
   }
   if(collection[major3]>0){
      //cout<<"Check "<<major3<<endl;
      uint colmajor[12];
      memcpy(colmajor, collection, 12*sizeof(uint));
      colmajor[pc] = 0;
      ThirdsTree *pMajTree = getThirdsChains(colmajor, major3);
      pTree->Add(pMajTree, eThirdType_major);
   }
   return pTree;
}

PitchSet ChordsAnalyzer::getUpperStructure(const string& thirds, const uint basePitch)
{
   PitchSet ret;
   list<uint> ext;
   uint tmp[12];
   uint start = basePitch % 12;
   memcpy(tmp, m_collection, 12*sizeof(uint));
   tmp[start] = 0;

   for(uint i = 0; i<3; ++i){
      start += thirds[i] - '0';
      uint pc = start % 12;
      tmp[pc] = 0;
   }

   for(uint i = 0; i<12; ++i){
      if(tmp[i] != 0){
         int pc = i - basePitch;
         if(pc<0) pc += 12;
         //pc = pc % 12;
         ext.push_back(pc);
      }
   }
   ext.sort();
   for(list<uint>::iterator iter = ext.begin(); iter != ext.end(); ++iter){
      uint pc = *iter % 12;
      ret.push_back(pc);
   }  
   return ret;
}
PitchSet ChordsAnalyzer::getChordNotes(const string& thirds, const uint basePitch)
{
   PitchSet ret;
   list<uint> ext;
   uint start = basePitch % 12;

   for(uint i = 0; i<3; ++i){
      start += thirds[i] - '0';
      uint pc = start % 12;
      ext.push_back(pc);
   }

   ext.sort();
   for(list<uint>::iterator iter = ext.begin(); iter != ext.end(); ++iter){
      uint pc = *iter % 12;
      ret.push_back(pc);
   }  
   return ret;
}


eChordGender  ChordsAnalyzerResult::getChordGender()
{
   eChordGender ret = eChordGender_unknown;
   string tmp = m_sThirdsChain.substr(0,3);
   switch(tmp.size()){
      case 0:
         ret = eChordGender_nc;
      break;
      case 1:
         if(tmp[0] == '1') ret = eChordGender_1;
         if(tmp[0] == '2') ret = eChordGender_2;
         if(tmp[0] == '3') ret = eChordGender_3;
         if(tmp[0] == '4') ret = eChordGender_4;
         if(tmp[0] == '5') ret = eChordGender_5;
         if(tmp[0] == '6') ret = eChordGender_6;
         if(tmp[0] == '7') ret = eChordGender_7;
      break;
      case 2:
         if(tmp[0] == '3'){
            if(tmp[0] == '3') ret = eChordGender_dim;
            if(tmp[0] == '4') ret = eChordGender_min;
         }
         if(tmp[0] == '4'){
            if(tmp[0] == '3') ret = eChordGender_maj;
            if(tmp[0] == '4') ret = eChordGender_aug;
         }
         break;
      case 3:
         if(tmp == "333") ret = eChordGender_fulldim;
         if(tmp == "334") ret = eChordGender_halfdim;
         if(tmp == "343") ret = eChordGender_min7;
         if(tmp == "433") ret = eChordGender_dom7;
         if(tmp == "434") ret = eChordGender_maj7;
         if(tmp == "443") ret = eChordGender_augmaj7;
         if(tmp == "344") ret = eChordGender_minmaj7;
         if(tmp == "444") ret = eChordGender_maj;
         break;
      default:
      break;
   }
   return ret;
}
static const char* genToneNames[] = { "C", "C#", "D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B"};
static const char* genChordNames[] = { 
   "Unknown",  //eChordGender_unknown 
   "N.C.",       //eChordGender_nc 
   "Unknown",  //eChordGender_1, 
   "Unknown",  //eChordGender_2, 
   "-",        //eChordGender_3, 
   "",         //eChordGender_4, 
   "sus4",     //eChordGender_5, 
   "7",        //eChordGender_6,    
   "5",        //eChordGender_7, 
   "",         //eChordGender_maj, 
   "-",        //eChordGender_min, 
   "o",        //eChordGender_dim, 
   "+",        //eChordGender_aug, 
   "maj7",     //eChordGender_maj7, 
   "7",        //eChordGender_dom7, 
   "-7",       //eChordGender_min7, 
   "-maj7",    //eChordGender_minmaj7, 
   "-7b5",     //eChordGender_halfdim, 
   "o7",       //eChordGender_fulldim, 
   "+7",       //eChordGender_aug7, 
   "+maj7",    //eChordGender_augmaj7, 
   "",         //eChordGender_dimmaj7, 
   "7sus4",    //eChordGender_fourths, 
   "sus2",     //eChordGender_fifths, 
};
static const char* genExtNames[] = { "", "b9", "9", "#9", "10", "11", "#11", "", "b13", "13", "7", "maj7"};

string ChordsAnalyzerResult::ToString()
{
   string ret;
   string root = string(genToneNames[m_root]);
   eChordGender gender = getChordGender();
   ret = root + string(genChordNames[gender]);
   if(m_extensions.size()!=0) ret +="/";
   for(uint i = 0; i<m_extensions.size(); ++i){
      ret += string(genExtNames[m_extensions[i]]);
      if(i!=m_extensions.size()-1) ret +=string("/");
   }
   return ret;
}