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

#pragma pack(push, 1)
typedef struct SAMPLE24_MONO {
    BYTE p[3];
}SAMPLE24_MONO;
#pragma pack(pop)

/*--------------------------------------------------------------------------------------*/
/* Name:        TvqGetStandardChunkINfo()                                               */
/* Description: theManager から標準チャンクの情報を取得し、setupInfo のメンバに格納する */
/* Return:      (int) 取得に成功すれば 0 失敗したら 1                                   */
/* Access:      static                                                                  */
/*--------------------------------------------------------------------------------------*/
static
int TvqGetStandardChunkInfo ( CHeaderManager* theManager, headerInfo* setupInfo )
{
	memset( setupInfo, 0, sizeof(headerInfo) );
	// get TwinVQ ID
	_snprintf_s( setupInfo->ID, _TRUNCATE, "%s", theManager->GetID().c_str() );
	// get COMM chunk information
	try {
		CCommChunk commChunk( theManager->GetPrimaryChunk("COMM") );
		setupInfo->channelMode = commChunk.GetChannelMode();
		setupInfo->bitRate = commChunk.GetBitRate();
		setupInfo->samplingRate = commChunk.GetSamplingRate();
		setupInfo->securityLevel = commChunk.GetSecurityLevel();
	}
	catch (CHeaderManager::err_FailGetChunk) {
		//std::cerr << "no COMM chunk" << std::endl;
		return 1;
	}
	return 0;
}

/*----------------------------------------------------------------------------*/
/* Name:        frtobuf()                                                     */
/* Description: copy frame data to output buffer                              */
/* Return:      none                                                          */
/* Access:      static                                                        */
/*----------------------------------------------------------------------------*/
static const double inv_0x8000 = 1.0/0x8000;
void frtobuf64f(float  input[],     /* Input  --- input data frame */
			    double bufout[],    /* Output --- output data buffer array */
			    int   frameSize,   /* Input  --- frame size */
			    int   numChannels) /* Input  --- number of channels */
{
	int	ismp, ich;
	float *ptr;
    for ( ich=0; ich<numChannels; ich++ ){
		ptr = input+ich*frameSize;
		for ( ismp=0; ismp<frameSize; ismp++ ){
            double* out = &bufout[ismp*numChannels+ich];
			*out = ptr[ismp]*inv_0x8000;//*(1<<16);
		}
	}
}
//////////////////////////////////////////////////////////////////////////
KbTvqDecoder* KbTvqDecoder::s_pActive = NULL;
//////////////////////////////////////////////////////////////////////////
KbTvqDecoder::KbTvqDecoder(void)
{
    kpi_InitMediaInfo(&m_MediaInfo);
    m_pFile = NULL;
    m_Chunk = NULL;
    m_Manager = NULL;
    m_Frame = NULL;
    m_FrameSize = 0;
    m_bfp=NULL;
    m_FirstDecode = 0;
    m_Bits = 16;
    ZeroMemory(&m_Index, sizeof(m_Index));
}
//////////////////////////////////////////////////////////////////////////
KbTvqDecoder::~KbTvqDecoder(void)
{
    free(m_Frame);
    if(m_bfp){
        TvqTerminate(&m_Index);
        bclose(m_bfp);
    }
    if(m_pFile){
        m_pFile->Release();
    }
    InterlockedCompareExchangePointer((void**)&s_pActive, NULL, this);
}
//////////////////////////////////////////////////////////////////////////
DWORD __fastcall KbTvqDecoder::Open(const KPI_MEDIAINFO *cpRequest,
                                        IKpiFile   *pFile,
                                        IKpiFolder *pFolder)
{
    if(InterlockedCompareExchangePointer((void**)&s_pActive, this, NULL) != NULL){
    //複数のインスタンスを同時に持つことは出来ない
        return 0;
    }
    DWORD dwBits = dwBits = -64;
    const char* cszFileName;
    if(!pFile->GetRealFileA(&cszFileName)){
        return 0;
    }
    /*-----------------------------------------------------------------*/
    /* OPEN INPUT BITSTREAM FILE AND OUTPUT AUDIO DATA FILE            */
    /*-----------------------------------------------------------------*/
    BFILE *bfp = bopen(pFile);
    /*-----------------------------------------------------------------*/
    /* INITIALIZE TwinVQ decoder                                       */
    /*-----------------------------------------------------------------*/
    /* get bitstream header */
    CChunkChunk* twinChunk = TvqGetBsHeaderInfo(bfp);
    if ( twinChunk == NULL ) {
        bclose(bfp);
        return FALSE;
    }
    /* parse bitstream header */
    CHeaderManager *theHeaderManager = CHeaderManager::Create( *twinChunk );
    if ( theHeaderManager == NULL) {
        delete twinChunk;
        bclose(bfp);
        return FALSE;
    }
    headerInfo setupInfo; 
    TvqGetStandardChunkInfo( theHeaderManager, &setupInfo );
    /* initialize TwinVQ */
    INDEX &index = m_Index;
    int errorCode = TvqInitialize( &setupInfo, &index, 0/*INIT_ERR_DISP_MBOX*/);
    if ( errorCode ) {
        delete twinChunk;
        delete theHeaderManager;
        bclose(bfp);
        return FALSE;
    }
    /* initialize bitstream reader */
    if ( TvqInitBsReader( &setupInfo ) ) {
        delete twinChunk;
        delete theHeaderManager;
        bclose(bfp);
        return FALSE;
    }

    UINT64 qwFileSize = pFile->GetSize();
    int frameSize = TvqGetFrameSize();
    int numChannels = TvqGetNumChannels();
    m_MediaInfo.dwSampleRate = TvqGetSamplingRate();
    m_MediaInfo.dwChannels = numChannels;
    m_MediaInfo.nBitsPerSample = dwBits;
    m_MediaInfo.dwSeekableFlags = KPI_MEDIAINFO::SEEK_FLAGS_SAMPLE;
    m_MediaInfo.dwUnitSample = frameSize;
    m_MediaInfo.qwLength = qwFileSize*10000 / (setupInfo.bitRate/8);
    m_MediaInfo.dwCount = 1;
    m_Frame = (float*)malloc(sizeof(float)*frameSize * numChannels);
    m_FrameSize = frameSize;
    m_bfp = bfp;
    m_FirstDecode = 2;
    m_Bits = dwBits;
    delete twinChunk; 
    delete theHeaderManager;
    m_pFile = pFile;
    pFile->AddRef();
    return m_MediaInfo.dwCount;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbTvqDecoder::Select(DWORD dwNumber,
                                  const KPI_MEDIAINFO **ppMediaInfo,
                                  IKpiTagInfo *pTagInfo,
                                  DWORD dwTagGetFlags)
{
    if(ppMediaInfo){
        *ppMediaInfo;
    }
    if(dwNumber > m_MediaInfo.dwCount){
        return 0;
    }
    m_MediaInfo.dwNumber = dwNumber;
    if (!ppMediaInfo) {
    //タイトル/アーティスト取得のみ
        return dwNumber;
    }
    *ppMediaInfo = &m_MediaInfo;
    return m_MediaInfo.dwNumber;
}
//////////////////////////////////////////////////////////////////////////
UINT64 WINAPI KbTvqDecoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{
    if(!m_bfp)return 0;
    if(qwPosSample == 0 && m_FirstDecode == 2){
        //ファイルオープンの直後ですでに先頭にある
        return 0;
    }
    CChunkChunk* twinChunk = NULL;
    CHeaderManager *theHeaderManager = NULL;

    if(m_FirstDecode != 2){
        //ファイルオープンの直後ではない
        BOOL bOk = FALSE;
        do{
            TvqTerminate(&m_Index);
            twinChunk = TvqGetBsHeaderInfo(m_bfp);
            if (twinChunk == NULL ) {
                break;
            }
            theHeaderManager = CHeaderManager::Create( *twinChunk );
            if ( theHeaderManager == NULL) {
                break;
            }
            headerInfo setupInfo; 
            TvqGetStandardChunkInfo( theHeaderManager, &setupInfo );
            INDEX &index = m_Index;
            int errorCode = TvqInitialize( &setupInfo, &index, 0);
            if ( errorCode ) {
                break;
            }
            if( TvqInitBsReader( &setupInfo ) ) {
                break;
            }
            bOk = TRUE;
        }
        while(0);
        delete twinChunk;
        delete theHeaderManager;
        if(!bOk){
            bclose(m_bfp);
            m_bfp = NULL;
            return 0;
        }
    }
    m_FirstDecode = 1;
    int skip = (double)qwPosSample / m_FrameSize;
    TvqSkipFrame(m_bfp, skip);
    ZeroMemory(m_Frame, sizeof(float)*m_FrameSize * m_MediaInfo.dwChannels);
    DWORD dwRet = skip * m_FrameSize;
    //delete twinChunk;
    //delete theHeaderManager;
    return dwRet;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbTvqDecoder::Render(BYTE* pBuffer, DWORD dwSizeSample)
{
    if(!m_bfp)return 0;
    if(m_FirstDecode){
        //Dummy でデコード
        int i;
        for(i = 0; i < 2; i++){
            if(TvqReadBsFrame(&m_Index, m_bfp)){
                TvqDecodeFrame(&m_Index, m_Frame);
            }
        }
        m_FirstDecode = 0;
    }
    if(TvqReadBsFrame(&m_Index, m_bfp)){
        TvqDecodeFrame(&m_Index, m_Frame);
        frtobuf64f(m_Frame, (double*)pBuffer, m_FrameSize, m_MediaInfo.dwChannels);
        return dwSizeSample;
    }
    return 0;
}
//////////////////////////////////////////////////////////////////////////
