#include <windows.h>
#include <stdio.h>
#include "kbgme_decoder.h"
#include "../../kbvgm/source/kbvgm_gzfile.h"

//////////////////////////////////////////////////////////////////////////
LONG KbGmeDecoder::s_lInitialized = 0;
//////////////////////////////////////////////////////////////////////////
KbGmeDecoder::KbGmeDecoder(IKpiConfig *pConfig)
{
    kpi_InitMediaInfo(&m_MediaInfo);
    m_pFile = NULL;
    m_pEmu = NULL;
    kbgme_GetSettings(pConfig, &m_Settings);
}
//////////////////////////////////////////////////////////////////////////
KbGmeDecoder::~KbGmeDecoder(void)
{
    if(m_pEmu){
        gme_delete(m_pEmu);
    }
    if(m_pFile){
        m_pFile->Release();
    }
}
//////////////////////////////////////////////////////////////////////////
DWORD __fastcall KbGmeDecoder::Open(const KPI_MEDIAINFO *cpRequest,
                                    IKpiFile   *pFile,
                                    IKpiFolder *pFolder)
{
    Music_Emu  *emu = NULL;
    const BYTE *buffer = NULL;
    KbGzFile *pGzFile = NULL;
    BYTE *buffer_extracted = NULL;
    size_t size = 0;
    int nSampleRate = 44100;
    if(cpRequest){
        nSampleRate = cpRequest->dwSampleRate;
    }
    if(m_Settings.nSampleRate < 0){
        nSampleRate = m_Settings.nSampleRate * -1;//要求無視
    }
    else if(nSampleRate == 0){
        nSampleRate = m_Settings.nSampleRate;//プラグインのデフォルト
    }
    do{
        if(!pFile->GetBuffer(&buffer, &size)){
            break;
        }
        //GZ 対応
        //(libgme の機能を使うと本体同梱の kbzlib.dll ではエクスポートされない API が必要)
        if(buffer[0] == 0x1f || buffer[1] == 0x8b){//gz
            pGzFile = new KbGzFile;
            if(!pGzFile->Open(pFile)){
                break;
            }
            buffer_extracted = (BYTE*)malloc(pGzFile->GetSize());
            size = pGzFile->Read(buffer_extracted, pGzFile->GetSize());
            buffer = buffer_extracted;
        }
        if(gme_open_data(buffer, size,  &emu, nSampleRate)){
            break;
        }
        m_MediaInfo.dwSampleRate = nSampleRate;
        m_MediaInfo.dwChannels = 2;
        m_MediaInfo.nBitsPerSample = 16;
        m_MediaInfo.qwLength = 0;
        m_MediaInfo.dwSeekableFlags = KPI_MEDIAINFO::SEEK_FLAGS_SAMPLE;
        m_MediaInfo.dwUnitSample = 0;
        m_MediaInfo.dwNumber = 1;
        m_MediaInfo.dwCount = gme_track_count(emu);
        m_pFile = pFile;
        pFile->AddRef();
        m_pEmu = emu;
        emu = NULL;//削除されないようにする
    }while(0);
    if(emu){
        gme_delete(emu);
    }
    if(pGzFile){
        pGzFile->Release();
    }
    if(buffer_extracted){
        free(buffer_extracted);
    }
    return m_MediaInfo.dwCount;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbGmeDecoder::Select(DWORD dwNumber,
                                  const KPI_MEDIAINFO **ppMediaInfo,
                                  IKpiTagInfo *pTagInfo,
                                  DWORD dwTagGetFlags)
{
    if(ppMediaInfo){
        *ppMediaInfo;
    }
    if(dwNumber > m_MediaInfo.dwCount){
    //番号指定が不正
        return 0;
    }
    m_MediaInfo.dwNumber = dwNumber;
    if (!ppMediaInfo) {
        //タイトル/アーティスト取得のみ
        GetTagInfo(pTagInfo, dwTagGetFlags);
        return dwNumber;
    }
    if(dwNumber == 0){
        return 0;
    }
    int nTrack = dwNumber-1;//0 ベースに直す
    if(gme_start_track(m_pEmu, nTrack)){
        return 0;
    }
    gme_info_t *gme_info = NULL;
    gme_track_info(m_pEmu, &gme_info, nTrack);
    if(gme_info){
        int intro = gme_info->intro_length;
        int loop = gme_info->loop_length;
        int fade = gme_info->fade_length;
        int length = gme_info->length;
        if(intro >= 0 && loop >= 0){
            m_MediaInfo.qwLength = (UINT64)(intro + loop) * 10000;
            m_MediaInfo.qwLoop = (UINT64)loop * 10000;
        }
        else if(length >= 0){
            m_MediaInfo.qwLength = (UINT64)length * 10000;
            m_MediaInfo.qwLoop = (UINT64)-1;
        }
        else{
            m_MediaInfo.qwLength = (UINT64)-1;
            m_MediaInfo.qwLoop = loop >= 0 ? (UINT64)loop*10000 : (UINT64)-1;
        }
        if(fade >= 0){
            m_MediaInfo.qwFadeOut = (UINT64)fade*10000;
        }
        else{
            m_MediaInfo.qwFadeOut = (UINT64)-1;
        }
        gme_free_info(gme_info);
    }
    else{
        return 0;
    }
    GetTagInfo(pTagInfo, dwTagGetFlags);
    gme_equalizer_t eq = {0};
    eq.treble = m_Settings.dTreble;
    eq.bass = m_Settings.dBass;
    gme_enable_accuracy(m_pEmu, m_Settings.bEnableAccuracy);
    gme_set_stereo_depth(m_pEmu, m_Settings.dStereoDepth);
    gme_set_equalizer(m_pEmu, &eq);
    gme_ignore_silence(m_pEmu, false);
    gme_set_tempo(m_pEmu, 1.0);
    gme_mute_voices(m_pEmu, 0);
    *ppMediaInfo = &m_MediaInfo;
    return m_MediaInfo.dwNumber;
}
//////////////////////////////////////////////////////////////////////////
UINT64 WINAPI KbGmeDecoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{
    gme_seek_samples(m_pEmu, qwPosSample*2);
    return gme_tell_samples(m_pEmu)/2;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbGmeDecoder::Render(BYTE* pBuffer, DWORD dwSizeSample)
{
    if(gme_track_ended(m_pEmu)){
        return 0;
    }
    int start_sample = gme_tell_samples(m_pEmu);
    gme_play(m_pEmu, dwSizeSample*2, (short*)pBuffer);
    int cur_sample = gme_tell_samples(m_pEmu);
    int ret = (cur_sample - start_sample)/2;
    return ret;
}
//////////////////////////////////////////////////////////////////////////
void __fastcall KbGmeDecoder::GetTagInfo(IKpiTagInfo *pTagInfo, DWORD dwTagGetFlags)
{
    if(!pTagInfo){
        return;
    }
    //本体のタグ取得ルーチンを使わない
    pTagInfo->GetTagInfo(NULL, NULL, KPI_TAGTYPE_NONE, 0);
    gme_info_t *gme_info = NULL;
    gme_track_info(m_pEmu, &gme_info, m_MediaInfo.dwNumber-1);
    if(gme_info){
        if(gme_info->system[0]){
            pTagInfo->wSetValueA(L"system", -1, gme_info->system, -1);
        }
        if(gme_info->game[0]){
            pTagInfo->wSetValueA(L"game", -1, gme_info->game, -1);
        }
        if(gme_info->song[0]){
            pTagInfo->wSetValueA(L"title", -1, gme_info->song, -1);
        }
        if(gme_info->author[0]){
            pTagInfo->wSetValueA(L"author", -1, gme_info->author, -1);
        }
        if(gme_info->copyright[0]){
            pTagInfo->wSetValueA(L"copyright", -1, gme_info->copyright, -1);
        }
        if(gme_info->comment[0]){
            pTagInfo->wSetValueA(L"comment", -1, gme_info->comment, -1);
        }
        if(gme_info->dumper[0]){
            pTagInfo->wSetValueA(L"dumper", -1, gme_info->dumper, -1);
        }
        gme_free_info(gme_info);
    }
}
//////////////////////////////////////////////////////////////////////////
void __fastcall KbGmeDecoder::Initialize(IKpiConfig *pConfig)
{
    if(InterlockedIncrement(&s_lInitialized) != 1){
        return;
    }
}
//////////////////////////////////////////////////////////////////////////
void __fastcall KbGmeDecoder::Uninitialize(void)
{
    if(InterlockedDecrement(&s_lInitialized) == 0){
        return;
    }
}
//////////////////////////////////////////////////////////////////////////
