#include <windows.h>
#include <mmreg.h>
#include <ks.h>

#include "kbwavefile_device.h"
#include "kbwavefile_config.h"
#include "kbcomdlg.h"

extern HINSTANCE g_hKpi;
static bool g_bShowDialog = false;//使用上の注意ダイアログを表示してたら true
///////////////////////////////////////////////////////////////////////////////
KbWaveFileDevice::KbWaveFileDevice(void)
    :m_hmmio(NULL),
     m_mmckRiff{0},
     m_mmckFmt{0},
     m_mmckData{0}
{
    m_nSampleRate = 0;
    m_nChannels = 0;
    m_nBps = 0;
    m_nBufferSizeSample = 0;
    m_nBlockAlign = 0;
    m_qwCurSample = 0;
    m_qwMaxSample = 0;
}
///////////////////////////////////////////////////////////////////////////////
KbWaveFileDevice::~KbWaveFileDevice(void)
{
    Stop();
}
///////////////////////////////////////////////////////////////////////////////
BOOL WINAPI KbWaveFileDevice::Output(BYTE *pOutput, int nSizeSample)
{
    if(m_hmmio){
        if(m_qwCurSample + nSizeSample > m_qwMaxSample){
            nSizeSample = m_qwMaxSample - m_qwCurSample;
            ShowMsgBox(NULL, 
                L"WAV ファイルのサイズの上限(2GB)に達しました。WAV ファイル出力を終了します。", 
                L"kbwavefile.kpi", MB_OK);
        }
        mmioWrite(m_hmmio, (char *)pOutput, nSizeSample * m_nBlockAlign);
	    if(nSizeSample < m_nBufferSizeSample){
            mmioAscend(m_hmmio, &m_mmckData, 0);
            mmioAscend(m_hmmio, &m_mmckRiff, 0);
            mmioClose(m_hmmio, 0);
            m_hmmio = NULL;
        }
        m_qwCurSample += nSizeSample;
        return TRUE;
    }
    return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
void WINAPI KbWaveFileDevice::Stop(void)
{
    if(m_hmmio){
        mmioAscend(m_hmmio, &m_mmckData, 0);
        mmioAscend(m_hmmio, &m_mmckRiff, 0);
        mmioClose(m_hmmio, 0);
        m_hmmio = NULL;
    }
    m_qwCurSample = 0;
}
///////////////////////////////////////////////////////////////////////////////
void WINAPI KbWaveFileDevice::Pause(void)
{
}
///////////////////////////////////////////////////////////////////////////////
void WINAPI KbWaveFileDevice::Restart(void)
{
}
///////////////////////////////////////////////////////////////////////////////
UINT64 WINAPI KbWaveFileDevice::GetPosition(void)
{//デバイスの再生位置をサンプル単位で返す
    return m_qwCurSample;
}
///////////////////////////////////////////////////////////////////////////////
void WINAPI KbWaveFileDevice::Wait(void)
{//次の書き込みが可能になるまで待つ
}
///////////////////////////////////////////////////////////////////////////////
BOOL WINAPI KbWaveFileDevice::SetVolume(int nVolume)
{
    return false;
}
///////////////////////////////////////////////////////////////////////////////
BOOL WINAPI KbWaveFileDevice::SetRate(int nRate)
{
    return false;
}
///////////////////////////////////////////////////////////////////////////////
BOOL __fastcall KbWaveFileDevice::Open(IKpiConfig *pConfig,
                                       const wchar_t *cszDeviceName,
                                       const wchar_t  *cszId, 
                                       const KPI_DEVICE_FORMAT *cpFormat,
                                       int nBufferSizeSample)
{
    if(cpFormat->dwChannels != 1 &&
       cpFormat->dwChannels != 2){
        return false;
    }
    if(cpFormat->nBitsPerSample != 8 &&
       cpFormat->nBitsPerSample != 16 &&
       cpFormat->nBitsPerSample != 24 &&
       cpFormat->nBitsPerSample != 32 &&
       cpFormat->nBitsPerSample != -32 &&
       cpFormat->nBitsPerSample != -64){
        return false;
    }
    if(cpFormat->dwFormatType != KPI_MEDIAINFO::FORMAT_PCM){
        return false;
    }
    kbwavefile_Settings settings;
    kbwavefile_GetSettings(pConfig, &settings);
    if(!settings.bDisableDialog){
        int ret = IDNO;
        if(!g_bShowDialog){//プラグインロード後の初回のみダイアログ表示
            ret = ShowMsgBox(NULL, 
                L"デコーダプラグインが出力した PCM を WAVE ファイルに出力します。\r\n"
                L"このプラグインは使い方が特殊であるため、使用する前に必ず kbwavefile.txt をお読み下さい。\r\n"
                L"「はい」をクリックすると kbwavefile.txt を開いた後、デバイスオープンに失敗させます。\r\n"
                L"\r\n"
                L"この表示が煩わしい場合はプラグイン設定で「ダイアログ非表示」を true に設定して下さい。\r\n"
                L"本体の「ツール」->「プラグイン設定」->「Device」->「kbwavefile.kpi」->「General」セクションから設定します。",
                L"使用上の注意",
                MB_YESNO | MB_DEFBUTTON2 | MB_TASKMODAL);
        }
        g_bShowDialog = true;
        if(ret == IDYES){
            wchar_t szPath[MAX_PATH];
            GetModuleFileName(g_hKpi, szPath, _countof(szPath));
            wchar_t *ext = wcsrchr(szPath, L'.');
            if(ext){
                ext[1] = 0;
            }
            wcsncat_s(szPath, L"txt", _TRUNCATE);
            ShellExecute(NULL, L"open", szPath, 0, 0, SW_SHOW);
            return false;
        }
    }
    else{
        g_bShowDialog = false;
    }
    KbFileDialog dlg(NULL);
    dlg.SetFilter(L"wave file(*.wav)\0*.wav\0");
    dlg.SetDefaultExt(L".wav");
    if(settings.szOutputPath[0]){
        dlg.SetFileName(settings.szOutputPath);
    }
    wchar_t szFileName[MAX_PATH];
    //if(!dlg.Execute(false, szFileName, _countof(szFileName))){
    //    return false;
    //}
    //ダイアログ表示中に本体の演奏停止ボタン等を押すと本体が固まるので
    //メッセージループを回さないようにする
    if(!ExecFileDlg(&dlg, FALSE, szFileName, _countof(szFileName))){
        return false;
    }
    wcsncpy_s(settings.szOutputPath, szFileName, _TRUNCATE);
    kbwavefile_SetSettings(pConfig, &settings);
    
    WAVEFORMATEX wfx = {0};
    if(cpFormat->nBitsPerSample < 0){
        wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
    }
    else{
        wfx.wFormatTag = WAVE_FORMAT_PCM;
    }
    wfx.nChannels = cpFormat->dwChannels;
    wfx.nSamplesPerSec = cpFormat->dwSampleRate;
    wfx.nBlockAlign = (abs(cpFormat->nBitsPerSample) / 8) * cpFormat->dwChannels;
    wfx.wBitsPerSample = abs(cpFormat->nBitsPerSample);
    wfx.cbSize = 0;
    wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;

	m_hmmio = mmioOpen(szFileName, NULL, MMIO_CREATE | MMIO_WRITE);
	if(!m_hmmio){
        return false;
    }
	m_mmckRiff.fccType = mmioStringToFOURCC(TEXT("WAVE"), 0);
	mmioCreateChunk(m_hmmio, &m_mmckRiff, MMIO_CREATERIFF);

	m_mmckFmt.ckid = mmioStringToFOURCC(TEXT("fmt "), 0);
	mmioCreateChunk(m_hmmio, &m_mmckFmt, 0);
	mmioWrite(m_hmmio, (char*)&wfx, offsetof(WAVEFORMATEX, cbSize));
	mmioAscend(m_hmmio, &m_mmckFmt, 0);

	m_mmckData.ckid = mmioStringToFOURCC(TEXT("data"), 0);
	mmioCreateChunk(m_hmmio, &m_mmckData, 0);
	
    m_nSampleRate = cpFormat->dwSampleRate;
    m_nChannels = cpFormat->dwChannels;
    m_nBps = cpFormat->nBitsPerSample;
    m_nBufferSizeSample = nBufferSizeSample;
    m_nBlockAlign = (abs(m_nBps)/8) * m_nChannels;
    LONG curpos = mmioSeek(m_hmmio, 0, SEEK_CUR);
    m_qwMaxSample = (0x80000000 - curpos) / m_nBlockAlign;
    return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
