/// G Hagopian -- read/write with MIDI
/// http://www.mobilefish.com/tutorials/midi/midi_quickguide_specification.html

#include <iostream>
#include <fstream>
using namespace std;

template<class T>
char* as_bytes(T& i) // treat a T as a sequence of bytes
{
    void* addr = &i; // get the address of the first byte
                    // of memory used to store the object
    return static_cast<char*>(addr); // treat that memory as bytes
}

short reverseBytes(short getal);
int reverseBytes(int& target);


int main() {
    ifstream mfile{"morse_code_a.mid",ios_base::binary};
    //read the first 4 bytes
    char ch, ch2;
    for(int i=0; i<4; ++i) {
        mfile.read(as_bytes(ch),sizeof(char));
        cout << ch;
    }
    cout << "\ntellg() = " << mfile.tellg();
    int headerLength;
    //read the header length (should be 6, but the bytes are reversed, so it's 6*2^24 = 100663296)
    mfile.read(as_bytes(headerLength),sizeof(int)); // note: reading bytes
    cout << "\ntellg() = " << mfile.tellg();
    cout << "\nheader length = " << headerLength/16777216;
    short mtrack;
    mfile.read(as_bytes(mtrack),sizeof(short));
    if(mtrack==0) {
        cout << "\nSingle track MIDI file.";
    }
    else cout << "\nMulti-track MIDI file.";
    mfile.read(as_bytes(mtrack),sizeof(short));
    mtrack = mtrack/256 + mtrack%256;
    if(mtrack>1)
        cout << "\nThere are " << mtrack << " tracks.";
    else cout << "\nThere is " << mtrack << " track.";
    cout << "\ntellg() = " << mfile.tellg();
    mfile.read(as_bytes(mtrack),sizeof(short));
    cout << "\nmtrack = " << hex << mtrack;
    mtrack = reverseBytes(mtrack); // mtrack%256)*256 + mtrack/256;
    cout << "\nmtrack = " << hex << mtrack;
    if(mtrack>127) {
        mtrack -= 128;
        cout << "\nThe division has " << mtrack/256 << " fps and " << endl
             << mtrack%256 << " ticks per frame." << endl;
    }
    else cout << "\nThe division has " << mtrack << " ticks per quarter note." << endl;
    //read the next 4 bytes (should be MTrk)
    for(int i=0; i<4; ++i) {
        mfile.read(as_bytes(ch),sizeof(char));
        cout << ch;
    }
    // read header length
    mfile.read(as_bytes(headerLength),sizeof(int)); // note: reading bytes
    cout << "\ntellg() = " << mfile.tellg();
    headerLength = reverseBytes(headerLength);
    cout << "\nTrack header length = " << dec << headerLength;
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    cout << "\nDelta time (ticks since previous event) = " << (int)ch;
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    mfile.read(as_bytes(ch2),sizeof(char)); // note: reading bytes
    cout << "\n" << hex << (char)ch << ' ' << dec << (int)ch2 << " means, \"here comes a track name:\"";
    mfile.read(as_bytes(ch2),sizeof(char)); // note: reading bytes
    int strLen = (int)ch2;
    cout << "\nThe number of characters in the track name is " << strLen;
    string trackName;
    for(int i=0; i<strLen; ++i) {
        mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
        trackName += ch;
    }
    //mfile.unget();
    cout << "\nThe track name is " << trackName;
    cout << "\ntellg() = " << mfile.tellg();

    /// and again...
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    cout << "\nDelta time (ticks since previous event) = " << (int)ch;
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    mfile.read(as_bytes(ch2),sizeof(char)); // note: reading bytes
    cout << "\n" << hex << (char)ch << ' ' << dec << (int)ch2 << " means, \"here comes a copy right:\"";
    mfile.read(as_bytes(ch2),sizeof(char)); // note: reading bytes
    strLen = (int)ch2;
    cout << "\nThe number of characters in the copyright is " << strLen;
    string copyright;
    for(int i=0; i<strLen; ++i) {
        mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
        copyright += ch;
    }
    cout << "\nThe copyright is " << copyright;
    cout << "\ntellg() = " << mfile.tellg();

    /// and again...
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    cout << "\nDelta time (ticks since previous event) = " << (int)ch;
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    mfile.read(as_bytes(ch2),sizeof(char)); // note: reading bytes
    cout << "\n" << hex << (char)ch << ' ' << dec << (int)ch2 << " means, \"here comes a time signature:\"";
    mfile.read(as_bytes(ch2),sizeof(char)); // note: reading bytes
    int timeSignature = (int)ch2;
    cout << "\nThe number of bytes in the time signature is " << timeSignature;
    cout << "\ntellg() = " << mfile.tellg();

    ///Get time signature
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    int timeSignatureNum = (int)ch;
    cout << "\nBeats per measure = " << timeSignatureNum;
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    int timeSignatureDen = (int)ch;
    cout << "\nA 1/" << timeSignatureDen << " note gets one beat.";
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    int midiClocks = (int)ch;
    cout << "\nThere are " << midiClocks << " midi clicks in a metronome clock.";
    //eat a character
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    cout << "\ntellg() = " << mfile.tellg();

    /// timing
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    cout << "\nDelta time (ticks since previous event) = " << (int)ch;
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    mfile.read(as_bytes(ch2),sizeof(char)); // note: reading bytes
    cout << "\n" << hex << (char)ch << ' ' << dec << (int)ch2 << " means, \"here comes the tempo (msec/quarter note):\"";
    mfile.read(as_bytes(ch2),sizeof(char)); // note: reading bytes
    int  bytesPerTempo = (int)ch2;
    cout << "\nThe number of bytes in the tempo is " << bytesPerTempo;
    cout << "\ntellg() = " << mfile.tellg();
    int powerOf256 = 1;
    for(int i = 0; i < bytesPerTempo-1; ++i) {
        powerOf256 *= 256;
    }
    int msecPerQuarterNote = 0;
    for(int i=0; i< bytesPerTempo; ++i) {
        mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
        msecPerQuarterNote += (int)ch*powerOf256;
        powerOf256 /= 256;
    }
    cout << "\nThere are " << msecPerQuarterNote << " microseconds per quarternote.";
    cout << "\ntellg() = " << mfile.tellg();

    ///Select instrument channel and instrument
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    cout << "\nDelta time (ticks since previous event) = " << (int)ch;
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    cout << endl << hex << ch << " means, \"select instrument channel\" " << dec <<  (int)ch%16;
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    cout << "\nAnd instrument = " << (int)ch; //79

    //start notes
    int delay=0;
    //while(mfile.read(as_bytes(ch),sizeof(char))  &&  (int)ch<0x90) {
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    mfile.read(as_bytes(ch2),sizeof(char)); // note: reading bytes
    ch = (int)ch%0x80; ch2 = (int)ch2%0x80;
    delay = (int)ch + (int)ch2;
    //}
    cout << "\nDelta time = " << delay << endl;
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    if(ch%0x90==0) cout << "\nNote on.";
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    mfile.read(as_bytes(ch2),sizeof(char)); // note: reading bytes
    cout << "\ndelta time = "  << (int)ch+ (int)ch2;
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    cout << endl << hex << ch << " means, \"select instrument channel\" "
         << dec <<  (int)ch%16;
    cout << "\ntellg() = " << dec << mfile.tellg();


    //PLAY NOTES
    mfile.read(as_bytes(ch),sizeof(char)); // note: reading bytes
    mfile.read(as_bytes(ch2),sizeof(char)); // note: reading bytes
    cout << "\nPlay note " << hex << (int)ch  << " with velocity " << (int)ch2;
    cout << "\ntellg() = " << mfile.tellg();


}

short reverseBytes(short target) {
    short reversed;  // this is reverse of getal

    char *n1, *n2;
    n1 = (char *)&target;
    n2 = (char *)&reversed;

    *(n2 + 0) =  *(n1 + 1);  // or n2[0] = n1[3];
    //*(n2 + 1) =  *(n1 + 2);  // or n2[1] = n1[2];
    //*(n2 + 2) =  *(n1 + 1);  // or n2[2] = n1[1];
    *(n2 + 1) =  *(n1 + 0);  // or n2[3] = n1[0];
    return(reversed);
    /* print reversed here */
}

int reverseBytes(int& target) {
    int reversed;  // this is reverse of target

    char *n1, *n2;
    n1 = (char *)&target;
    n2 = (char *)&reversed;

    *(n2 + 0) =  *(n1 + 3);  // or n2[0] = n1[3];
    *(n2 + 1) =  *(n1 + 2);  // or n2[1] = n1[2];
    *(n2 + 2) =  *(n1 + 1);  // or n2[2] = n1[1];
    *(n2 + 3) =  *(n1 + 0);  // or n2[3] = n1[0];
    return(reversed);
    /* print reversed here */
}
