// -*- c-indentation-style:"stroustrup" c-basic-offset: 4 -*-
/*
  Rosegarden
  A sequencer and musical notation editor.
 
  This program is Copyright 2000-2007
  Guillaume Laurent   <glaurent@telegraph-road.org>,
  Chris Cannam        <cannam@all-day-breakfast.com>,
  Richard Bown        <bownie@bownie.com>
 
  The moral right of the authors to claim authorship of this work
  has been asserted.
 
  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.  See the file
  COPYING included with this distribution for more information.
*/

#include "Midi.h"
#include "MidiEvent.h"
#include <iostream>

// MidiEvent is a representation of MIDI which we use
// for the import/export of MidiFiles.  It uses std::strings for
// meta event messages which makes them nice and easy to handle.
//
//
namespace Rosegarden
{

using std::string;
using std::cout;
using std::endl;

MidiEvent::MidiEvent()
{}

MidiEvent::MidiEvent(timeT deltaTime,
                     MidiByte eventCode):
        m_deltaTime(deltaTime),
        m_duration(0),
        m_eventCode(eventCode),
        m_data1(0),
        m_data2(0),
        m_metaEventCode(0),
        m_metaMessage("")
{}

MidiEvent::MidiEvent(timeT deltaTime,
                     MidiByte eventCode,
                     MidiByte data1):
        m_deltaTime(deltaTime),
        m_duration(0),
        m_eventCode(eventCode),
        m_data1(data1),
        m_data2(0),
        m_metaEventCode(0),
        m_metaMessage("")
{}

MidiEvent::MidiEvent(timeT deltaTime,
                     MidiByte eventCode,
                     MidiByte data1,
                     MidiByte data2):
        m_deltaTime(deltaTime),
        m_duration(0),
        m_eventCode(eventCode),
        m_data1(data1),
        m_data2(data2),
        m_metaEventCode(0),
        m_metaMessage("")

{}

MidiEvent::MidiEvent(timeT deltaTime,
                     MidiByte eventCode,
                     MidiByte metaEventCode,
                     const string &metaMessage):
        m_deltaTime(deltaTime),
        m_duration(0),
        m_eventCode(eventCode),
        m_data1(0),
        m_data2(0),
        m_metaEventCode(metaEventCode),
        m_metaMessage(metaMessage)
{}

MidiEvent::MidiEvent(timeT deltaTime,
                     MidiByte eventCode,
                     const string &sysEx):
        m_deltaTime(deltaTime),
        m_duration(0),
        m_eventCode(eventCode),
        m_data1(0),
        m_data2(0),
        m_metaEventCode(0),
        m_metaMessage(sysEx)
{}

MidiEvent::~MidiEvent()
{}

// Show a representation of our MidiEvent purely for information
// purposes (also demos how we decode them)
//
//
void
MidiEvent::print()
{
    timeT tempo;
    int tonality;
    string sharpflat;

    if (m_metaEventCode) {
        switch (m_metaEventCode) {
        case MIDI_SEQUENCE_NUMBER:
            cout << "MIDI SEQUENCE NUMBER" << endl;
            break;

        case MIDI_TEXT_EVENT:
            cout << "MIDI TEXT:\t\"" << m_metaMessage << "\"" << endl;
            break;

        case MIDI_COPYRIGHT_NOTICE:
            cout << "COPYRIGHT:\t\"" << m_metaMessage << "\"" << endl;

        case MIDI_TRACK_NAME:
            cout << "TRACK NAME:\t\"" << m_metaMessage << "\"" << endl;
            break;

        case MIDI_INSTRUMENT_NAME:
            cout << "INSTRUMENT NAME:\t\"" << m_metaMessage << "\"" << endl;
            break;

        case MIDI_LYRIC:
            cout << "LYRIC:\t\"" << m_metaMessage << "\"" << endl;
            break;

        case MIDI_TEXT_MARKER:
            cout << "MARKER:\t\"" << m_metaMessage << "\"" << endl;
            break;

        case MIDI_CUE_POINT:
            cout << "CUE POINT:\t\"" << m_metaMessage << "\"" << endl;
            break;

            // Sets a Channel number for a TRACK before it starts
        case MIDI_CHANNEL_PREFIX:
            cout << "CHANNEL PREFIX:\t"
            << (timeT)m_metaMessage[0]
            << endl;
            break;

            // These are actually the same case but this is not an
            // official META event - it just crops up a lot.  We
            // assume it's a MIDI_CHANNEL_PREFIX though
            //
        case MIDI_CHANNEL_PREFIX_OR_PORT:
            cout << "FIXED CHANNEL PREFIX:\t"
            << (timeT)m_metaMessage[0] << endl;
            break;

        case MIDI_END_OF_TRACK:
            cout << "END OF TRACK" << endl;
            break;

        case MIDI_SET_TEMPO:
            tempo =
                ((timeT)(((MidiByte)m_metaMessage[0]) << 16)) +
                ((timeT)(((MidiByte)m_metaMessage[1]) << 8)) +
                (short)(MidiByte)m_metaMessage[2];

            tempo = 60000000 / tempo;
            cout << "SET TEMPO:\t" << tempo << endl;
            break;

        case MIDI_SMPTE_OFFSET:
            cout << "SMPTE TIME CODE:\t"
            << (timeT)m_metaMessage[0]
            << ":" << (timeT)m_metaMessage[1]
            << ":" << (timeT)m_metaMessage[2]
            << "  -  fps = " << (timeT)m_metaMessage[3]
            << "  - subdivsperframe = "
            << (timeT)m_metaMessage[4]
            << endl;
            break;

        case MIDI_TIME_SIGNATURE:
            cout << "TIME SIGNATURE:\t"
            << (timeT)m_metaMessage[0]
            << "/"
            << (1 << (timeT)m_metaMessage[1]) << endl;
            break;

        case MIDI_KEY_SIGNATURE:
            tonality = (int)m_metaMessage[0];

            if (tonality < 0) {
                sharpflat = -tonality + " flat";
            } else {
                sharpflat = tonality;
                sharpflat += " sharp";
            }

            cout << "KEY SIGNATURE:\t" << sharpflat << " "
            << (((int)m_metaMessage[1]) == 0 ? "major" : "minor")
            << endl;

            break;

        case MIDI_SEQUENCER_SPECIFIC:
            cout << "SEQUENCER SPECIFIC:\t\"" << m_metaMessage << endl;
            break;


        default:
            cout << "Undefined MIDI META event - "
            << (timeT)m_metaEventCode << endl;
            break;
        }
    } else {
        switch (m_eventCode & MIDI_MESSAGE_TYPE_MASK) {
        case MIDI_NOTE_ON:
            cout << "NOTE ON:\t" << (int)m_data1 << " - "
            << (int)m_data2 << endl;
            break;

        case MIDI_NOTE_OFF:
            cout << "NOTE OFF:\t" << (int)m_data1 << " - "
            << (int)m_data2 << endl;
            break;

        case MIDI_POLY_AFTERTOUCH:
            cout << "POLY AFTERTOUCH:\t" << (int)m_data1
            << " - " << (int)m_data2 << endl;
            break;

        case MIDI_CTRL_CHANGE:
            cout << "CTRL CHANGE:\t" << (int)m_data1
            << " - " << (int)m_data2 << endl;
            break;

        case MIDI_PITCH_BEND:
            cout << "PITCH BEND:\t" << (int)m_data1
            << " - " << (int)m_data2 << endl;
            break;

        case MIDI_PROG_CHANGE:
            cout << "PROG CHANGE:\t" << (int)m_data1 << endl;
            break;

        case MIDI_CHNL_AFTERTOUCH:
            cout << "CHNL AFTERTOUCH\t" << (int)m_data1 << endl;
            break;

        default:
            cout << "Undefined MIDI event" << endl;
            break;
        }
    }


    return ;
}

// Adds the argument to _deltaTime and returns the result
// thus aggregating the times as we go aint
timeT
MidiEvent::addTime(const timeT &time)
{
    m_deltaTime += time;
    return m_deltaTime;
}


// Compare based on time
//
bool
operator<(const MidiEvent &a, const MidiEvent &b)
{
    return a.getTime() < b.getTime();
}


}


