#include <windows.h>
#include <stdlib.h>

#include "kbmpg123_decoder.h"
#include "kbmpg123_config.h"

////////////////////////////////////////////////////////
void KbMpg123Decoder::GetDefaultDecoder(wchar_t* pszDecoder, int nSize)
{
#ifdef _WIN64
    static const char* default_decoders[] = { "AVX", "x86-x64", "generic", "" };
#else
    static const char* default_decoders[] = { "SSE", "generic", "" };
#endif
    pszDecoder[0] = 0;
    const char** supported_decoders = mpg123_supported_decoders();
    int i, j;
    for (i = 0; default_decoders[i]; i++) {
        for (j = 0; supported_decoders[j]; j++) {
            if (strcmp(default_decoders[i], supported_decoders[j]) == 0) {
                MultiByteToWideChar(CP_ACP, 0, default_decoders[i], -1, pszDecoder, nSize);
                pszDecoder[nSize -1] = 0;
                return;
            }
        }
    }
}
////////////////////////////////////////////////////////
bool KbMpg123Decoder::IsSupportDecoder(const wchar_t* cszDecoder)
{
    if(!cszDecoder[0])return false;
    char decoder_a[32];
    WideCharToMultiByte(CP_ACP, 0, cszDecoder, -1, decoder_a, _countof(decoder_a), NULL, NULL);
    decoder_a[_countof(decoder_a)-1] = 0;
    const char** supported_decoders = mpg123_supported_decoders();
    for (int i = 0; supported_decoders[i]; i++) {
        if (strcmp(decoder_a, supported_decoders[i]) == 0) {
            return true;
        }
    }
    return false;
}
////////////////////////////////////////////////////////
KbMpg123Decoder::KbMpg123Decoder(void)
{
    m_dwInputBufLen = 0;
    m_bEof = 0;
    m_qwEndSample = 0;
    m_dwOutputRemain = 0;
    m_dwStartSample = 0;
    m_qwDecodedSample = 0;
    m_dwSkipRemain = 0;
    m_dwOutputBufSizeSample = 0;
    m_dwBlockAlign = 0;
    m_decode_status = MPG123_NEED_MORE;
    m_mpg123 = NULL;
    m_pFile = NULL;
    ZeroMemory(m_OutputBuf, sizeof(m_OutputBuf));
    kpi_InitMediaInfo(&m_MediaInfo);
}
////////////////////////////////////////////////////////
KbMpg123Decoder::~KbMpg123Decoder(void)
{
    if(m_mpg123){
        mpg123_delete(m_mpg123);
    }
    if(m_pFile){
        m_pFile->Release();
    }
}
DWORD __fastcall KbMpg123Decoder::Open(const KPI_MEDIAINFO *cpRequest,
                                       IKpiFile   *pFile,
                                       IKpiFolder *pFolder,
                                       IKpiConfig *pConfig)
{
    if(!cpRequest){//タグ取得専用モード
        m_pFile = pFile;
        pFile->AddRef();
        m_MediaInfo.dwNumber = 1;//常に曲番号=1
        m_MediaInfo.dwCount = 1; //常に曲数=1
        return 1;//中身を調べずに曲数(=1)を返す
    }
    kpi_InitMediaInfo(&m_MediaInfo);
    if(!m_Mp3Info.GetMp3Info(pFile)){
        return 0;
    }
    m_mpg123 = mpg123_new(NULL, NULL);
    if(!m_mpg123){
        return 0;
    }
    DWORD dwFlags = //MPG123_FORCE_FLOAT |      //float で出力
                    MPG123_IGNORE_INFOFRAME | //Xing/Info ヘッダを無視（スキップは m_dwStartSample に含む）
                    MPG123_NO_RESYNC |        //データを渡す段階で既に同期ヘッダを検索しているので libmpg123 には処理させない
                    0;
    mpg123_param(m_mpg123, MPG123_FLAGS, dwFlags, 0);
    //デコーダを選択
    kbmpg123_Settings settings;
    kbmpg123_GetSettings(pConfig, &settings);
    if (!IsSupportDecoder(settings.szDecoder)) {//サポートしないデコーダが選択されたら既定値にする
        GetDefaultDecoder(settings.szDecoder, _countof(settings.szDecoder));
    }
    char decoder_a[32];
    WideCharToMultiByte(CP_ACP, 0, settings.szDecoder, -1, decoder_a, _countof(decoder_a), NULL, NULL);
    decoder_a[_countof(decoder_a)-1] = 0;
    mpg123_decoder(m_mpg123, decoder_a);
#ifdef _DEBUG
    const char *decoder = mpg123_current_decoder(m_mpg123);
    OutputDebugString(L"decoder=");
    OutputDebugStringA(decoder);
    OutputDebugString(L"\r\n");
#endif
    const int nBps = sizeof(mpg123_float) * 8;
    m_dwBlockAlign = (nBps / 8)* m_Mp3Info.GetChannels();
    m_dwStartSample = m_Mp3Info.GetStartSample();
    m_qwEndSample = m_Mp3Info.GetEndSample();
    m_MediaInfo.dwSampleRate = m_Mp3Info.GetSampleRate();
    m_MediaInfo.dwChannels = m_Mp3Info.GetChannels();
    m_MediaInfo.nBitsPerSample = -nBps;//float なので負の値
    m_MediaInfo.dwUnitSample = 0;
    m_MediaInfo.qwLength = m_Mp3Info.GetLength100ns();
    m_MediaInfo.dwSeekableFlags = KPI_MEDIAINFO::SEEK_FLAGS_SAMPLE;
    m_MediaInfo.dwNumber = 1;//常に曲番号=1
    m_MediaInfo.dwCount = 1; //常に曲数=1
    m_pFile = pFile;
    pFile->AddRef();
    return 1;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbMpg123Decoder::Select(DWORD dwNumber,
                                     const KPI_MEDIAINFO **ppMediaInfo,
                                     IKpiTagInfo *pTagInfo,
                                     DWORD dwTagGetFlags)
{
    if(ppMediaInfo){
        *ppMediaInfo = NULL;
    }
    if(dwNumber > m_MediaInfo.dwCount){//番号がおかしい
        return 0;
    }
    if(pTagInfo){
        while(1){//ID3v2=>APE=>RIFF=>Lyric3v2=>ID3v1 の順に取得
            if(pTagInfo->GetTagInfo(m_pFile, NULL, KPI_TAGTYPE_ID3V2, dwTagGetFlags) == 0){
                break;
            }
            if(pTagInfo->GetTagInfo(m_pFile, NULL, KPI_TAGTYPE_APE, dwTagGetFlags) == 0){
                break;
            }
            if(pTagInfo->GetTagInfo(m_pFile, NULL, KPI_TAGTYPE_RIFF, dwTagGetFlags) == 0){
                break;
            }
            if(pTagInfo->GetTagInfo(m_pFile, NULL, KPI_TAGTYPE_LYR3TAG, dwTagGetFlags) == 0){
                break;
            }
            if(pTagInfo->GetTagInfo(m_pFile, NULL, KPI_TAGTYPE_ID3V1, dwTagGetFlags) == 0){
                break;
            }
            break;
        }
        //pTagInfo->SetOverwrite(FALSE);//上書き禁止(デフォルトが禁止なので呼ばない)
        m_Mp3Info.GetTagInfo(pTagInfo, dwTagGetFlags);
    }
    if(!ppMediaInfo){
        return 1;
    }
    m_MediaInfo.dwNumber = dwNumber;
    *ppMediaInfo = &m_MediaInfo;
    return m_MediaInfo.dwCount;
}
//////////////////////////////////////////////////////////////////////////
UINT64 WINAPI KbMpg123Decoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{//シーク（戻り値はシーク後の再生位置）
    m_dwInputBufLen = 0;
    m_dwOutputRemain = 0;
    m_qwDecodedSample = 0;
    m_dwSkipRemain = m_dwStartSample;
    m_dwOutputBufSizeSample = 0;
    m_bEof = FALSE;
    mpg123_open_feed(m_mpg123);
    m_decode_status = MPG123_NEED_MORE;
    UINT64 qwRetSample = m_Mp3Info.Mp3SeekSample(qwPosSample);
    DWORD  dwDiscardSample = 0;
    //目的の位置との差を求め、その分だけ次回以降の Render 時にスキップする
    //差分があっても本体が補正するので実際には不要だが大した手間でもないので。
    if(qwRetSample < qwPosSample){
        dwDiscardSample = qwPosSample - qwRetSample;
        m_dwSkipRemain += dwDiscardSample;
    }
    m_qwDecodedSample = qwRetSample;
    return qwRetSample;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbMpg123Decoder::Render(BYTE* out, DWORD dwSizeSample)
{
    //ZeroMemory(out, dwSizeSample * m_dwBlockAlign);
    const DWORD dwBlockAlign = m_dwBlockAlign;
    DWORD dwRetSample = 0;
    int err_count = 0;
    while (dwRetSample < dwSizeSample){
        if(m_dwSkipRemain > 0){
            if(m_dwOutputRemain >= m_dwSkipRemain){
                m_dwOutputRemain -= m_dwSkipRemain;
                m_qwDecodedSample += m_dwSkipRemain;
                m_dwSkipRemain = 0;
            }
            else{
                m_dwSkipRemain -= m_dwOutputRemain;
                m_qwDecodedSample += m_dwOutputRemain;
                m_dwOutputRemain = 0;
            }
        }
        if(m_dwOutputRemain){
            if(dwRetSample + m_dwOutputRemain > dwSizeSample){
                DWORD dwCopySizeSample = dwSizeSample - dwRetSample;
                memcpy(out + dwRetSample * dwBlockAlign,
                       m_OutputBuf + (m_dwOutputBufSizeSample - m_dwOutputRemain) * dwBlockAlign,
                       dwCopySizeSample  * dwBlockAlign);
                m_dwOutputRemain -= dwCopySizeSample;
                dwRetSample += dwCopySizeSample;
                break;
            }
            memcpy(out + dwRetSample * dwBlockAlign,
                   m_OutputBuf + (m_dwOutputBufSizeSample - m_dwOutputRemain) * dwBlockAlign,
                   m_dwOutputRemain * dwBlockAlign);
            dwRetSample += m_dwOutputRemain;
            m_dwOutputRemain = 0;
        }
        if(m_decode_status == MPG123_NEED_MORE){
            m_dwInputBufLen = m_Mp3Info.ReadFrames(m_InputBuf, sizeof(m_InputBuf), &m_bEof);
            mpg123_feed(m_mpg123, m_InputBuf, m_dwInputBufLen);
        }
        while(1){
            size_t processed_bytes = 0;
            DWORD dwFrameBytes = m_Mp3Info.GetFrameSamples() * dwBlockAlign;
            m_decode_status = mpg123_read(m_mpg123, m_OutputBuf, dwFrameBytes, &processed_bytes);
            if(m_decode_status == MPG123_NEW_FORMAT){
                m_decode_status = mpg123_read(m_mpg123, m_OutputBuf, dwFrameBytes, &processed_bytes);
            }
            if(m_decode_status == MPG123_NEED_MORE){
                if(m_bEof){
                   goto finish;
                }
                break;
            }
            if(m_decode_status != MPG123_OK){
                if(m_bEof){
                   goto finish;
                }
                if(++err_count > 4){
                    goto finish;
                }
                ZeroMemory(m_OutputBuf, dwFrameBytes);
                m_dwOutputBufSizeSample = m_dwOutputRemain = dwFrameBytes / dwBlockAlign;
                break;
            }
            m_dwOutputBufSizeSample = m_dwOutputRemain = processed_bytes / dwBlockAlign;
            break;
        }

    }
finish:
    if(m_qwDecodedSample + dwRetSample >= m_qwEndSample){
        dwRetSample = m_qwEndSample - m_qwDecodedSample;
    }
    m_qwDecodedSample += dwRetSample;
    if(dwRetSample < dwSizeSample){
#ifdef _DEBUG
        DWORD dw = dwRetSample;
#endif
        if(m_Mp3Info.GetEncPadding() < 0){
            //末尾の無音を削除
            //今回の Render が全て無音で、1つ前の Render の最後にも無音が
            //含まれているような場合には未対応
            //（Render の dwSize 次第で演奏時間が微妙に変わる）
            int nch = m_Mp3Info.GetChannels();
            if(nch == 1){
                while(dwRetSample){
                    mpg123_float *l = (mpg123_float*)(out + (dwRetSample - 1)*dwBlockAlign);
                    if(*l == 0.0){
                        dwRetSample--;
                    }
                    else{
                        break;
                    }
                }
            }
            else{
                while(dwRetSample){
                    mpg123_float *l = (mpg123_float*)(out + (dwRetSample - 1)*dwBlockAlign);
                    mpg123_float *r = l + 1;
                    if(*l == 0.0 && *r == 0.0){
                        dwRetSample--;
                    }
                    else{
                        break;
                    }
                }

            }
        }//
#ifdef _DEBUG
        DWORD dwDiscardSample = (dw - dwRetSample);
        int diff_sample = m_qwEndSample - m_qwDecodedSample;
        if(diff_sample != 0){
            int dec_delay = m_Mp3Info.GetDecDelay();
            int enc_delay = m_Mp3Info.GetEncDelay();
            int enc_padding = m_Mp3Info.GetEncPadding();
            wchar_t szMes[512];
            wsprintf(szMes, L"ギャップレス再生に失敗? diff = %d samples\n", diff_sample);
            OutputDebugString(szMes);
            wsprintf(szMes, L"dec_delay = %d, enc_delay = %d, enc_padding = %d, discard = %d\n", dec_delay, enc_delay, enc_padding, dwDiscardSample);
            OutputDebugString(szMes);
        }
#endif
    }
    return dwRetSample;
}
//////////////////////////////////////////////////////////////////////////
