#include "MidiMetaEventAdapter.h"

#include "BinaryStream.h"
#include "ByteUtils.h"

#include <iostream>
#include <bitset>

int MidiMetaEventAdapter::ReadEvent(std::ifstream* file, MetaMidiEvent* event, int& lastMidiChannel)
{
    unsigned byteCount = 0;
    char c;
    file->get(c);
    byteCount++;

    event->SetType(c);

    std::cout << "Meta event type: " << std::hex << int(c) << std::dec<<std::endl;

    switch (event->GetType())
    {
    case MetaMidiEvent::Type::SEQ_NUM:
        byteCount += ReadIntEvent(file, event, 2);
        break;
    case MetaMidiEvent::Type::TEXT:
    case MetaMidiEvent::Type::COPYRIGHT:
    case MetaMidiEvent::Type::TRACK_NAME:
    case MetaMidiEvent::Type::INSTRUMENT_NAME:
    case MetaMidiEvent::Type::MARKER:
    case MetaMidiEvent::Type::CUE_POINT:
        byteCount += ReadStringEvent(file, event);
        break;
    case MetaMidiEvent::Type::CHANNEL_PREFIX:
        byteCount += ReadChannelPrefixEvent(file, event, lastMidiChannel);
        break;
    case MetaMidiEvent::Type::END_TRACK:
    {
        int length = *BinaryStream::getNextByteAsInt(file);
        byteCount ++;
        break;
    }
    case MetaMidiEvent::Type::SMPTE_OFFSET:
        byteCount += ReadTimeCodeEvent(file, event);
        break;
    case MetaMidiEvent::Type::SET_TEMPO:
        byteCount += ReadIntEvent(file, event);
        break;
    case MetaMidiEvent::Type::TIME_SIG:
        byteCount += ReadTimeSignatureEvent(file, event);
        break;
    case MetaMidiEvent::Type::KEY_SIG:
        byteCount += ReadKeySignatureEvent(file, event);
        break;
    case MetaMidiEvent::Type::SEQ_CUSTOM:
        break;
    case MetaMidiEvent::Type::UNKNOWN:
        std::cout << "Unknown meta event " << std::bitset<8>(c) << "|"  <<std::endl;
        byteCount += ReadUnknownEvent(file);
        break;
    default:
        break;
    }
    return byteCount;
}

int MidiMetaEventAdapter::ReadUnknownEvent(std::ifstream* file)
{
    int length = *BinaryStream::getNextByteAsInt(file);

    char c;
    for(unsigned idx=0; idx<length; idx++)
    {
        file->get(c);
    }
    return length;
}

int MidiMetaEventAdapter::ReadStringEvent(std::ifstream* file, MetaMidiEvent* event)
{
    unsigned byteCount = 0;

    int length = *BinaryStream::getNextByteAsInt(file);
    byteCount++;

    std::string name;
    BinaryStream::getNextString(file, name, length);
    byteCount += length;
    event->SetLabel(name);
    return byteCount;
}

int MidiMetaEventAdapter::ReadIntEvent(std::ifstream* file, MetaMidiEvent* event, int lengthIn)
{
    unsigned byteCount = 0;
    int length = 0;
    if(lengthIn > -1)
    {
        length = lengthIn;
    }
    else
    {
        length = *BinaryStream::getNextByteAsInt(file);
        byteCount ++;
    }

    std::string buffer;
    BinaryStream::getNextNBytes(file, buffer.data(), length);
    byteCount += length;

    const int value = ByteUtils::ToType<ByteUtils::DWord>(buffer.data(), length);
    event->SetValue(value);
    return byteCount;
}

int MidiMetaEventAdapter::ReadChannelPrefixEvent(std::ifstream* file, MetaMidiEvent* event, int& lastMidiChannel)
{
    unsigned byteCount = 0;
    int length = *BinaryStream::getNextByteAsInt(file);
    byteCount ++;

    std::string buffer;
    BinaryStream::getNextNBytes(file, buffer.data(), length);
    byteCount += length;

    const int value = ByteUtils::ToType<ByteUtils::DWord>(buffer.data(), length);
    event->SetValue(value);
    lastMidiChannel = value;
    return byteCount;
}

int MidiMetaEventAdapter::ReadTimeSignatureEvent(std::ifstream* file, MetaMidiEvent* event)
{
    unsigned byteCount = 0;
    int length = *BinaryStream::getNextByteAsInt(file);
    byteCount++;

    MidiTimeSignature timeSig;
    timeSig.mNumer = *BinaryStream::getNextByteAsInt(file);
    timeSig.mDenom = *BinaryStream::getNextByteAsInt(file);
    timeSig.mMetro = *BinaryStream::getNextByteAsInt(file);
    timeSig.mF32 = *BinaryStream::getNextByteAsInt(file);
    byteCount +=4;

    if (length > 4)
    {
        char c;
        for(unsigned idx=0; idx<length-4; idx++)
        {
            file->get(c);
            byteCount++;
        }
    }
    return byteCount;
}

int MidiMetaEventAdapter::ReadKeySignatureEvent(std::ifstream* file, MetaMidiEvent* event)
{
    unsigned byteCount = 0;
    int length = *BinaryStream::getNextByteAsInt(file);
    byteCount++;

    MidiKeySignature keySig;
    keySig.mSharpsFlats = *BinaryStream::getNextByteAsInt(file);
    keySig.mMinor = *BinaryStream::getNextByteAsInt(file);
    byteCount +=2;
    return byteCount;
}

int MidiMetaEventAdapter::ReadTimeCodeEvent(std::ifstream* file, MetaMidiEvent* event)
{
    unsigned byteCount = 0;
    int length = *BinaryStream::getNextByteAsInt(file);
    byteCount++;

    MidiSmtpeTimecode timeCode;
    timeCode.mHr = *BinaryStream::getNextByteAsInt(file);
    timeCode.mMin = *BinaryStream::getNextByteAsInt(file);
    timeCode.mSec = *BinaryStream::getNextByteAsInt(file);
    timeCode.mFrame = *BinaryStream::getNextByteAsInt(file);
    timeCode.mFrameFrac = *BinaryStream::getNextByteAsInt(file);
    byteCount +=5;
    return byteCount;
}
