/***************************************************************************
                          CSVIO.cpp  -  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.                                   *
 *                                                                         *
 ***************************************************************************/
#include "types.h"
#include "FileReader.h"
#include "Melody.h"
#include "MIDITime.h"
#include "CSV.h"
#include "BarTime.h"
#include "Segmenter.h"

bool CSVIO::read(const string& inFile, Melody** ppMelody )
{
   if(!ppMelody || !m_pParam) {
      setErrorText("Internal error: Invalid pointer");
      return false;
   }
   *ppMelody = new Melody;
   if(!*ppMelody){
      setErrorText("Out of memory");
      return false;
   }
   CSVReader reader(inFile, *ppMelody);
   if(!reader.read()){
      setErrorText(reader.getErrText());
      return false;
   }
   if((*ppMelody)->size() == 0){
      setErrorText("File empty");
      return false;
   }
   return true;
}

void substitute(char *str, char ch1, char ch2)
{
   for(uint i = 0; i<strlen(str); ++i){
      if(str[i] == ch1) str[i] = ch2;
   }
}
bool CSVIO::write(const string& outFile, const Melody* pMelody)
{
   if(!pMelody) return false;

   char        buf[1024]   = "";
   bool        bRet        = false;
   double      mspertick   = pMelody->getMsPerTick();
   bool        hasUpBeat   = pMelody->getFirstOneIdx() == 0 ? false : true;
   MIDITime    midiTime;
   Signature   sig         = pMelody->getSignature();
   uint  ticksPerBar = pMelody->getTicksPerBar();
   dData durs        = pMelody->getDurations();
   iData pitches     = pMelody->getPitches();
   dData onsets      = pMelody->getOnsets();
   iData ioitics     = pMelody->getIOITicks();
   iData onsetics    = pMelody->getOnsetTicks();
   iData durtics     = pMelody->getDurationTicks();
   iData dur16       = pMelody->getDurationsInUnits();
   BarTimeline bars  = pMelody->getBarPositions();
   string segHeader, segVals;
   if(m_pParam->getWriteSegmentation()){
      fillBoundaryList(pMelody);
   }
   FILE *fp = fopen(outFile.c_str(), "w");
   if(!fp){
      setErrorText("Could not open file");
      return bRet;
   }

   fprintf(fp, "Signature: %d/%d, Ticks per Beat: %d, Annotated Key: %d\n", sig.count(), sig.denom(), sig.ticks(), pMelody->getKey());
   if(ferror(fp)) goto out;
   if(m_pParam->getWriteSegmentation()){
      segHeader = getBoundaryHeader();
      fprintf(fp, "onset;onsetics;takt;beat;ticks;pitch;durs;durtic;dur16;%s\n", segHeader.c_str());
   }
   else{
      fprintf(fp, "onset;onsetics;takt;beat;ticks;pitch;durs;durtic;dur16;\n");
   }
   if(ferror(fp)) goto out;

   midiTime.set(0, 0, 0, sig.count(), sig.ticks(), (uint)floor(pMelody->getTempo()*1e3), 0, 0);

   if(hasUpBeat){
      uint idx = pMelody->getFirstOneIdx();
      if(idx<pMelody->size()){ 
         uint ticksOfFirstBar = (uint)floor(pMelody->at(idx).getTime() / mspertick);
         ticksPerBar -= ticksOfFirstBar;
      }
   }

   midiTime.set(ticksPerBar);

   for(uint i = 0; !feof(fp) && i < pMelody->size(); ++i){
      if(pitches[i] == 0){
         if(i!= pMelody->size()-1){
            midiTime += ioitics[i];
         }
         continue;
      }
      uint bar       = midiTime.getMeasure();
      uint beat      = midiTime.getBeat()+1;
      uint ticks     = midiTime.getTicks();
      BarPosition   pos = bars[i];
      if( pos != invalidBarPosition){
         bar   = pos.getBar();
         beat  = pos.getBeat();
         ticks = pos.getTicks();
      }      
      if(m_pParam->getWriteSegmentation()){
         segVals = getBoundaryValues(i);
         sprintf(buf,"%.4f;%d;%d;%d;%d;%d;%.4f;%d;%d;%s\n", onsets[i]/1000., onsetics[i], bar, beat,ticks , pitches[i], durs[i]/1000., durtics[i], dur16[i], segVals.c_str());
      }
      else{
         sprintf(buf,"%.4f;%d;%d;%d;%d;%d;%.4f;%d;%d;\n",
            onsets[i]/1000., onsetics[i], bar, beat,ticks , pitches[i], durs[i]/1000., durtics[i], dur16[i]);
      }
      substitute(buf, '.', ',');
      fputs(buf, fp);
      if(ferror(fp)) goto out;
      if(i != pMelody->size()-1){
         midiTime += ioitics[i];
      }
   }

   bRet = true;

out:
   if(!bRet)
      setErrorText("I/O error during write");
   fclose(fp);
   return bRet;

}

string CSVIO::getBoundaryHeader()
{
   string header("");
   for(uint i = 0; i<m_boundHeaders.size(); ++i){
      header += m_boundHeaders[i] + string(";");
   }
   if(header.length()){
      header = header.substr(0, header.length()-1);
   }
   return header;
}

string CSVIO::getBoundaryValues(const uint idx)
{
   string values("");
   for(uint i = 0; i<m_boundaryList.size(); ++i){
      char buf[1024] = "";
      Vector bound = m_boundaryList[i];
      double val = 0;
      if(!bound.empty()){
         if(idx>bound.size()-1){
            val = 1.0;
         }
         else val = bound[idx];
      }
      sprintf(buf, "%0.0f;", val);
      values += string(buf);
   }
   if(values.length()){
      values = values.substr(0, values.length()-1);
   }

   return values;
}

void  CSVIO::fillBoundaryList(const Melody* pMel)
{
   if(!pMel || pMel->empty()) return;

   PlainLBDM      segmenter(pMel, LBDMBase::iois);
   RefinedLBDM    segmenter2(pMel, LBDMBase::iois);
   Vector segments1 = segmenter.analyze();
   //segments1.dump();
   
   segmenter.setIntervalType(LBDMBase::pitches);
   Vector segments2 =  segmenter.analyze();
   //segments2.dump();
   segments1 += segments2;
 
   segmenter.setIntervalType(LBDMBase::rests);
   segments2 =  segmenter.analyze();
   //segments2.dump();
   segments1 += segments2;

   //segments1.dump();
   Vector bounds = segmenter.getBoundaries(segments1);
   //cout<<"First order local boundaries (plain LDBM, iois, pitches, rests)"<<endl;  
   //bounds.dump();
   Vector bounds2 = segmenter.getSecondBoundaries(segments1);
   //cout<<"Second order local boundaries (plain  LDBM, iois, pitches, rests)"<<endl;  
   //bounds2.dump();
   addBoundaryToList(bounds, "LBDM1");
   addBoundaryToList(bounds2, "LBDM2");
   segments1 = segmenter2.analyze();
   //segments1.dump();
   segmenter2.setIntervalType(LBDMBase::pitches);
   segments2 =  segmenter2.analyze();
   //segments2.dump();
   segments1 += segments2;
   segmenter2.setIntervalType(LBDMBase::rests);
   segments2 =  segmenter2.analyze();
   //segments2.dump();
   segments1 += segments2;
   //segments1.dump();
   bounds = segmenter2.getBoundaries(segments1);
   //cout<<"First order local boundaries (refined LDBM, iois, pitches, rests)"<<endl;  
   //bounds.dump();
   bounds2 = segmenter.getSecondBoundaries(segments1);
   //cout<<"Second order local boundaries (refined LDBM, iois, pitches, rests)"<<endl;  
   //bounds2.dump();
   addBoundaryToList(bounds, "refLBDM1");
   addBoundaryToList(bounds2, "refLBDM2");

   TemperleySegmenter segmenter3(pMel);
   segments1 = segmenter3.analyze();
   addBoundaryToList(segments1, "temperley");

   //cout<<"Temperley boundaries."<<endl;
   //segments1.dump();
   SimpleSegmenter simple(pMel);
   SimpleSegmenter::Parameters simpleParams;
   for(uint i = 0; i<2;++i){
      if(i == 0) simpleParams.m_bRunningModus = false;
      else simpleParams.m_bRunningModus  = true;
      for(uint j = 0; j<5; ++j){
         simpleParams.m_gapTime = 1000 + 500*j; 
         for(uint k = 0; k<5; ++k){
            simpleParams.m_modusFactor = k + 1.9; 
            simple.setParameters(simpleParams);
            segments1 = simple.analyze();
            //segments1.dump("SimpleSegmenter Phrases");
            char name[256] ="";
            sprintf(name, "simple%d-%d-%d", i, j, k);
            addBoundaryToList(segments1, name);
         }
      }
   }
}

