#include <windows.h>
#include <wchar.h>

#include "kmp_pi.h"
#include "kbmp4_decoder.h"
extern "C" {
#include "neaacdec.h"
#include "mp4ff/mp4ff.h"
}
//////////////////////////////////////////////////////////////////////////////
//mp4ff/metadata.c で実装
//処理速度の関係上内部データに直接アクセスしたかったので元ソース内に追加
extern "C" int32_t mp4ff_meta_find_by_name_added_by_kobarin(const mp4ff_t *f, 
                                                            const char *item, 
                                                            const char **value);
extern "C" int32_t mp4ff_meta_get_by_index_added_by_kobarin(const mp4ff_t *f, 
                                                            uint32_t index,
                                                            const char **item, 
                                                            const char **value,
                                                            uint32_t   *value_size);
//////////////////////////////////////////////////////////////////////////////
//KbMp4Decoder
//////////////////////////////////////////////////////////////////////////////
KbMp4Decoder::KbMp4Decoder(void)
{
    m_pMp4ff = NULL;
    m_pFile = NULL;
    m_callback.read = read_callback;
    m_callback.seek = seek_callback;
    m_callback.truncate = NULL;
    m_callback.write = NULL;
    m_callback.user_data = NULL;
    m_qwLastSample = 0;
    m_qwCurrentSample = 0;
    m_nNumSampleId = 0;
    m_nCurrentSampleId = 0;
    m_nTrack = 0;
    kpi_InitMediaInfo(&m_MediaInfo);
}
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbMp4Decoder::Select(DWORD dwNumber,
                                  const KPI_MEDIAINFO **ppMediaInfo,
                                  IKpiTagInfo *pTagInfo,
                                  DWORD dwTagGetFlags)
{
    if(ppMediaInfo){
        *ppMediaInfo = NULL;
    }
    if (dwNumber > m_MediaInfo.dwCount){ //曲番号が不正
        return 0;
    }
    if(!ppMediaInfo){//タグ取得のみ
        if (pTagInfo && m_pFile) {
            //再生はしないのでファイルポインタの保存は不要
            //タイトル/アーティストのみ取得
            GetTagInfo(pTagInfo, dwTagGetFlags);
        }
        return 1;
    }
    if(m_pFile) {
        //ファイルポインタの保存
        UINT64 qwSavedPos = m_pFile->Seek(0, FILE_CURRENT);
        GetTagInfo(pTagInfo, dwTagGetFlags);
        m_pFile->Seek(qwSavedPos, FILE_BEGIN);
    }
    m_MediaInfo.dwNumber = 1;
    *ppMediaInfo = &m_MediaInfo;
    return 1;
}
//////////////////////////////////////////////////////////////////////////
UINT64 WINAPI KbMp4Decoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{
    if (qwPosSample == 0 && m_nCurrentSampleId == 0) {
        //既に先頭位置にある
        return 0;
    }
    int nStartId = 0;
    int nEndId = m_nNumSampleId;
    UINT64 qwCurPos;
    while (1) {
        //2分探索で目的の位置を探す（一発で見つける方法が分からないので）
        //mp4ff_find_sample を使えば一発で行けそうだけど使い方が分からない
        int nId = nStartId + (nEndId - nStartId) / 2;
        qwCurPos = mp4ff_get_sample_position(m_pMp4ff, m_nTrack, nId);
        if (qwCurPos == qwPosSample) {//滅多に成立しない
            nStartId = nEndId = nId;
        }
        else if (qwCurPos > qwPosSample) {
            nEndId = nId - 1;
        }
        else {
            nStartId = nId + 1;
        }
        if (nEndId <= nStartId) {
            break;
        }
    }
    m_nCurrentSampleId = nEndId - 1;//1つ手前にシークする
    if (m_nCurrentSampleId >= m_nNumSampleId) {
        m_nCurrentSampleId = m_nNumSampleId - 1;
    }
    if (m_nCurrentSampleId < 0) {
        m_nCurrentSampleId = 0;
    }
    m_qwCurrentSample = mp4ff_get_sample_position(m_pMp4ff, m_nTrack, m_nCurrentSampleId);
    return m_qwCurrentSample;
}
//////////////////////////////////////////////////////////////////////////////
void __fastcall KbMp4Decoder::GetTagInfo(IKpiTagInfo *pTagInfo,
                                         DWORD dwTagGetFlags)//TRUE のときタイトル/アーティスト以外も取得
{//画像が複数個埋め込まれている場合、１つ目しか取得出来ないことが多い
 //誰か直して下さい
    m_pFile->Seek(0, FILE_BEGIN);
    mp4ff_t *ff = m_pMp4ff;
    if (!ff) {
        return;
    }
    static const struct _S {
        const char    *cszMp4Name;
        const wchar_t *cszKmpName;
        DWORD          dwFlag;
    }s[] = {
        { "title",        SZ_KMP_NAME_TITLE_W, KPI_TAGGET_FLAG_TITLE},
        { "artist",       SZ_KMP_NAME_ARTIST_W, KPI_TAGGET_FLAG_ARTIST},
        { "album",        SZ_KMP_NAME_ALBUM_W, KPI_TAGGET_FLAG_ALBUM},
        { "genre",        SZ_KMP_NAME_GENRE_W, KPI_TAGGET_FLAG_GENRE},
        { "date",         SZ_KMP_NAME_DATE_W, KPI_TAGGET_FLAG_DATE},
        { "comment",      SZ_KMP_NAME_COMMENT_W, KPI_TAGGET_FLAG_COMMENT},
        { "album_artist", SZ_KMP_NAME_ALBUMARTIST_W, KPI_TAGGET_FLAG_ALBMART },
        { "track",        SZ_KMP_NAME_TRACKNUMBER_W, KPI_TAGGET_FLAG_TRACK },
        { "totaltracks",  SZ_KMP_NAME_TRACKTOTAL_W, KPI_TAGGET_FLAG_TRACK },
        { "disc",         SZ_KMP_NAME_DISCNUMBER_W, KPI_TAGGET_FLAG_TRACK },
        { "totaldiscs",   SZ_KMP_NAME_DISCTOTAL_W, KPI_TAGGET_FLAG_TRACK },
        { "writer",       SZ_KMP_NAME_COMPOSER_W, KPI_TAGGET_FLAG_GENERAL },
        { NULL, NULL }
    };
    int i, j;
    const int nCount = mp4ff_meta_get_num_items(ff);
    int nPictureCount = 0;
    for (i = 0; i < nCount; i++) {
        const char *item, *value;
        uint32_t value_size;
        if (!mp4ff_meta_get_by_index_added_by_kobarin(ff, i, &item, &value, &value_size)) {
            break;
        }
        j = 0;
        while (s[j].cszMp4Name) {//フィールド名が異なるものがあるか探す
            if ((s[j].dwFlag & dwTagGetFlags) && _strcmpi(s[j].cszMp4Name, item) == 0) {
                if(pTagInfo)pTagInfo->wSetValueU8(s[j].cszKmpName, -1, value, -1);//SZ_KMP_TAGINFO_NAME_XXXX の名前に置換して渡す
                break;
            }
            j++;
        }
        if(!(dwTagGetFlags & KPI_TAGGET_FLAG_GENERAL)){
            continue;
        }
        if(_stricmp(item, "cover") == 0){
            const BYTE *pPictureData = (const BYTE*)value;
            DWORD dwPictureSize = value_size;
            DWORD dwType;
            if(nPictureCount == 0){
                dwType = 3;//front
            }
            else if(nPictureCount == 1){
                dwType = 4;//back
            }
            else{
                dwType = 0;//other
            }
            if(pTagInfo)pTagInfo->SetPicture(dwType, L"", L"", L"", 0, 0, pPictureData, dwPictureSize);
            nPictureCount;
        }
        else if(_stricmp(item, "compilation") == 0){
            if(pTagInfo)pTagInfo->wSetValueW(L"compilation", -1, value[0] ? L"true" : L"false", -1);
        }
        else if (!s[j].cszMp4Name) {
            if(pTagInfo)pTagInfo->aSetValueU8(item, -1, value, -1);
        }
        //free(item);
        //free(value);
    }
    if(!(dwTagGetFlags & KPI_TAGGET_FLAG_GENERAL)){
        return;
    }
    int nBitrate = mp4ff_get_avg_bitrate(ff, 0);
    if (nBitrate > 0) {
        wchar_t szBitrate[128];
        _snwprintf_s(szBitrate, _TRUNCATE, L"%dkbps", nBitrate / 1000);
        if(pTagInfo)pTagInfo->wSetValueW(L"bitrate", -1, szBitrate, -1);
    }
}
//////////////////////////////////////////////////////////////////////////////
