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

typedef struct 
{
    int version;
    int channels;
    int sampling_rate;
    int bitrate;
    int length;
    int object_type;
    int headertype;
} faadAACInfo;

//faad の common/faad/aacinfo.c を改変
//get_AAC_format:   第1引数を char* から FILE* に変更
//read_ADIF_header: 第1引数を FILE_STREAM* から IKpiFile* に変更
//read_ADTS_header: 第1引数を FILE_STREAM* から IKpiFile* に変更

static int get_AAC_format(IKpiFile *file, faadAACInfo *info,
                          unsigned long **seek_table, int *seek_table_len,
                          int no_seek_table);
static int read_ADIF_header(IKpiFile *file, faadAACInfo *info);
static int read_ADTS_header(IKpiFile *file, faadAACInfo *info,
                            unsigned long **seek_table, int *seek_table_len,
                            int tagsize, int no_seek_table);
static int StringComp(char const *str1, char const *str2, unsigned long len){return memcmp(str1, str2, len);}

#define ADIF_MAX_SIZE 30 /* Should be enough */
#define ADTS_MAX_SIZE 10 /* Should be enough */

static int sample_rates[] = {96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000};

//////////////////////////////////////////////////////////////////////////////
int read_ADIF_header(IKpiFile *pFile, faadAACInfo *info)
{
    int bitstream;
    unsigned char buffer[ADIF_MAX_SIZE];
    int skip_size = 0;
    int sf_idx;

    /* Get ADIF header data */
    info->headertype = 1;

    if(pFile->Read(buffer, ADIF_MAX_SIZE) < ADIF_MAX_SIZE)
		return -1;

    /* copyright string */
    if(buffer[0] & 0x80)
        skip_size += 9; /* skip 9 bytes */

    bitstream = buffer[0 + skip_size] & 0x10;
    info->bitrate = ((unsigned int)(buffer[0 + skip_size] & 0x0F)<<19)|
        ((unsigned int)buffer[1 + skip_size]<<11)|
        ((unsigned int)buffer[2 + skip_size]<<3)|
        ((unsigned int)buffer[3 + skip_size] & 0xE0);

    if (bitstream == 0)
    {
        info->object_type =  ((buffer[6 + skip_size]&0x01)<<1)|((buffer[7 + skip_size]&0x80)>>7);
        sf_idx = (buffer[7 + skip_size]&0x78)>>3;
    } else {
        info->object_type = (buffer[4 + skip_size] & 0x18)>>3;
        sf_idx = ((buffer[4 + skip_size] & 0x07)<<1)|((buffer[5 + skip_size] & 0x80)>>7);
    }
    info->sampling_rate = sample_rates[sf_idx];

    return 0;
}
//////////////////////////////////////////////////////////////////////////////
int read_ADTS_header(IKpiFile *pFile, faadAACInfo *info,
                     unsigned long **seek_table, int *seek_table_len,
                     int tagsize, int no_seek_table)
{
    /* Get ADTS header data */
    unsigned char buffer[ADTS_MAX_SIZE];
    int frames, framesinsec=0, t_framelength = 0, frame_length, sr_idx, ID;
    int second = 0, pos;
    float frames_per_sec = 0;
    unsigned long bytes;
	unsigned long *tmp_seek_table = NULL;

    info->headertype = 2;

    /* Read all frames to ensure correct time and bitrate */
    for(frames=0; /* */; frames++, framesinsec++)
    {
        pos = pFile->Seek(0, FILE_CURRENT);

        /* 12 bit SYNCWORD */
        bytes = pFile->Read(buffer, ADTS_MAX_SIZE);

        if(bytes != ADTS_MAX_SIZE)
        {
            /* Bail out if no syncword found */
            break;
        }

		/* check syncword */
        if (!((buffer[0] == 0xFF)&&((buffer[1] & 0xF6) == 0xF0)))
            break;

        if(!frames)
        {
            /* fixed ADTS header is the same for every frame, so we read it only once */
            /* Syncword found, proceed to read in the fixed ADTS header */
            ID = buffer[1] & 0x08;
            info->object_type = (buffer[2]&0xC0)>>6;
            sr_idx = (buffer[2]&0x3C)>>2;
            info->channels = ((buffer[2]&0x01)<<2)|((buffer[3]&0xC0)>>6);

            frames_per_sec = sample_rates[sr_idx] / 1024.f;
        }

        /* ...and the variable ADTS header */
        if (ID == 0) {
            info->version = 4;
        } else { /* MPEG-2 */
            info->version = 2;
        }
        frame_length = ((((unsigned int)buffer[3] & 0x3)) << 11)
            | (((unsigned int)buffer[4]) << 3) | (buffer[5] >> 5);

        t_framelength += frame_length;

		//if(!file->http)
		if(1)
        {
			if(framesinsec == 43)
				framesinsec = 0;

			if(framesinsec == 0 && seek_table_len)
			{
				tmp_seek_table = (unsigned long *) realloc(tmp_seek_table, (second + 1) * sizeof(unsigned long));
				tmp_seek_table[second] = pos;
			}
			if(framesinsec == 0)
				second++;
		}

		/* NOTE: While simply skipping ahead by reading may seem to be more work than seeking,
			it is actually much faster, and keeps compatibility with streaming */
		pFile->Seek(frame_length - ADTS_MAX_SIZE, FILE_CURRENT);
        /*for(i=0; i < frame_length - ADTS_MAX_SIZE; i++)
        {
			if(read_byte_filestream(file) < 0)
				break;
        }*/
    }

	if(seek_table_len)
	{
		*seek_table_len = second;
		*seek_table = tmp_seek_table;
	}

    info->sampling_rate = sample_rates[sr_idx];
    info->bitrate = (int)(((t_framelength / frames) * (info->sampling_rate/1024.0)) +0.5)*8;

#if 0
	if(file->http)
    {
		/* Since we only use 5 seconds of aac data to get a rough bitrate, we must use a different
		   method of calculating the overall length */
		if(filelength_filestream(file))
		{
			info->length = (int)((filelength_filestream(file)/(((info->bitrate*8)/1024)*16))*1000);
		}
		else
		{
			/* Since the server didnt tell us how long the file is, 
			   we have no way of determining length */
			info->length = 0;
		}
	}
	else
#endif
    {
		//info->length = (int)((float)(frames/frames_per_sec))*1000;
		info->length = MulDiv(frames, 1000, frames_per_sec);
	}

    return 0;
}
//////////////////////////////////////////////////////////////////////////////
int get_AAC_format(IKpiFile *pFile, faadAACInfo *info,
                   unsigned long **seek_table, int *seek_table_len,
                   int no_seek_table)
{
    unsigned long tagsize;
	char buffer[10];
    UINT64 file_len;
    unsigned char adxx_id[5];

    memset(info, 0, sizeof(faadAACInfo));
  
    file_len = pFile->GetSize();
    pFile->Seek(0, FILE_BEGIN);
    /* Skip the tag, if it's there */
    if(pFile->Read(buffer, 10) != 10){
        return -1;
    }
    if (StringComp(buffer, "ID3", 3) == 0)
	{
		//unsigned int i;

        /* high bit is not used */
        tagsize = (buffer[6] << 21) | (buffer[7] << 14) |
            (buffer[8] <<  7) | (buffer[9] <<  0);

        /*for(i=0; i < tagsize; i++)
			if(read_byte_filestream(file) < 0)
				return -1;
        */
        pFile->Seek(tagsize, FILE_CURRENT);
        tagsize += 10;
    }
	else
	{
        tagsize = 0;
		/* Simple hack to reset to the beginning */
        pFile->Seek(0, FILE_BEGIN);
    }

	if(file_len)
	    file_len -= tagsize;

    pFile->Read(adxx_id, 2);

    adxx_id[5-1] = 0;
    info->length = 0;

	// Determine the header type of the file, check the first two bytes
    if(StringComp((const char*)adxx_id, "AD", 2) == 0)
    {
        // We think its an ADIF header, but check the rest just to make sure
        pFile->Read(adxx_id + 2, 2);
		if(StringComp((const char*)adxx_id, "ADIF", 4) == 0)
		{
			read_ADIF_header(pFile, info);
		}
    }
    else
    {
		/* No ADIF, check for ADTS header */
        if ((adxx_id[0] == 0xFF)&&((adxx_id[1] & 0xF6) == 0xF0))
        {
			/* ADTS  header located */
			/* Since this routine must work for streams, we can't use the seek function to go backwards, thus 
			   we have to use a quick hack as seen below to go back where we need to. */
			
			/*if(file->buffer_offset >= 2)
			{
				// simple seeking hack, though not really safe, the probability of it causing a problem is low.
				file->buffer_offset -= 2;
				file->file_offset -= 2;
			}
            */
            pFile->Seek(-2, FILE_CURRENT);
            read_ADTS_header(pFile, info, seek_table, seek_table_len, tagsize, no_seek_table);
        }
        else
        {
            /* Unknown/headerless AAC file, assume format: */
            info->version = 2;
            info->bitrate = 128000;
            info->sampling_rate = 44100;
            info->channels = 2;
            info->headertype = 0;
            info->object_type = 1;
        }
    }
    return 0;
}
//////////////////////////////////////////////////////////////////////////////
static int id3v2_tag(unsigned char *buffer)
{
    if (memcmp(buffer, "ID3", 3) == 0){
        unsigned long tagsize;
        tagsize = (buffer[6] << 21) | (buffer[7] << 14) |
            (buffer[8] <<  7) | (buffer[9] <<  0);
        tagsize += 10;
        return tagsize;
    }
	else{
        return 0;
    }
}
//////////////////////////////////////////////////////////////////////////////
KbAacDecoder::KbAacDecoder(void)
{
    m_hDecoder = NULL;
    m_pFile = NULL;
    m_seek_table = NULL;
    m_seek_table_length = 0;
    m_buffercount = 0;
    m_bDecodeDone = TRUE;
    m_pSample = NULL;
    m_dwRemainSample = 0;
    kpi_InitMediaInfo(&m_MediaInfo);
}
//////////////////////////////////////////////////////////////////////////////
KbAacDecoder::~KbAacDecoder(void)
{
    Close();
}
//////////////////////////////////////////////////////////////////////////////
BOOL __fastcall KbAacDecoder::Open(const KPI_MEDIAINFO *cpRequest, 
                                   IKpiFile *pFile, 
                                   IKpiFolder *pFolder)
{
    if(!cpRequest){
        m_MediaInfo.dwNumber = 1;
        m_MediaInfo.dwCount = 1;
        m_pFile = pFile;
        pFile->AddRef();
        return 1;
    }
    int buffercount;
    int tagsize;
    DWORD samplerate = 0;
    BYTE  channels = 0;
    DWORD dwRead;
    unsigned long *seek_table = NULL;
    int seek_table_length = 0;
    m_pFile = pFile;
    pFile->AddRef();
    dwRead = pFile->Read(m_buffer, FAAD_MIN_STREAMSIZE*2);
    if(dwRead != FAAD_MIN_STREAMSIZE*2){
        Close();
        return FALSE;
    }
    //id3v2 タグを飛ばす
    tagsize = id3v2_tag(m_buffer);
    if(tagsize){
        if(pFile->Seek(tagsize, FILE_BEGIN) != tagsize){
            Close();
            return FALSE;
        }
        dwRead = pFile->Read(m_buffer, FAAD_MIN_STREAMSIZE*2);
        if(dwRead != FAAD_MIN_STREAMSIZE*2){
            Close();
            return FALSE;
        }
    }
    m_hDecoder = NeAACDecOpen();
    if((buffercount = NeAACDecInit(m_hDecoder, m_buffer, sizeof(m_buffer), &samplerate, &channels)) < 0){
		Close();
        return FALSE;
    }
    if(buffercount > 0){
        int k;
        for (k = 0; k < (FAAD_MIN_STREAMSIZE*2 - buffercount); k++){
		    m_buffer[k] = m_buffer[k + buffercount];
        }
        if(pFile->Read(m_buffer + (FAAD_MIN_STREAMSIZE*2) - buffercount,  buffercount) != buffercount){
            Close();
            return FALSE;
        }
		buffercount = 0;
	}
    faadAACInfo file_info = {0};
    
    UINT64 qwCurPos = pFile->Seek(0, FILE_CURRENT);//ファイルポインタを保存
    if(get_AAC_format(pFile, &file_info, &seek_table, &seek_table_length, 0) < 0){
        Close();
        return FALSE;
    }
    pFile->Seek(qwCurPos, FILE_BEGIN);//元の位置に戻す
//
    NeAACDecConfigurationPtr pconfig = NeAACDecGetCurrentConfiguration(m_hDecoder);
    pconfig->outputFormat = FAAD_FMT_DOUBLE;//64bit float
    m_MediaInfo.dwSampleRate = samplerate;
    m_MediaInfo.dwChannels = channels;
    m_MediaInfo.nBitsPerSample = -64;
    m_MediaInfo.dwSeekableFlags = (file_info.headertype == 2) ? KPI_MEDIAINFO::SEEK_FLAGS_ACCURATE : 0;
    m_MediaInfo.dwUnitSample = 0;//いくつでも OK
    m_MediaInfo.qwLength = (UINT64)file_info.length * 10000;
    if(m_MediaInfo.qwLength == 0 && file_info.bitrate != 0){
        //曲の長さがわからない場合はビットレートとファイルサイズを元に
        //近似値を計算する
        UINT64 qwSize = pFile->GetSize();
        m_MediaInfo.qwLength = 1000 * 10000 * qwSize / (file_info.bitrate/8);
    }
    m_MediaInfo.dwNumber = 1;
    m_MediaInfo.dwCount = 1;
    m_buffercount = buffercount;
    m_bDecodeDone = FALSE;
    m_seek_table = seek_table;
    m_seek_table_length = seek_table_length;
    return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
void __fastcall KbAacDecoder::Close(void)
{
    if(m_hDecoder){
        NeAACDecClose(m_hDecoder);
        m_hDecoder = NULL;
    }
    if(m_seek_table){
        free(m_seek_table);
        m_seek_table = NULL;
    }
    if(m_pFile){
        m_pFile->Release();
        m_pFile = NULL;
    }
    m_seek_table_length = 0;
    m_buffercount = 0;
    m_pSample = NULL;
    m_dwRemainSample = 0;
}
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbAacDecoder::Select(DWORD dwNumber,
                                  const KPI_MEDIAINFO **ppMediaInfo,
                                  IKpiTagInfo *pTagInfo,
                                  DWORD dwTagGetFlags)
{
    if (dwNumber != 1 || //1ファイル1曲が前提
        m_MediaInfo.dwCount != 1) {//ファイルオープンに失敗している
        return 0;
    }
    if (!ppMediaInfo) {//タグ取得のみ
        //タイトル/アーティストのみ取得は未対応(本体に任せる)
        return 1;
    }
    if (pTagInfo && 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 KbAacDecoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{
    if(qwPosSample == 0 && !m_pSample){//既に先頭位置にいる
        return 0;
    }
    if(m_MediaInfo.dwSampleRate == 0){
        return 0;
    }
    m_bDecodeDone = TRUE;
    if(!m_hDecoder){
        return 0;
    }
    DWORD dwOffsetSec = 0;
    NeAACDecClose(m_hDecoder);
    m_hDecoder = NeAACDecOpen();
   
    if(m_seek_table && m_seek_table_length > 0){//シーク可能
        dwOffsetSec = kpi_SampleTo100ns(qwPosSample, m_MediaInfo.dwSampleRate) / (1000*10000);
        //dwOffsetSec = dwPos / 1000;
        if(dwOffsetSec >= m_seek_table_length){
            dwOffsetSec = m_seek_table_length-1;
        }
        if(m_pFile->Seek(m_seek_table[dwOffsetSec], SEEK_SET) != m_seek_table[dwOffsetSec]){
            m_pFile->Seek(m_seek_table[0], SEEK_SET);
            qwPosSample = 0;
        }
    }
    else{//シーク不可。先頭位置に戻す
        m_pFile->Seek(0, SEEK_SET);
        qwPosSample = 0;
        dwOffsetSec = 0;
    }
    m_buffercount = 0;
    DWORD dwRead = m_pFile->Read(m_buffer, FAAD_MIN_STREAMSIZE*2);
    if(dwRead != FAAD_MIN_STREAMSIZE*2){
        return 0;
    }
    //id3v2 タグを飛ばす
    int tagsize = id3v2_tag(m_buffer);
    if(tagsize){
        if(m_pFile->Seek(tagsize, FILE_BEGIN) != tagsize){
            Close();
            return 0;
        }
        dwRead = m_pFile->Read(m_buffer, FAAD_MIN_STREAMSIZE*2);
        if(dwRead != FAAD_MIN_STREAMSIZE*2){
            return 0;
        }
    }
    DWORD samplerate = 0;
    BYTE  channels = 0;
    int buffercount;
    if((buffercount = NeAACDecInit(m_hDecoder, m_buffer, FAAD_MIN_STREAMSIZE*2, &samplerate, &channels)) < 0){
        return 0;
    }
    NeAACDecConfigurationPtr pconfig = NeAACDecGetCurrentConfiguration(m_hDecoder);
    pconfig->outputFormat = FAAD_FMT_DOUBLE;//64bit float
    if(buffercount > 0){
        int k;
        for (k = 0; k < (FAAD_MIN_STREAMSIZE*2 - buffercount); k++){
            m_buffer[k] = m_buffer[k + buffercount];
        }
        if(m_pFile->Read(m_buffer + (FAAD_MIN_STREAMSIZE*2) - buffercount, buffercount) != buffercount){
            return 0;
        }
    }
    m_buffercount = 0;
    m_bDecodeDone = FALSE;
    m_pSample = NULL;
    m_dwRemainSample = 0;
    return kpi_100nsToSample(dwOffsetSec * 1000 * 10000, m_MediaInfo.dwSampleRate);
}
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbAacDecoder::Render(BYTE *Buffer, DWORD dwSizeSample)
{
    if(m_bDecodeDone){
        return 0;
    }
    const DWORD cdwBlockAlign = sizeof(double) * m_MediaInfo.dwChannels;
    DWORD dwRetSample = 0;
    DWORD dwCopySample = m_dwRemainSample;
    if(dwCopySample){
        if(dwCopySample > dwSizeSample){
            m_dwRemainSample -= dwSizeSample;
            dwCopySample = dwSizeSample;
        }
        else{
            m_dwRemainSample -= dwCopySample;
        }
        memcpy(Buffer, m_pSample, dwCopySample * cdwBlockAlign);
        m_pSample += dwCopySample*cdwBlockAlign;
        dwRetSample += dwCopySample;
    }
    while(dwRetSample < dwSizeSample){
        NeAACDecFrameInfo frameInfo;
        if(m_buffercount > 0) {
            int k;
            for (k = 0; k < (FAAD_MIN_STREAMSIZE*2 - m_buffercount); k++){
                m_buffer[k] = m_buffer[k + m_buffercount];
            }
            DWORD dwRead = m_pFile->Read(m_buffer + (FAAD_MIN_STREAMSIZE*2) - m_buffercount, m_buffercount);
            if(dwRead != m_buffercount){
                m_bDecodeDone = TRUE;
                return dwRetSample;
            }
            m_buffercount = 0;
        }
        m_pSample = (BYTE*)NeAACDecDecode(m_hDecoder, &frameInfo, m_buffer, sizeof(m_buffer));
        if(frameInfo.error || !m_pSample){
            m_bDecodeDone = TRUE;
            return dwRetSample;
        }
        m_buffercount += frameInfo.bytesconsumed;
        m_dwRemainSample = dwCopySample = frameInfo.samples / m_MediaInfo.dwChannels;
        if(dwRetSample + dwCopySample > dwSizeSample){
            dwCopySample = dwSizeSample - dwRetSample;
        }
        memcpy(Buffer + dwRetSample * cdwBlockAlign, m_pSample, dwCopySample * cdwBlockAlign);
        m_pSample += (dwCopySample * cdwBlockAlign);
        m_dwRemainSample -= dwCopySample;
        dwRetSample += dwCopySample;
    }
    return dwRetSample;
}
//////////////////////////////////////////////////////////////////////////////
