#include <windows.h>

#include "kbmac_decoder.h"
#include "mac_sdk/source/maclib/APELink.h"

///////////////////////////////////////////////////////////
class KbApeLink :public APE::CAPELink
{
public:
    KbApeLink(IKpiFile *pFile);
};
///////////////////////////////////////////////////////////
KbApeLink::KbApeLink(IKpiFile *pFile)
    :CAPELink(L"")
{
    UINT64 qwSize = pFile->GetSize();
    if(qwSize > 1 * 1024 * 1024){
        return;
    }
    char *pBuffer = (char*)malloc((size_t)qwSize + 1);
    qwSize = pFile->Read(pBuffer, (DWORD)qwSize);
    pBuffer[(size_t)qwSize] = 0;
    ParseData(pBuffer, L"");
    free(pBuffer);
}
///////////////////////////////////////////////////////////
CApeIO::CApeIO()
    :CIO()
{
    m_pFile = NULL;
}
///////////////////////////////////////////////////////////
CApeIO::~CApeIO()
{
    Close();
}
///////////////////////////////////////////////////////////
int CApeIO::Open(IKpiFile *pFile)
{
    m_pFile = pFile;
    pFile->AddRef();
    return TRUE;
}
///////////////////////////////////////////////////////////
int CApeIO::Close()
{
    if(m_pFile){
        m_pFile->Release();
        m_pFile = NULL;
    }
    return 0;
}
///////////////////////////////////////////////////////////
int CApeIO::Read(void * pBuffer, unsigned int nBytesToRead, unsigned int * pBytesRead)
{
    *pBytesRead = m_pFile->Read(pBuffer, nBytesToRead);
    return 0;
}
///////////////////////////////////////////////////////////
int CApeIO::Write(const void * pBuffer, unsigned int nBytesToWrite, unsigned int * pBytesWritten)
{
    return ERROR_IO_WRITE;
}
///////////////////////////////////////////////////////////
int CApeIO::Seek(APE::int64 nPosition, APE::SeekMethod nMethod)
{//CWinFileIO::Seek の実装を見る限り常に ERROR_SUCCESS を返す
    m_pFile->Seek(nPosition, nMethod);
    return ERROR_SUCCESS;
}
/*
APE::int64 CApeIO::PerformSeek()
{//CUnBitArrayBase::FillAndResetBitArray での戻り値のチェックの
 //仕方から、PerformSeek の戻り値は Seek 後の位置ではなくて
 //Seek の成功時に 0、失敗時に 0 以外を返す？
 //何のために戻り値の型を int64 にしてるのだろうか…
    UINT64 pos = m_pFile->Seek(m_nSeekPosition, m_nSeekMethod);
    return pos == KPI_FILE_EOF;
}
*/
///////////////////////////////////////////////////////////
int CApeIO::SetEOF()
{
    m_pFile->Seek(0, FILE_END);
    return 0;
}
///////////////////////////////////////////////////////////
APE::int64 CApeIO::GetPosition()
{
    return m_pFile->Seek(0, FILE_CURRENT);
}
///////////////////////////////////////////////////////////
APE::int64 CApeIO::GetSize()
{
    return m_pFile->GetSize();
}
///////////////////////////////////////////////////////////
int CApeIO::GetName(wchar_t *pBuffer)
{
    pBuffer[0] = 0;
    return ERROR_UNDEFINED;
}
///////////////////////////////////////////////////////////
int CApeIO::Delete()
{
    Close();
    return 0;
}
///////////////////////////////////////////////////////////
//KbMacDecoder
///////////////////////////////////////////////////////////
KbMacDecoder::KbMacDecoder(void)
    :m_IO()
{
    m_pApeDecompress = NULL;
    kpi_InitMediaInfo(&m_MediaInfo);
    m_qwStartSample = 0;
    m_qwEndSample = (UINT64)-1;
    m_qwCurrentSample = 0;
}
///////////////////////////////////////////////////////////
KbMacDecoder::~KbMacDecoder(void)
{
    delete m_pApeDecompress;
}
///////////////////////////////////////////////////////////
DWORD __fastcall KbMacDecoder::Open(const KPI_MEDIAINFO *cpRequest, 
                                    IKpiFile *pFile, 
                                    IKpiFolder *pFolder)
{
    wchar_t szFileName[MAX_PATH];
    if(pFile->GetFileName(szFileName, sizeof(szFileName)) >= sizeof(szFileName)){
        return 0;
    }
    if(szFileName[0]){
        const wchar_t *pszExt = wcsrchr(szFileName, L'.');
        if(pszExt && _wcsicmp(pszExt, L".apl") == 0){
            KbApeLink link(pFile);
            if(!link.GetIsLinkFile()){
                return 0;
            }
            const wchar_t *pszImageFileName = link.GetImageFilename();
            if(!pszImageFileName || !pszImageFileName[0]){
                return 0;
            }
            if(!pFolder->OpenFile(pszImageFileName, &pFile)){
                return 0;
            }
            m_qwStartSample = link.GetStartBlock();
            m_qwEndSample = link.GetFinishBlock();
        }
    }
    m_IO.Open(pFile);
    m_pApeDecompress = CreateIAPEDecompressEx(&m_IO, NULL/*, true*/);
    if(!m_pApeDecompress){
        return FALSE;
    }
    APE::int64 SampleRate = m_pApeDecompress->GetInfo(APE::IAPEDecompress::APE_INFO_SAMPLE_RATE, 0, 0);
    APE::int64 Bits = m_pApeDecompress->GetInfo(APE::IAPEDecompress::APE_INFO_BITS_PER_SAMPLE, 0, 0);
    APE::int64 Channels = m_pApeDecompress->GetInfo(APE::IAPEDecompress::APE_INFO_CHANNELS, 0, 0);
    APE::int64 sample = m_pApeDecompress->GetInfo(APE::IAPEDecompress::APE_DECOMPRESS_TOTAL_BLOCKS, 0, 0);
    if((SampleRate == 0) || (Channels == 0) || (Bits == 0)){
        return 0;
    }
    if(m_qwEndSample != (UINT64)-1){
        sample = m_qwEndSample - m_qwStartSample;
    }
    m_MediaInfo.dwSampleRate = SampleRate;
    m_MediaInfo.dwChannels = Channels;
    m_MediaInfo.nBitsPerSample = Bits;
    m_MediaInfo.qwLength = kpi_SampleTo100ns(sample, SampleRate);
    m_MediaInfo.dwSeekableFlags = KPI_MEDIAINFO::SEEK_FLAGS_SAMPLE;
    m_MediaInfo.dwUnitSample = 0;
    m_MediaInfo.dwNumber = 1;
    m_MediaInfo.dwCount = 1;
    return 1;
}
///////////////////////////////////////////////////////////
DWORD WINAPI KbMacDecoder::Select(DWORD dwNumber,
                                  const KPI_MEDIAINFO **ppMediaInfo,
                                  IKpiTagInfo *pTagInfo,
                                  DWORD dwTagGetFlags)
{
    if(ppMediaInfo){
        *ppMediaInfo = NULL;
    }
    if(dwNumber > m_MediaInfo.dwCount){//番号がおかしい
        return 0;
    }
    *ppMediaInfo = &m_MediaInfo;
    return dwNumber;
}
///////////////////////////////////////////////////////////
UINT64 WINAPI KbMacDecoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{
    qwPosSample += m_qwStartSample;
    if(qwPosSample > (DWORD)-1){
        qwPosSample = 0;
    }
    m_pApeDecompress->Seek(qwPosSample);
    m_qwCurrentSample = qwPosSample;
    return qwPosSample - m_qwStartSample;
}
///////////////////////////////////////////////////////////
DWORD WINAPI KbMacDecoder::Render(BYTE *Buffer, DWORD dwSizeSample)
{
    if(m_qwCurrentSample >= m_qwEndSample){
        return 0;
    }
    APE::int64 ret = 0;
    m_pApeDecompress->GetData(Buffer, dwSizeSample , &ret);
    if(m_qwCurrentSample + ret >= m_qwEndSample){
        ret = m_qwEndSample - m_qwCurrentSample;
    }
    m_qwCurrentSample += ret;
    return ret;
}
///////////////////////////////////////////////////////////
