/***************************************************************************
                          MIDIFileAnalyzer.cpp  -  description
                             -------------------
    begin                : Don Feb 3 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 "jdkmidi/world.h"
#include "jdkmidi/fileread.h"
#include "jdkmidi/msg.h"

#include "types.h"
#include "MIDIFileAnalyzer.h"
#define UNUSED(x) (x) = (x);

      
void  TrackInfo::dump()
{
   cout<<"***Track Nr:"<<m_nTrkNr<<" (Name: "<<m_sTrackName.c_str()<<", Inst.: "<<m_sInstName.c_str()<<")"<<endl;
   cout<<"   - has "<<m_nNoteOns<<" notes"<<endl;

   cout<<"   - has "<<m_nTimeSigEvents<<" time signature event(s)"<<endl;
   
   cout<<"   - has "<<m_nTempoEvents<<" tempo event(s)"<<endl;
   cout<<"   - has "<<m_nKeySigEvents<<" key signature event"<<endl;
   if(m_nChNr==-2){
      cout<<"   - has channel switch"<<endl;
   }
   else{
      if(m_nChNr==-1){
         cout<<"   - has no channel"<<endl;
      }
      else{
         cout<<"   - uses channel "<<m_nChNr<<endl;
      }
   }
   if(hasNotes()){
      if(m_bPolyphonic){
         cout<<"   - is polyphonic"<<endl;
      }
      else{
         cout<<"   - is monophonic"<<endl;
      }
   }
   if(isUsable()){
      cout<<"   - is usable"<<endl;
   }
   cout<<endl;
}

bool MIDIFileAnalyzer::analyze()
{
   if(m_trackInfos.size() == 0 ){
      m_sErrText = "No tracks";
      return false;
   }
   uint nKeySigs = 0, nTimeSigs = 0, nTempos = 0, nSigTrks = 0;
   
   for(uint i = 0; i<m_trackInfos.size(); ++i){
      if(m_trackInfos[i].isSignatureTrack()){
         if(m_nSigTrkNum == nopos)
            m_nSigTrkNum = i;
         nSigTrks++;
      }
      if(m_trackInfos[i].hasKeySigEvent()){
         nKeySigs++;
      }
      if(m_trackInfos[i].hasTimeSigEvent()){
         nTimeSigs++;
      }
      if(m_trackInfos[i].hasTempoEvent()){
         nTempos++;
      }
      if(m_trackInfos[i].isUsable()){
         m_usableTracks.push_back(m_trackInfos[i]);
      }
      
   }
   m_bIsWellFormed = (m_usableTracks.size()>0);

   cout<<"Found "<<nSigTrks<<" signature track(s)"<<endl;   
   cout<<"Using signature track "<<(int)m_nSigTrkNum<<endl;
   cout<<"Found "<<nKeySigs<<" key signature(s)"<<endl;
   cout<<"Found "<<nTimeSigs<<" time signature(s)"<<endl;
   cout<<"Found "<<nTempos<<" tempo indication(s)"<<endl;
   cout<<"Found "<<(uint)m_usableTracks.size()<<" usable tracks"<<endl;
   if(m_bIsWellFormed){
      cout<<"File is well-formed"<<endl;
   }
   else{
      cout<<"File is NOT well-formed"<<endl;
   }
   return true;
}

int MIDIFileAnalyzer::selectTrack()
{
   char buf[1024]="";
   if(m_usableTracks.size()==1){
      return m_usableTracks[0].getTrackNr();
   }
   cout<<"Found multiple tracks in file!"<<endl;
   cout<<"Please select"<<endl;
   cout<<"      Track Nr. TrackName    TrackInstrument"<<endl;

   for(uint i = 0;i<m_usableTracks.size(); ++i){
      cout<<"   "<<i+1<<"  "<<m_usableTracks[i].getTrackNr()<<"            ";
      cout<<m_usableTracks[i].getTrackName()<<"            ";
      cout<<m_usableTracks[i].getInstName()<<"            "<<endl;
   }

   cout<<endl;
   cout<<"Select number."<<endl;
   cin>>buf;
   uint selection = atoi(buf);
   
   if(selection == 0){
      cout<<"No track selected."<<endl;
      return -1;
   }
   for(uint i = 0;i<m_usableTracks.size(); ++i){
      if(selection == i+1)
         return m_usableTracks[i].getTrackNr();
   }
   return -1;
}
uint MIDIFileAnalyzer::getMaxTrackNr()
{
   uint maxTrkNr = 0;
   for(uint i = 0;i<m_usableTracks.size();++i){
      if(m_usableTracks[i].getTrackNr()>maxTrkNr){
         maxTrkNr = m_usableTracks[i].getTrackNr();
      }
   }
   return maxTrkNr;
}

void MIDIFileAnalyzer::mf_note_on( const MIDITimedMessage &msg )
{
   m_curTime = msg.GetTime();
   m_trackInfos[m_nCurTrk].countNoteOns();
   if(m_state == noteon){
      m_state = noteonTwin;
   }
   else if(m_state == noteonTwin){
      m_trackInfos[m_nCurTrk].setPolyphonic();
   }
   else{
      m_state = noteon;
   }
   int ch = (int)msg.GetChannel();
   if(m_trackInfos[m_nCurTrk].getChannel() == -1){
      m_trackInfos[m_nCurTrk].setChannel(ch); //first define
      return;
   }
   if(m_trackInfos[m_nCurTrk].getChannel() == -2){
      return; //channel switch alread found
   }
   if(m_trackInfos[m_nCurTrk].getChannel() != ch){
      m_trackInfos[m_nCurTrk].setChannel(-2);//channel switch
   }

}

void MIDIFileAnalyzer::mf_note_off( const MIDITimedMessage &msg )
{
   if(m_state == noteonTwin){
      if((uint)abs((int)msg.GetTime() - (int)m_curTime)<m_nDivision/6){
         m_state = noteon;
      }
   }
   else {
      m_state = noteoff;
   }
   m_trackInfos[m_nCurTrk].countNoteOffs();
}

void MIDIFileAnalyzer::mf_timesig(
      MIDIClockTime time,
      int num,
      int denom_power,
      int midi_clocks_per_metronome,
      int notated_32nds_per_midi_quarter_note )

{
   time = time;
   num = num;
   denom_power = denom_power;
   midi_clocks_per_metronome = midi_clocks_per_metronome;
   notated_32nds_per_midi_quarter_note = notated_32nds_per_midi_quarter_note;
   m_trackInfos[m_nCurTrk].countTimeSigEvent();   
}

void MIDIFileAnalyzer::mf_tempo( MIDIClockTime time, unsigned long tempo )
{
   cout<<" time = "<<time<<" tempo = "<<60000000/tempo<<endl;;
   m_trackInfos[m_nCurTrk].countTempoEvent();
   
}
void MIDIFileAnalyzer::mf_keysig(MIDIClockTime time, int sf, int mi)
{
   sf = sf;
   mi = mi;
   m_trackInfos[m_nCurTrk].countKeySigEvent();   
}

void MIDIFileAnalyzer::mf_text( MIDIClockTime time, int type , int len, unsigned char *text)
{
   len = len;
   if(type == 3){
      m_trackInfos[m_nCurTrk].setInstName((const char*)text );
   }
   if(type == 4){
      m_trackInfos[m_nCurTrk].setTrackName((const char*)text);
   }
}


void MIDIFileAnalyzer::mf_error( char *errTxt )
{
   m_sErrText = errTxt;
}

void MIDIFileAnalyzer::mf_starttrack( int trk )
{
   TrackInfo trackInfo;
   trackInfo.setTrkNr(trk);
   m_trackInfos.push_back(trackInfo);
   m_nCurTrk =   (uint) m_trackInfos.size()-1;
   m_state = other;
   m_curTime = 0;

}
void MIDIFileAnalyzer::mf_endtrack( int trk )
{
   trk = trk;
   //nothing to do
}

void MIDIFileAnalyzer::mf_header( int format, int nTrks, int division)
{
   cout<<"Format ="<< format<<" Tracks = "<<nTrks<<" division = "<<division<<endl;  
   m_nDivision = division;
}
