/***************************************************************************
                          MIDIWriter.cpp  -  description
                             -------------------
    begin                : Die Feb 8 2005
    copyright            : (C) 2005 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 "MIDIWriter.h"
#include "jdkmidi/msg.h"


MIDIWriter::MIDIWriter(const string& fileName, const Melody* pMelody, const uchar chan, const uchar GMInst):
   m_pWriter(0),
   m_pStream(0),
   m_sFileName(fileName),
   m_pMel(pMelody),
   m_nChannel (chan),
   m_nGMInst(GMInst),
   m_sErrText("")
{
   init();
}
   
MIDIWriter::~MIDIWriter()
{
   cleanUp();
}

bool MIDIWriter::init()
{
   m_pStream = new MIDIFileWriteStreamFileName( m_sFileName.c_str() );
   if(!m_pStream->IsValid()){
      cleanUp();
      m_sErrText = string("Could not open ") + m_sFileName;
      return false;
   }
   m_pWriter = new MIDIFileWrite(m_pStream);
   if(!m_pWriter){
      m_sErrText = string("Out of memory");
      cleanUp();
      return false;
   }
   
   return true;
}

void  MIDIWriter::cleanUp()
{
   if(m_pWriter){
      delete m_pWriter;
      m_pWriter = 0;
   }
   if(m_pStream){
      delete m_pStream;
      m_pStream = 0;
   }
   
}

bool MIDIWriter::write()
{
   bool bRet = false;
   if(!m_pWriter){
      return bRet;
   }
   if(!writeHeader()){
      return bRet;
   }
   if(!writeSignatureTrack()){
      return bRet;
   }
   if(writeTrack()){
      bRet = true;
   }
   return bRet;
}

bool MIDIWriter::writeHeader()
{
   bool bRet = false;
   if(!m_pWriter){
      return bRet;
   }
   uint division = m_pMel->getTicks();
   m_pWriter->WriteFileHeader(1, 2, division);
   if(m_pWriter->ErrorOccurred()){
      m_sErrText = "Error writing file header";
   }
   else bRet = true;
   return bRet;
}

int MIDIWriter::signs[] = { 0, -5, +2, -3, +4, -1, +6, +2, -4, +3, -2, +5, -3, +4, -1, +6, +2, -4, +3, -2, +5, 0, -5, +2};

bool MIDIWriter::writeSignatureTrack()
{
   bool bRet = false;

   if(!m_pWriter){
      return bRet;
   }
   
   uint key = m_pMel->getKey();
   char sf  = (char)MIDIWriter::signs[key];
   char mi  = key > 11 ? 1 : 0;
   Signature sig = m_pMel->getSignature();
   uint num    = sig.count();
   uchar denom  = (uchar)floor(log((double)sig.denom())/log(2.0));
   long tempo  = (long)floor(m_pMel->getTempo() * 1000); //usecs;

   m_pWriter->WriteTrackHeader(0);
   
   m_pWriter->WriteKeySignature(0, sf, (uchar)mi);
   m_pWriter->WriteTimeSignature(0, (uchar)num, (uchar)denom, 24, 8);
   m_pWriter->WriteTempo(0, tempo);
   m_pWriter->WriteEndOfTrack(0);
   m_pWriter->RewriteTrackLength();


   if(m_pWriter->ErrorOccurred()){
      m_sErrText = "Error writing signature track";
   }
   else bRet = true;

   return bRet;

}

bool MIDIWriter::writeTrack()
{
   bool bRet = false;
   if(!m_pWriter){
      return bRet;
   }
   if(!m_pMel){
      m_sErrText = "No melody. ";
      return bRet;
   }
   iData pitches     = m_pMel->getPitches();
   iData durations   = m_pMel->getDurationTicks();
   iData iois        = m_pMel->getIOITicks();

   if(pitches.size() == 0 || pitches.size() != durations.size()){
      m_sErrText = "Melody invalid.";
      return bRet;
   }
   m_pWriter->WriteTrackHeader(0);

   uchar vel = 127;
   uint deltaTime = m_pMel->getStartTicks();
   MIDITimedMessage msg;
   msg.SetTime(0);
   msg.SetProgramChange((uchar)m_nChannel, (uchar)m_nGMInst);
   m_pWriter->WriteEvent(msg);

   msg.SetControlChange(m_nChannel, 7, 80);
   m_pWriter->WriteEvent(msg);
   
   for(uint i = 0; i<pitches.size(); ++i){
      if(pitches[i] == 0 || pitches[i] > 127){
         goto cont;
      }
      msg.SetTime(deltaTime);
      msg.SetNoteOn(m_nChannel, (unsigned char)pitches[i], vel);
      m_pWriter->WriteEvent(msg);
      msg.SetTime(deltaTime+durations[i]);
      msg.SetNoteOn(m_nChannel, (unsigned char)pitches[i], 0);
      m_pWriter->WriteEvent(msg);
cont:
      if(i < pitches.size()-1){
         deltaTime +=iois[i];
      }
   }
   m_pWriter->WriteEndOfTrack(0);
   
   m_pWriter->RewriteTrackLength();
   bRet = true;
   return bRet;

}
