#include <windows.h>
#include <wchar.h>

#include "kbwsr_decoder.h"
#include "kbwsr_config.h"

HINSTANCE KbWsrDecoder::s_hDll = NULL;
WSRPlayerApi* KbWsrDecoder::s_pApi = NULL;
KbWsrDecoder* KbWsrDecoder::s_pActive = NULL;

#define MIN_WSR_SIZE (64*1024)
//////////////////////////////////////////////////////////////////////////
KbWsrDecoder::KbWsrDecoder(IKpiConfig *pConfig)
{
    kpi_InitMediaInfo(&m_MediaInfo);
    m_pROM = NULL;
    m_nROMSize = 0;
    m_nROMBank = 0;
    m_pFooter = NULL;
    m_dVolume = 1.0;
    m_pBuffer = NULL;
    m_pConfig = pConfig;
    pConfig->AddRef();
}
//////////////////////////////////////////////////////////////////////////
KbWsrDecoder::~KbWsrDecoder(void)
{
    if(s_pActive == this){
        s_pApi->pCloseWSR();
        s_pActive = NULL;
    }
    free(m_pROM);
    free(m_pBuffer);
    m_pConfig->Release();
}
//////////////////////////////////////////////////////////////////////////
DWORD __fastcall KbWsrDecoder::Open(const KPI_MEDIAINFO *cpRequest, 
                                    IKpiFile *pFile, 
                                    IKpiFolder *pFolder)
{
    if(!LoadCore(m_pConfig)){
        return 0;
    }
    wchar_t szFileName[MAX_PATH];
    pFile->GetFileName(szFileName, sizeof(szFileName));
    const wchar_t *cszExt = wcsrchr(szFileName, L'.');
    if(!cszExt){
        return 0;
    }
    DWORD dwCount;
    if(_wcsicmp(cszExt, L".wsc") == 0){
        dwCount = 1;
    }
    else{
        dwCount = 256;
    }
    if(!cpRequest){
        m_MediaInfo.dwSampleRate = 44100;
        m_MediaInfo.dwChannels = 2;
        m_MediaInfo.nBitsPerSample = 16;
        m_MediaInfo.dwUnitSample = 0;//m_MediaInfo.dwSampleRate/100;
        m_MediaInfo.qwLength = -1;
        m_MediaInfo.qwLoop = -1;
        m_MediaInfo.qwFadeOut = -1;
        m_MediaInfo.dwCount = dwCount;//曲の数
        return m_MediaInfo.dwCount;
    }
    if (s_pActive) {//複数のインスタンスを持つことは出来ない
        return 0;
    }
    UINT64 qwSize = pFile->GetSize();
    if(qwSize < MIN_WSR_SIZE){//サイズが小さすぎる
        return 0;
    }
    if(qwSize > 1024 * MIN_WSR_SIZE){//サイズが大きすぎる
        return 0;
    }
    m_nROMSize = qwSize;
    m_nROMBank = (m_nROMSize+0xFFFF)>>16;
    m_pROM = (BYTE*)malloc(m_nROMBank*0x10000);
    m_nROMSize = pFile->Read(m_pROM, m_nROMBank*0x10000);
    m_pFooter = (WSR_FOOTER*)(m_pROM + m_nROMSize - sizeof(WSR_FOOTER));
    if(memcmp(m_pFooter->WSRF, "WSRF", 4) != 0){
        //WSC?
        if(memcmp(m_pROM, "WTD", 3) != 0){
            return 0;
        }
        //WSC
        memcpy(m_pFooter->WSRF, "WSRF", 4);
    }
    if(s_pApi->pLoadWSR(m_pROM, m_nROMSize) == 0){
        return 0;
    }
    s_pActive = this;
    kbwsr_Settings settings;
    kbwsr_GetSettings(m_pConfig, &settings);
    //再生周波数は本体からの要求に応じる
    int nSampleRate= cpRequest->dwSampleRate;
    if(settings.nSampleRate[settings.nEmulationCore] < 0){//要求無視
        nSampleRate = settings.nSampleRate[settings.nEmulationCore] * -1;
    }
    else if(nSampleRate == 0){//プラグインのデフォルト
        nSampleRate = settings.nSampleRate[settings.nEmulationCore];
    }
    else if(settings.nEmulationCore == 1){//oswan
        if(nSampleRate < 24000){
            nSampleRate = 12000;
        }
        else if(nSampleRate < 48000){
            nSampleRate = 24000;
        }
        else if(nSampleRate < 96000){
            nSampleRate = 48000;
        }
        else{
            nSampleRate = 96000;
        }
    }
    m_dVolume = settings.dVolume[settings.nEmulationCore];
    nSampleRate = s_pApi->pSetFrequency(nSampleRate);
    s_pApi->pSetChannelMuting(settings.nChannnelMuting);
    DWORD dwUnitSample = nSampleRate / 100;
    m_pBuffer = (short*)malloc(dwUnitSample * 2 * 2);
    m_MediaInfo.dwSampleRate = nSampleRate;
    m_MediaInfo.dwChannels = 2;
    m_MediaInfo.nBitsPerSample = -64;
    m_MediaInfo.dwUnitSample = dwUnitSample;
    m_MediaInfo.qwLength = -1; //曲の長さが不明
    m_MediaInfo.qwLoop = -1;   //ループ曲(ループ時間不明)
    m_MediaInfo.qwFadeOut = -1;//本体の設定(または tag/pls/m3u)に従う
    m_MediaInfo.dwLoopCount = 0;//ループ時間が不明なので実際は何を入れても無視される
    m_MediaInfo.dwCount = dwCount; //曲数(ファイルの内容から取得は不可)
    //ここまで曲番号によらず共通
    return m_MediaInfo.dwCount;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbWsrDecoder::Select(DWORD dwNumber,
                                  const KPI_MEDIAINFO **ppMediaInfo,
                                  IKpiTagInfo *pTagInfo,
                                  DWORD dwTagGetFlags)
{
    if(ppMediaInfo){
        *ppMediaInfo = NULL;
    }
    if(dwNumber > m_MediaInfo.dwCount){
    //番号指定が不正
        return 0;
    }
    if (!ppMediaInfo) {
    //タイトル/アーティスト取得のみ
        return dwNumber;
    }
    //int nSongNo = dwNumber - 1;//dwNumber は 1ベース
    //Reset_WSR(nSongNo);//ここでは不要(Render 前に必ず Seek が呼ばれるため)
    //
    m_MediaInfo.dwNumber = dwNumber;
    *ppMediaInfo = &m_MediaInfo;
    GetTagInfo(pTagInfo);
    return m_MediaInfo.dwNumber;
}
//////////////////////////////////////////////////////////////////////////
UINT64 WINAPI KbWsrDecoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{//シーク（戻り値はシーク後の再生位置）
 //先頭の位置にしかシーク出来ない
    int nSongNo = m_MediaInfo.dwNumber - 1;//dwNumber は 1ベース
    s_pApi->pResetWSR(nSongNo);
    return 0;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbWsrDecoder::Render(BYTE* pBuffer, DWORD dwSizeSample)
{
    DWORD dwBufLen = dwSizeSample * (16/8)*2;
    s_pApi->pUpdateWSR(m_pBuffer, dwBufLen, dwSizeSample);
    short  *src = m_pBuffer;
    double *dst = (double*)pBuffer;
    for(int i = 0; i < dwSizeSample*2; i++){
        dst[i] = src[i] * m_dVolume / 32768.0;
    }
    return dwSizeSample;
}
//////////////////////////////////////////////////////////////////////////
void __fastcall KbWsrDecoder::GetTagInfo(IKpiTagInfo *pTagInfo)
{
    if(!pTagInfo)return;
    if(!m_pFooter){
        return;
    }
    wchar_t szData[128];
    //FirstSongNo
    _snwprintf_s(szData, _TRUNCATE, L"0x%02x", m_pFooter->FirstSongNo);
    pTagInfo->wSetValueW(L"1stSongNo", -1, szData, -1);
    //MakerCode
    _snwprintf_s(szData, _TRUNCATE, L"0x%04x", m_pFooter->Maker);
    pTagInfo->wSetValueW(L"MakerCode", -1, szData, -1);
    //TitleCode
    _snwprintf_s(szData, _TRUNCATE, L"0x%02x", m_pFooter->Title);
    pTagInfo->wSetValueW(L"TitleCode", -1, szData, -1);
    //Version
    _snwprintf_s(szData, _TRUNCATE, L"0x%02x", m_pFooter->Version);
    pTagInfo->wSetValueW(L"Version", -1, szData, -1);
    //ROM
    _snwprintf_s(szData, _TRUNCATE, L"%dMbit", 1 << (m_pFooter->ROMSize&0x0f));
    pTagInfo->wSetValueW(L"ROM", -1, szData, -1);
    //RAM
    const wchar_t* cszRAMs[] = {L"0Kbit", L"256Kbit", L"1Mbit", L"2Mbit"};
    int nRamIndex = m_pFooter->RAMSize & 0x0F;
    if(nRamIndex < 4){
        pTagInfo->wSetValueW(L"RAM", -1, cszRAMs[nRamIndex], -1);
    }
    //EEPROM
    DWORD EEPROM = (m_pFooter->RAMSize >> 4);
    if(EEPROM){
        EEPROM = 1 << (EEPROM-1);
    }
    _snwprintf_s(szData, _TRUNCATE, L"%dKbit", EEPROM);
    pTagInfo->wSetValueW(L"EEPROM", -1, szData, -1);

}
//////////////////////////////////////////////////////////////////////////
DWORD  WINAPI KbWsrDecoder::UpdateConfig(void* pvReserved)
{
    kbwsr_Settings settings;
    kbwsr_GetSettings(m_pConfig, &settings);
    s_pApi->pSetChannelMuting(settings.nChannnelMuting);
    //m_dVolume = settings.dVolume[settings.nEmulationCore];//音量はファイルを開き直してから反映させる
    return 0;
}
//////////////////////////////////////////////////////////////////////////
bool KbWsrDecoder::LoadCore(IKpiConfig* pConfig)
{
    if(s_pApi)return true;

    wchar_t szCorePath[MAX_PATH];
    kbwsr_GetCorePath(pConfig, szCorePath, _countof(szCorePath));

    HINSTANCE hDll = LoadLibrary(szCorePath);
    if(!hDll)return false;
    p_WSRPlayerSetUp WSRPlayerSetUp = (p_WSRPlayerSetUp)GetProcAddress(hDll, "WSRPlayerSetUp");
    if(WSRPlayerSetUp){
        s_pApi = WSRPlayerSetUp();
    }
    if(s_pApi){
        s_hDll = hDll;
    }
    else{
        FreeLibrary(hDll);
    }
    return s_pApi != NULL;
}
//////////////////////////////////////////////////////////////////////////
void KbWsrDecoder::UnloadCore(void)
{
    if(s_pApi){
        FreeLibrary(s_hDll);
        s_hDll = NULL;
        s_pApi = NULL;
    }
}
//////////////////////////////////////////////////////////////////////////

