#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <mbstring.h>
#include <locale.h>

#include "kmp_pi.h"
#include "kbfmmidi_decoder.h"

extern HINSTANCE g_hKpi;

#define NCH  2
#define BPS 64
#define BLOCK_ALIGN ((BPS/8)*NCH)

int KbFmMidiDecoder::kbfmmidi_getc(void *fp)
{
    kbfmmidi_File *file = (kbfmmidi_File*)fp;
    if(file->dwPointer < file->dwSize){
        return (int)file->pBuffer[file->dwPointer++];
    }
    return -1;
}
KbFmMidiDecoder::KbFmMidiDecoder(void)
    :m_synthesizer(&m_note_factory)
{
    kpi_InitMediaInfo(&m_MediaInfo);
    int i;
    m_synthesizers[0] = &m_synthesizer;
    for(i = 1; i < MAX_NUM_PORTS; i++){
        m_synthesizers[i] = NULL;
    }
    m_nNumPorts = 0;
    m_qwCurrentSample = 0;
    m_qwLastSample = 0;
    m_dwEndSample = 0;   //MIDI 解析終了後に再生したサンプル数
    m_dwSilentSample = 0;//解析終了後の無音再生時間

    m_bLoop = false;
    m_bSeeking = false;
    m_dLoopPoint = -1.0;
}
KbFmMidiDecoder::~KbFmMidiDecoder(void)
{
    Close();
}
void KbFmMidiDecoder::midi_message(int port, uint_least32_t message)
{
    if(m_bSeeking){
        if(((message & 0xF0) == 0x90)){
        //シーク中はノートオンを無視する
            int velocity = (message >> 16) & 0x7F;
            if(velocity > 0){
                return;
            }
        }
    }
    if(m_bLoop){//ループポイントに戻った直後
        if((message&0xFFF0) == 0x6FB0){//ループポイント(CC111)
            m_bLoop = false;
        }
        else{//ループポイントに戻った直後は何もしない
            return;
        }
    }
    if(port < 0 || port >= m_nNumPorts){
        port = 0;
    }
    m_synthesizers[port]->midi_event(message);
}
void KbFmMidiDecoder::sysex_message(int port, const void* data_, size_t size)
{
    if(m_bLoop){//ループポイントに戻った直後は何もしない
        return;
    }
    if(m_nNumPorts > 1){
    if(port >= 1){
        int t = 0;
    }
    //リセット系とマスターボリュームは全ポートに送信
    BOOL bAll = FALSE;
    const BYTE* data = (const BYTE*)data_;
    if (size == 6 && memcmp(data, "\xF0\x7E\x7F\x09\x01\xF7", 6) == 0) {
        // GM system on 
        bAll = TRUE;
    }
    else if (size == 6 && memcmp(data, "\xF0\x7E\x7F\x09\x02\xF7", 6) == 0) {
        // GM system off 
        bAll = TRUE;
    }
    else if (size == 6 && memcmp(data, "\xF0\x7E\x7F\x09\x03\xF7", 6) == 0) {
        // GM2 system on 
        bAll = TRUE;
    }
    else if (size == 11 && memcmp(data, "\xF0\x41", 2) == 0 && memcmp(data + 3, "\x42\x12\x40\x00\x7F\x00\x41\xF7", 8) == 0) {
        // GS reset 
        bAll = TRUE;
    }
    else if (size == 9 && memcmp(data, "\xF0\x43", 2) == 0 && (data[2] & 0xF0) == 0x10 && memcmp(data + 3, "\x4C\x00\x00\x7E\x00\xF7", 6) == 0) {
        // XG system on
        bAll = TRUE;
    }
    else if (size == 8 && memcmp(data, "\xF0\x7F\x7F\x04\x01", 5) == 0 && data[7] == 0xF7) {
        // master volume
        bAll = TRUE;
    }
    else if (size == 11 && memcmp(data, "\xF0\x41", 2) == 0 && (data[2] & 0xF0) == 0x10 &&
                           memcmp(data + 3, "\x42\x12\x40\x00\x04", 5) == 0 && data[10] == 0xF7) {
    //else if (size == 11 && memcmp(data, "\xF0\x41\x10\x42\x12\x40\x00\x04", 8) == 0 && data[10] == 0xF7) {
        // master volume(GS)
        bAll = TRUE;
    }
    else if (size == 9 && memcmp(data, "\xF0\x43", 2) == 0 && (data[2] & 0xF0) == 0x10 &&
                          memcmp(data + 3, "\x4c\x00\x00\x04", 4) == 0 && data[8] == 0xF7) {
    //else if (size == 9 && memcmp(data, "\xF0\x43\x10\x4c\x00\x00\x04", 7) == 0 && data[8] == 0xF7) {
        // master volume(XG)
        bAll = TRUE;
    }
    if(bAll){//リセット系とマスターボリュームは全てのポートに送信
        for (int i = 0; i < m_nNumPorts; i++) {
            m_synthesizers[i]->sysex_message(data, size);
        }
        return;
    }
    }//if(m_nNumPorts > 1)
    
    if(port < 0 || port >= m_nNumPorts){
        port = 0;
    }
    m_synthesizers[port]->sysex_message(data_, size);
}
void KbFmMidiDecoder::meta_event(int type, const void* data, size_t size)
{
}
void KbFmMidiDecoder::reset()
{
    if(m_bLoop){//ループポイントに戻った直後はリセットしない
        return; 
    }
    int i;
    for(i = 0; i < m_nNumPorts; i++){
        m_synthesizers[i]->reset();
    }
}
DWORD __fastcall KbFmMidiDecoder::Open(const KPI_MEDIAINFO *cpRequest,
                                   IKpiFile   *pFile,
                                   IKpiFolder *pFolder)
{
    DWORD dwSampleRate = cpRequest->dwSampleRate;
    UINT64 qwSize = pFile->GetSize();
    if(qwSize > 1024 * 1024 * 1024){//サイズが大きすぎ
        return 0;
    }
    BYTE *pBuf = (BYTE*)malloc((size_t)qwSize);
    qwSize = pFile->Read(pBuf, (DWORD)qwSize);
    kbfmmidi_File file;
    file.pBuffer = pBuf;
    file.dwSize = (DWORD)qwSize;
    file.dwPointer = 0;

    if(!m_sequencer.load(&file, kbfmmidi_getc)){
        free(pBuf);
        return 0;
    }
    free(pBuf);
    wchar_t szProgramsTxt[MAX_PATH];
    GetModuleFileNameW(g_hKpi, szProgramsTxt, _countof(szProgramsTxt));
    wchar_t *path_delimiter = wcsrchr(szProgramsTxt, L'\\');
    if(path_delimiter){
        path_delimiter[1] = 0;
    }
    wcsncat_s(szProgramsTxt, L"programs.txt", _TRUNCATE);
    FILE *fp = NULL;
#if 0
    _wfopen_s(&fp, szProgramsTxt, L"rt");
    if(fp){
        while(!feof(fp)){
            int c = getc(fp);
            if(c == '@'){
                int prog = 0;
                FMPARAMETER p;
                if(fscanf_s(fp, "%d%d%d%d", &prog, &p.ALG, &p.FB, &p.LFO) == 4
                && fscanf_s(fp, "%d%d%d%d%d%d%d%d%d%d", &p.op1.AR, &p.op1.DR, &p.op1.SR, &p.op1.RR, &p.op1.SL, &p.op1.TL, &p.op1.KS, &p.op1.ML, &p.op1.DT, &p.op1.AMS) == 10
                && fscanf_s(fp, "%d%d%d%d%d%d%d%d%d%d", &p.op2.AR, &p.op2.DR, &p.op2.SR, &p.op2.RR, &p.op2.SL, &p.op2.TL, &p.op2.KS, &p.op2.ML, &p.op2.DT, &p.op2.AMS) == 10
                && fscanf_s(fp, "%d%d%d%d%d%d%d%d%d%d", &p.op3.AR, &p.op3.DR, &p.op3.SR, &p.op3.RR, &p.op3.SL, &p.op3.TL, &p.op3.KS, &p.op3.ML, &p.op3.DT, &p.op3.AMS) == 10
                && fscanf_s(fp, "%d%d%d%d%d%d%d%d%d%d", &p.op4.AR, &p.op4.DR, &p.op4.SR, &p.op4.RR, &p.op4.SL, &p.op4.TL, &p.op4.KS, &p.op4.ML, &p.op4.DT, &p.op4.AMS) == 10){
                    m_note_factory.set_program(prog, p);
                }
            }else if(c == '*'){
                int prog = 0;
                DRUMPARAMETER p;
                if(fscanf_s(fp, "%d%d%d%d%d%d%d", &prog, &p.ALG, &p.FB, &p.LFO, &p.key, &p.panpot, &p.assign) == 7
                && fscanf_s(fp, "%d%d%d%d%d%d%d%d%d%d", &p.op1.AR, &p.op1.DR, &p.op1.SR, &p.op1.RR, &p.op1.SL, &p.op1.TL, &p.op1.KS, &p.op1.ML, &p.op1.DT, &p.op1.AMS) == 10
                && fscanf_s(fp, "%d%d%d%d%d%d%d%d%d%d", &p.op2.AR, &p.op2.DR, &p.op2.SR, &p.op2.RR, &p.op2.SL, &p.op2.TL, &p.op2.KS, &p.op2.ML, &p.op2.DT, &p.op2.AMS) == 10
                && fscanf_s(fp, "%d%d%d%d%d%d%d%d%d%d", &p.op3.AR, &p.op3.DR, &p.op3.SR, &p.op3.RR, &p.op3.SL, &p.op3.TL, &p.op3.KS, &p.op3.ML, &p.op3.DT, &p.op3.AMS) == 10
                && fscanf_s(fp, "%d%d%d%d%d%d%d%d%d%d", &p.op4.AR, &p.op4.DR, &p.op4.SR, &p.op4.RR, &p.op4.SL, &p.op4.TL, &p.op4.KS, &p.op4.ML, &p.op4.DT, &p.op4.AMS) == 10){
                    m_note_factory.set_drum_program(prog, p);
                }
            }
        }
    }
#else
    _wfopen_s(&fp, szProgramsTxt, L"rt,ccs=UNICODE");
    if(fp){
        wchar_t *lc_old = _wsetlocale(LC_ALL, NULL);
        _wsetlocale(LC_ALL, L"");
        while(!feof(fp)){
            wchar_t c = fgetwc(fp);
            if(c == L'@'){
                int prog;
                FMPARAMETER p;
                if(fwscanf_s(fp, L"%d%d%d%d", &prog, &p.ALG, &p.FB, &p.LFO) == 4
                && fwscanf_s(fp, L"%d%d%d%d%d%d%d%d%d%d", &p.op1.AR, &p.op1.DR, &p.op1.SR, &p.op1.RR, &p.op1.SL, &p.op1.TL, &p.op1.KS, &p.op1.ML, &p.op1.DT, &p.op1.AMS) == 10
                && fwscanf_s(fp, L"%d%d%d%d%d%d%d%d%d%d", &p.op2.AR, &p.op2.DR, &p.op2.SR, &p.op2.RR, &p.op2.SL, &p.op2.TL, &p.op2.KS, &p.op2.ML, &p.op2.DT, &p.op2.AMS) == 10
                && fwscanf_s(fp, L"%d%d%d%d%d%d%d%d%d%d", &p.op3.AR, &p.op3.DR, &p.op3.SR, &p.op3.RR, &p.op3.SL, &p.op3.TL, &p.op3.KS, &p.op3.ML, &p.op3.DT, &p.op3.AMS) == 10
                && fwscanf_s(fp, L"%d%d%d%d%d%d%d%d%d%d", &p.op4.AR, &p.op4.DR, &p.op4.SR, &p.op4.RR, &p.op4.SL, &p.op4.TL, &p.op4.KS, &p.op4.ML, &p.op4.DT, &p.op4.AMS) == 10){
                    m_note_factory.set_program(prog, p);
                }
            }else if(c == L'*'){
                int prog;
                DRUMPARAMETER p;
                if(fwscanf_s(fp, L"%d%d%d%d%d%d%d", &prog, &p.ALG, &p.FB, &p.LFO, &p.key, &p.panpot, &p.assign) == 7
                && fwscanf_s(fp, L"%d%d%d%d%d%d%d%d%d%d", &p.op1.AR, &p.op1.DR, &p.op1.SR, &p.op1.RR, &p.op1.SL, &p.op1.TL, &p.op1.KS, &p.op1.ML, &p.op1.DT, &p.op1.AMS) == 10
                && fwscanf_s(fp, L"%d%d%d%d%d%d%d%d%d%d", &p.op2.AR, &p.op2.DR, &p.op2.SR, &p.op2.RR, &p.op2.SL, &p.op2.TL, &p.op2.KS, &p.op2.ML, &p.op2.DT, &p.op2.AMS) == 10
                && fwscanf_s(fp, L"%d%d%d%d%d%d%d%d%d%d", &p.op3.AR, &p.op3.DR, &p.op3.SR, &p.op3.RR, &p.op3.SL, &p.op3.TL, &p.op3.KS, &p.op3.ML, &p.op3.DT, &p.op3.AMS) == 10
                && fwscanf_s(fp, L"%d%d%d%d%d%d%d%d%d%d", &p.op4.AR, &p.op4.DR, &p.op4.SR, &p.op4.RR, &p.op4.SL, &p.op4.TL, &p.op4.KS, &p.op4.ML, &p.op4.DT, &p.op4.AMS) == 10){
                    m_note_factory.set_drum_program(prog, p);
                }
            }
        }
        fclose(fp);
        _wsetlocale(LC_ALL, lc_old);
    }
#endif
    if(dwSampleRate < 8000){
        dwSampleRate = 44100;
    }
    else if(dwSampleRate > 192000){
       dwSampleRate = 192000;
    }
    m_nNumPorts = m_sequencer.get_num_ports();
    if(m_nNumPorts > MAX_NUM_PORTS){
        m_nNumPorts = MAX_NUM_PORTS;
    }
    int i;
    for(i = 1; i < m_nNumPorts; i++){
        m_synthesizers[i] = new synthesizer(&m_note_factory);
    }
    m_MediaInfo.dwSampleRate = dwSampleRate;
    m_MediaInfo.dwChannels = NCH;
    m_MediaInfo.nBitsPerSample = -64;//
    m_MediaInfo.dwSeekableFlags = KPI_MEDIAINFO::SEEK_FLAGS_SAMPLE;
    m_MediaInfo.dwUnitSample = (UINT64)dwSampleRate/100;//UNIT_SAMPLE;
    m_MediaInfo.qwLength = (UINT64)(m_sequencer.get_total_time()*1000.0*10000.0);
    m_qwLastSample = kpi_100nsToSample(m_MediaInfo.qwLength, dwSampleRate);
    m_dLoopPoint = m_sequencer.get_loop_point();
    if(m_dLoopPoint >= 0.0){//ループ曲
        m_MediaInfo.qwLoop = -1;
    }
    m_MediaInfo.dwCount = 1;
    m_MediaInfo.dwNumber = 1;
    return 1; 
}
void __fastcall KbFmMidiDecoder::Close(void)
{
    int i;
    for(i = 1; i < m_nNumPorts; i++){
        delete m_synthesizers[i];
        m_synthesizers[i] = NULL;
    }
    m_nNumPorts = 0;
    m_qwCurrentSample = 0;
    m_sequencer.clear();
}
DWORD WINAPI KbFmMidiDecoder::Render(BYTE *pBuffer, DWORD dwSizeSample)
{
    //if(m_qwCurrentSample >= m_qwLastSample){
        //return 0;
    //}
    //double fPos = (double)(m_qwCurrentSample)/(double)m_MediaInfo.dwSampleRate;
    double fPos = (double)(m_qwCurrentSample+dwSizeSample) / (double)m_MediaInfo.dwSampleRate;
    m_sequencer.play(fPos, this);
    ZeroMemory(pBuffer, sizeof(sample_t)*NCH*dwSizeSample);
    int i;
    for(i = 0; i < m_nNumPorts; i++){
          m_synthesizers[i]->synthesize_mixing((sample_t*)pBuffer, dwSizeSample, m_MediaInfo.dwSampleRate);
    }
    m_qwCurrentSample += dwSizeSample;
    DWORD dwRetSample = dwSizeSample;
    if(m_sequencer.is_play_end()){//終了
        if(m_dLoopPoint < 0){//ループ曲でない
            //0.5秒の無音か 5 秒以上再生したら演奏終了
            const sample_t limit = 0.001;
            sample_t *pcm = (sample_t*)pBuffer;
            for(i = 0; i < dwSizeSample; i++){
                if (m_dwEndSample++ >= m_MediaInfo.dwSampleRate * 5) {//終了後 5 秒以上再生
                    return i;
                }
                if (m_dwSilentSample++ >= m_MediaInfo.dwSampleRate * 5 / 10) {//0.5秒以上の無音
                    return i;
                }
                if(pcm[i*2] < -limit || pcm[i*2] > limit ||
                   pcm[i*2+1] < -limit || pcm[i*2+1] > limit){//無音でない
                    m_dwSilentSample = 0;
                }
            }
        }
        else{
            if(m_qwCurrentSample >= m_qwLastSample){
                dwRetSample -= (m_qwCurrentSample - m_qwLastSample);
                if((int)dwRetSample < 0){
                    dwRetSample = 0;
                }
                m_qwCurrentSample = kpi_100nsToSample(m_dLoopPoint * 1000.0 * 10000, m_MediaInfo.dwSampleRate);
                m_bLoop = true;//ループポイントに戻す
                dwRetSample += Render(pBuffer + dwRetSample*BLOCK_ALIGN, (dwSizeSample - dwRetSample));
                m_bLoop = false;
            }
        }
        /*if(m_qwCurrentSample >= m_qwLastSample){
            dwRetSample -= (m_qwCurrentSample - m_qwLastSample);
            if((int)dwRetSample < 0){
                dwRetSample = 0;
            }
            if(m_dLoopPoint > 0){
                m_qwCurrentSample = kpi_100nsToSample(m_dLoopPoint * 1000.0 * 10000, m_MediaInfo.dwSampleRate);
                m_bLoop = true;//ループポイントに戻す
                dwRetSample += Render(pBuffer + dwRetSample*BLOCK_ALIGN, (dwSizeSample - dwRetSample));
                m_bLoop = false;
            }
        }*/
    }
    return dwRetSample;
}
UINT64 WINAPI KbFmMidiDecoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{
    if(qwPosSample == 0){
        reset();//戻すときは sequencer::play で呼ばれるが、先頭に戻すときは呼ばれない
    }
    m_bSeeking = true;
    UINT64 qwPos100ns = kpi_SampleTo100ns(qwPosSample, m_MediaInfo.dwSampleRate);
    m_sequencer.play((double)qwPos100ns / (1000.0*10000), this);
    m_bSeeking = false;
    m_qwCurrentSample = qwPosSample;
    m_dwEndSample = 0;
    m_dwSilentSample = 0;
    return qwPosSample;
}
///////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbFmMidiDecoder::Select(DWORD dwNumber,
                                  const KPI_MEDIAINFO **ppMediaInfo,
                                  IKpiTagInfo *pTagInfo,
                                  DWORD dwTagGetFlags)
{
    if(ppMediaInfo){
        *ppMediaInfo;
    }
    if (dwNumber > m_MediaInfo.dwCount) {
        return 0;
    }
    if (!ppMediaInfo) {//タグ取得のみ(タイトル/アーティストのみ)
        return 1;
    }
    *ppMediaInfo = &m_MediaInfo;
    m_MediaInfo.dwNumber = dwNumber;
    return 1;
}
/////////////////////////////////////////////////////////////////////////
