#include <windows.h>
#include <stdio.h>
#include "kbsid_decoder.h"

#ifndef NO_RESID_HACK
extern int residHackEnabled;
#endif

KbSidDecoder* KbSidDecoder::s_pActive = NULL;
//////////////////////////////////////////////////////////////////////////
KbSidDecoder::KbSidDecoder(IKpiConfig *pConfig)
{
    kpi_InitMediaInfo(&m_MediaInfo);
    m_tune = NULL;
    m_engine = NULL;
    ZeroMemory(&m_config, sizeof(m_config));
    ZeroMemory(&m_tuneInfo, sizeof(m_tuneInfo));
    m_qwCurSample = 0;
    kbsid_GetSettings(pConfig, &m_Settings);
}
//////////////////////////////////////////////////////////////////////////
KbSidDecoder::~KbSidDecoder(void)
{
    delete m_tune;
    delete m_engine;
    InterlockedCompareExchangePointer((void**)&s_pActive, NULL, this);
}
//////////////////////////////////////////////////////////////////////////
DWORD __fastcall KbSidDecoder::Open(const KPI_MEDIAINFO *cpRequest, 
                                        IKpiFile   *pFile, 
                                        IKpiFolder *pFolder)
{
    if(InterlockedCompareExchangePointer((void**)&s_pActive, this, NULL) != NULL){
    //複数のインスタンスを同時に持つことは出来ない
        return 0;
    }
    DWORD dwSampleRate = cpRequest ? cpRequest->dwSampleRate : 0;
    if(dwSampleRate == 0){//プラグインのデフォルト
        dwSampleRate = 44100;
    }
    else if(dwSampleRate < 8000){
        dwSampleRate = 8000;
    }
    else if(dwSampleRate > 48000){
        dwSampleRate = 48000;
    }
    const BYTE *buf = NULL;
    size_t buf_size = 0;
    if(!pFile->GetBuffer(&buf, &buf_size)){
        return 0;
    }
	emuEngine *engine = new emuEngine;
    if(!engine->verifyEndianess()){
        delete engine;
        return 0;
    }
    sidTune *tune = new sidTune(0);
	if (!tune->load(buf, buf_size)){
        delete engine;
        delete tune;
        return 0;
    }
	tune->getInfo(m_tuneInfo);
	/*if (!sidEmuInitializeSong(*engine, *tune, 0)){
        delete engine;
        delete tune;
        return 0;
    } */    
    residHackEnabled = m_Settings.bEnableReSID;
	engine->getConfig(m_config);
    int nch  = m_Settings.bEnableReSID ? 1 : 2;
	m_config.frequency = dwSampleRate;
	m_config.channels = (nch == 2) ? SIDEMU_STEREO : SIDEMU_MONO;
	m_config.bitsPerSample = SIDEMU_16BIT;//(bits == 16) ? SIDEMU_16BIT : SIDEMU_8BIT;
	m_config.sampleFormat = SIDEMU_SIGNED_PCM;//(bits == 16) ?  SIDEMU_SIGNED_PCM : SIDEMU_UNSIGNED_PCM;
	m_config.memoryMode = MPU_BANK_SWITCHING;
	/*if (m_config.volumeControl == SIDEMU_VOLCONTROL)
	{
		int voice, muteVal = 0;  // which voices to mute
		for (voice = 1; voice <= 4; voice++)
		{
			if ((muteVal & (1 << (voice - 1))) != 0)
			{
				engine->setVoiceVolume(voice, 0, 0, 0);
			}
		}
	}*/
	//engine->setConfig(m_config);
    m_MediaInfo.dwChannels = nch;
    m_MediaInfo.nBitsPerSample = 16;
    m_MediaInfo.dwSampleRate = dwSampleRate;
    m_MediaInfo.dwSeekableFlags = 0;//KPI_MEDIAINFO::SEEK_FLAGS_SAMPLE;
    m_MediaInfo.qwLength = -1;
    m_MediaInfo.qwLoop = -1;
    m_MediaInfo.qwFadeOut = -1;
    m_MediaInfo.dwCount = m_tuneInfo.songs;
    m_engine = engine;
    m_tune = tune;
    return m_MediaInfo.dwCount;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbSidDecoder::Select(DWORD dwNumber,
                                  const KPI_MEDIAINFO **ppMediaInfo,
                                  IKpiTagInfo *pTagInfo,
                                  DWORD dwTagGetFlags)
{
    if(ppMediaInfo){
        *ppMediaInfo = NULL;
    }
    if(dwNumber > m_MediaInfo.dwCount){
    //番号指定が不正
        return 0;
    }
    m_MediaInfo.dwNumber = dwNumber;
    GetTagInfo(pTagInfo, dwTagGetFlags);
    if (!ppMediaInfo) {
        return dwNumber;
    }
    *ppMediaInfo = &m_MediaInfo;
    m_qwCurSample = (UINT64)-1;
    return m_MediaInfo.dwNumber;
}
//////////////////////////////////////////////////////////////////////////
UINT64 WINAPI KbSidDecoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{
    if(m_qwCurSample > qwPosSample){
        if (!sidEmuInitializeSong(*m_engine, *m_tune, m_MediaInfo.dwNumber)) {
            m_qwCurSample = (UINT64)-1;
            return 0;
        }
        m_qwCurSample = 0;
        m_engine->setConfig(m_config);
    }
	return m_qwCurSample;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbSidDecoder::Render(BYTE* pBuffer, DWORD dwSizeSample)
{
    if(m_qwCurSample == (UINT64)-1){
        return 0;
    }
	sidEmuFillBuffer(*m_engine, *m_tune, pBuffer, dwSizeSample*2*m_MediaInfo.dwChannels);
    m_qwCurSample += dwSizeSample;
    return dwSizeSample;
}
//////////////////////////////////////////////////////////////////////////
void __fastcall KbSidDecoder::GetTagInfo(IKpiTagInfo *pTagInfo, DWORD dwTagGetFlags)
{
    if(!pTagInfo)return;
    pTagInfo->GetTagInfo(NULL, NULL, 0, KPI_TAGGET_FLAG_NONE);
    if (dwTagGetFlags & KPI_TAGGET_FLAG_TITLE) {
        if(m_tuneInfo.nameString && m_tuneInfo.nameString[0]){
            pTagInfo->wSetValueU8(L"title", -1, m_tuneInfo.nameString, -1);
        }
    }
    if (dwTagGetFlags & KPI_TAGGET_FLAG_ARTIST) {
        if (m_tuneInfo.authorString && m_tuneInfo.authorString[0]) {
            pTagInfo->wSetValueU8(L"artist", -1, m_tuneInfo.authorString, -1);
        }
    }
    if (m_tuneInfo.copyrightString && m_tuneInfo.copyrightString[0]) {
        if (dwTagGetFlags & KPI_TAGGET_FLAG_TITLE) {
            pTagInfo->wSetValueU8(L"copyright", -1, m_tuneInfo.copyrightString, -1);
        }
    }
}
//////////////////////////////////////////////////////////////////////////
