#include <windows.h>

extern "C" {
#include "flac/include/FLAC/all.h"
}
#include "kbflac_tag.h"

///////////////////////////////////////////////////////////////////////////////
static void __fastcall utf8_to_utf16(const char *cszUtf8,
                                     wchar_t **ppszUtf16,
                                     int nSrcSize)
{// BOMなしUTF8 を UTF16 に変換
    // UTF8 => Unicode
    int nLenUtf16 = MultiByteToWideChar(CP_UTF8, 0, cszUtf8, nSrcSize, NULL, 0);
    wchar_t *pszUtf16;
    pszUtf16 = (wchar_t*)malloc((nLenUtf16 + 1)*sizeof(wchar_t));
    MultiByteToWideChar(CP_UTF8, 0, cszUtf8, nSrcSize, pszUtf16, nLenUtf16);
    pszUtf16[nLenUtf16] = 0;
    *ppszUtf16 = pszUtf16;
}
/*
typedef size_t (*FLAC__IOCallback_Read) (void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle);
typedef size_t (*FLAC__IOCallback_Write) (const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle);
typedef int (*FLAC__IOCallback_Seek) (FLAC__IOHandle handle, FLAC__int64 offset, int whence);
typedef FLAC__int64 (*FLAC__IOCallback_Tell) (FLAC__IOHandle handle);
typedef int (*FLAC__IOCallback_Eof) (FLAC__IOHandle handle);
typedef int (*FLAC__IOCallback_Close) (FLAC__IOHandle handle);
*/
/////////////////////////////////////////////////////////////////////////////
static size_t flac_iocallback_Read(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
{
    IKpiFile *pFile = (IKpiFile*)handle;
    return pFile->Read(ptr, size * nmemb) / size;
}
/////////////////////////////////////////////////////////////////////////////
static int flac_iocallback_Seek(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
{
    IKpiFile *pFile = (IKpiFile*)handle;
    if(pFile->Seek(offset, whence) == KPI_FILE_EOF){
        return -1;
    }
    return 0;
}
/////////////////////////////////////////////////////////////////////////////
static FLAC__int64 flac_iocallback_Tell(FLAC__IOHandle handle)
{
    IKpiFile *pFile = (IKpiFile*)handle;
    UINT64 ret = pFile->Seek(0, FILE_CURRENT);
    if(ret == KPI_FILE_EOF){
        return -1;
    }
    return ret;
}
/////////////////////////////////////////////////////////////////////////////
void __fastcall kbflac_GetTagInfoFromStream(IKpiFile *pFile,
                                            IKpiTagInfo *pInfo,
                                            DWORD dwTagGetFlags)
{
    if(!pInfo){
        return;
    }
    static const struct _S
    {
        const char    *cszField;
        const wchar_t *cszName;
        DWORD dwFlag;
    }_s[] = {
        {"title=",  L"title" , KPI_TAGGET_FLAG_TITLE},
        {"artist=", L"artist" , KPI_TAGGET_FLAG_ARTIST},
        {"album=",  L"album" , KPI_TAGGET_FLAG_ALBUM},
        {"albumartist=",  L"albumartist" , KPI_TAGGET_FLAG_ALBMART},
        {"copyright=",  L"copyright" , KPI_TAGGET_FLAG_COPYRIGHT},
        {"genre=",  L"genre" , KPI_TAGGET_FLAG_GENRE},
        {"comment=",  L"comment" , KPI_TAGGET_FLAG_COMMENT},
        {"description=",  L"description" , KPI_TAGGET_FLAG_COMMENT},
        {"tracknumber=",  L"tracknumber" , KPI_TAGGET_FLAG_TRACK},
        {"tracktotal=",  L"tracktotal" , KPI_TAGGET_FLAG_TRACK},
        {"discnumber=",  L"discnumber" , KPI_TAGGET_FLAG_TRACK},
        {"disctotal=",  L"disctotal" , KPI_TAGGET_FLAG_TRACK},
        {"replaygain_track_gain=",  L"replaygain_track_gain" , KPI_TAGGET_FLAG_RG},
        {"replaygain_track_peak=",  L"replaygain_track_peak" , KPI_TAGGET_FLAG_RG},
        {"replaygain_album_gain=",  L"replaygain_album_gain" , KPI_TAGGET_FLAG_RG},
        {"replaygain_album_peak=",  L"replaygain_album_peak" , KPI_TAGGET_FLAG_RG},
        {NULL, NULL, 0}
    };
    pFile->Seek(0, FILE_BEGIN);
    //read/seek/tell だけ実装すれば良いと metadata.h に明記されている
    FLAC__IOCallbacks io_callbacks = {
                                  flac_iocallback_Read,
                                  NULL,//flac_iocallback_Write,
                                  flac_iocallback_Seek,
                                  flac_iocallback_Tell,
                                  NULL,//flac_iocallback_Eof,
                                  NULL //flac_iocallback_Close
                                };
    FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
    if(!FLAC__metadata_chain_read_with_callbacks(chain, pFile, io_callbacks)){
        FLAC__metadata_chain_delete(chain);
        return;
    }
    FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
    FLAC__metadata_iterator_init(iterator, chain);
    do{
        FLAC__MetadataType type = FLAC__metadata_iterator_get_block_type(iterator);
        if(type != FLAC__METADATA_TYPE_VORBIS_COMMENT &&
           type != FLAC__METADATA_TYPE_PICTURE){
            continue;
        }
        FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(iterator);
        DWORD i;
        if(type == FLAC__METADATA_TYPE_VORBIS_COMMENT){
            const FLAC__StreamMetadata_VorbisComment *vc = &block->data.vorbis_comment;
            for(i = 0; i < vc->num_comments; i++){
                bool bNotify = false;
                const char *comment = (const char*)vc->comments[i].entry;//\0 で終わることが format.h に明記されている
                const _S *s = _s;
                while(s->cszField){
                    const int field_len = strlen(s->cszField);
                    if(_strnicmp(comment, s->cszField, field_len) == 0 &&
                       comment[field_len-1] == '='){
                        if((s->dwFlag & dwTagGetFlags)){
                            const char *szContentsU8 = comment + strlen(s->cszField);
                            pInfo->wSetValueU8(s->cszName, -1, szContentsU8, -1);
                            bNotify = true;
                        }
                        break;
                    }
                    s++;
                }
                if(bNotify){//通知済み
                    continue;
                }
                if(!(dwTagGetFlags & KPI_TAGGET_FLAG_GENERAL)){//一般的でないタグは取得不要
                    continue;
                }
                int j = 0;
                while(comment[j] && comment[j] != '='){
                    j++;
                }
                if(comment[j] != '='){
                    continue;
                }
                const char *szContentsU8 = comment + j + 1;
                pInfo->aSetValueU8(comment, j, szContentsU8, -1);
            }
            if(dwTagGetFlags & KPI_TAGGET_FLAG_GENERAL){//vendor を通知
                pInfo->wSetValueU8(L"vendor", -1, (const char*)vc->vendor_string.entry, -1);
            }
        }
        else if(dwTagGetFlags & KPI_TAGGET_FLAG_PICTURE){//Picture
            FLAC__StreamMetadata_Picture *pic = &block->data.picture;
            wchar_t *pszMIME;
            wchar_t *pszDesc;
            utf8_to_utf16(pic->mime_type, &pszMIME, -1);
            utf8_to_utf16((char*)pic->description, &pszDesc, -1);
            pInfo->SetPicture(pic->type, L"", pszMIME, pszDesc, pic->width, pic->height, pic->data, pic->data_length);
        }
        //break;
    }while(FLAC__metadata_iterator_next(iterator));
    FLAC__metadata_iterator_delete(iterator);
    FLAC__metadata_chain_delete(chain);
}
/////////////////////////////////////////////////////////////////////////////
