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

#include "kmp_pi.h"
#include "OptimFROG/PortableTypes.h"
#include "OptimFROG/OptimFROG.h"
#include "kbofr_decoder.h"

/*
typedef struct
{
    condition_t (*close)(void* instance);
    sInt32_t (*read)(void* instance, void* destBuffer, uInt32_t count);
    condition_t (*eof)(void* instance);
    condition_t (*seekable)(void* instance);
    sInt64_t (*length)(void* instance);
    sInt64_t (*getPos)(void* instance);
    condition_t (*seek)(void* instance, sInt64_t pos);
}
#if defined(CFG_ATTRIBUTE_PACKED)
__attribute__((packed))
#endif
ReadInterface;
*/
///////////////////////////////////////////////////////////////////////////
static condition_t kbofr_close(void* instance)
{
    IKpiFile *pFile = (IKpiFile*)instance;
    pFile->Release();
    return C_TRUE;
}
///////////////////////////////////////////////////////////////////////////
static sInt32_t kbofr_read(void* instance,void* destBuffer, uInt32_t count)
{
    IKpiFile *pFile = (IKpiFile*)instance;
    return pFile->Read(destBuffer, count);
}
///////////////////////////////////////////////////////////////////////////
static condition_t kbofr_eof(void* instance)
{
    IKpiFile *pFile = (IKpiFile*)instance;
    UINT64 qwPos = pFile->Seek(0, FILE_CURRENT);
    if(qwPos >= pFile->GetSize()){
        return C_TRUE;
    }
    return C_FALSE;
}
///////////////////////////////////////////////////////////////////////////
static condition_t kbofr_seekable(void* instance)
{
    return C_TRUE;
}
///////////////////////////////////////////////////////////////////////////
static sInt64_t kbofr_length(void* instance)
{
    IKpiFile *pFile = (IKpiFile*)instance;
    return pFile->GetSize();
}
///////////////////////////////////////////////////////////////////////////
static sInt64_t kbofr_getPos(void* instance)
{
    IKpiFile *pFile = (IKpiFile*)instance;
    return pFile->Seek(0, FILE_CURRENT);
}
///////////////////////////////////////////////////////////////////////////
static condition_t kbofr_seek(void* instance, sInt64_t pos)
{
    IKpiFile *pFile = (IKpiFile*)instance;
    if(pFile->Seek(pos, FILE_BEGIN) != KPI_FILE_EOF){
        return C_TRUE;
    }
    return C_FALSE;
}
///////////////////////////////////////////////////////////////////////////
//KbOfrDecoder
///////////////////////////////////////////////////////////////////////////
KbOfrDecoder::KbOfrDecoder(void)
{
    kpi_InitMediaInfo(&m_MediaInfo);
    m_pInstance = NULL;
    m_ReadInterface.close = kbofr_close;
    m_ReadInterface.read = kbofr_read;
    m_ReadInterface.eof = kbofr_eof;
    m_ReadInterface.seekable = kbofr_seekable;
    m_ReadInterface.length = kbofr_length;
    m_ReadInterface.getPos= kbofr_getPos;
    m_ReadInterface.seek= kbofr_seek;
}
///////////////////////////////////////////////////////////////////////////
KbOfrDecoder::~KbOfrDecoder(void)
{
    Close();
}
///////////////////////////////////////////////////////////////////////////
DWORD __fastcall KbOfrDecoder::Open(const KPI_MEDIAINFO *cpRequest,
                                    IKpiFile *pFile,
                                    IKpiFolder *pFolder)
{
    m_pInstance = OptimFROG_createInstance();
    if(!m_pInstance){
        return FALSE;
    }
    pFile->AddRef();
    if(!OptimFROG_openExt(m_pInstance, &m_ReadInterface, pFile, FALSE)){
        OptimFROG_destroyInstance(m_pInstance);
        m_pInstance = NULL;
        return FALSE;
    }
    OptimFROG_Info info = {0};
    OptimFROG_getInfo(m_pInstance, &info);
    m_MediaInfo.dwSampleRate = info.samplerate;
    m_MediaInfo.dwChannels = info.channels;
    m_MediaInfo.nBitsPerSample = info.bitspersample;
    m_MediaInfo.dwSeekableFlags = OptimFROG_seekable(m_pInstance) ? KPI_MEDIAINFO::SEEK_FLAGS_SAMPLE : 0;
    m_MediaInfo.dwUnitSample = 0;
    m_MediaInfo.dwNumber = 1;
    m_MediaInfo.dwCount = 1;
    m_MediaInfo.qwLength = info.length_ms * 10000;
    return TRUE;
}
///////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbOfrDecoder::Select(DWORD dwNumber,
                                  const KPI_MEDIAINFO **ppMediaInfo,
                                  IKpiTagInfo *pTagInfo,
                                  DWORD dwTagGetFlags)
{
    if(ppMediaInfo){
        *ppMediaInfo = NULL;
    }
    if(dwNumber > m_MediaInfo.dwCount){//番号がおかしい
        return 0;
    }
    GetTagInfo(pTagInfo, dwTagGetFlags);
    if (!ppMediaInfo) {//タグ取得のみ(タイトル/アーティストのみ)
        return 1;
    }
    *ppMediaInfo = &m_MediaInfo;
    return 1;
}
///////////////////////////////////////////////////////////////////////////
void __fastcall KbOfrDecoder::Close(void)
{
    if(m_pInstance){
        OptimFROG_destroyInstance(m_pInstance);
        m_pInstance = NULL;
    }
}
///////////////////////////////////////////////////////////////////////////
UINT64 WINAPI KbOfrDecoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{
    OptimFROG_seekPoint(m_pInstance, qwPosSample);
    return qwPosSample;
}
///////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbOfrDecoder::Render(BYTE *pBuffer, DWORD dwSizeSample)
{
    return OptimFROG_read(m_pInstance, pBuffer, dwSizeSample, false);
}
///////////////////////////////////////////////////////////////////////////
void __fastcall KbOfrDecoder::GetTagInfo(IKpiTagInfo *pTagInfo, DWORD dwTagGetFlags)
{
    if(!pTagInfo){
        return;
    }
    if(!(dwTagGetFlags&KPI_TAGGET_FLAG_GENERAL)){
        return;
    }
    OptimFROG_Info Info = {0};
    if(OptimFROG_getInfo(m_pInstance, &Info)){
        wchar_t szBitrate[64];
        wchar_t szVersion[64];
        swprintf_s(szBitrate, L"%dkbps", Info.bitrate);
        pTagInfo->wSetValueW(SZ_KMP_NAME_BITRATE_W, -1, szBitrate, -1);
        swprintf_s(szVersion, L"%d", Info.version);
        pTagInfo->wSetValueW(L"version", -1, szVersion, -1);
        if(Info.method){
            pTagInfo->wSetValueA(L"method", -1, Info.method, -1);
        }
        if(Info.speedup){
            pTagInfo->wSetValueA(L"speedup", -1, Info.speedup, -1);
        }
        if(Info.sampleType){
            pTagInfo->wSetValueA(L"sampleType", -1, Info.sampleType, -1);
        }
        if(Info.channelConfig){
            pTagInfo->wSetValueA(L"channelConfig", -1, Info.channelConfig, -1);
        }
    }
}
///////////////////////////////////////////////////////////////////////////
