//
//  HA_WavFileObject.cpp
//  HA_server
//
//  Created by hark on 2013/07/06.
//  Copyright (c) 2013年 hark. All rights reserved.
//
#include <unistd.h>
#include "HA_log.h"
#include "HA_WavFileObject.h"

namespace HARKAndroid {
    
WavFileObject::WavFileObject() :
    thread_id(0),
    filename(""),
    fp(NULL),
    buffer(NULL),
    BeingStopped(false)
{
    int ret = ::pthread_mutex_init(&Being_Stopped_mutex, NULL);
    if(ret != 0){ LOGE("pthread_mutex_init error"); }
    wav_file_info.bits     = 0;
    wav_file_info.channels = 0;
    wav_file_info.rate     = 0;
}

WavFileObject::WavFileObject(const char * const argfilename) :
    //filenameWithFullpath(argfilename),
    filename(argfilename),
    fp(NULL),
    buffer(NULL),
    thread_id(0),
    BeingStopped(false)
{
    int ret = ::pthread_mutex_init(&Being_Stopped_mutex, NULL);
    if(ret != 0){ LOGE("pthread_mutex_init error"); }
    wav_file_info.bits     = 0;
    wav_file_info.channels = 0;
    wav_file_info.rate     = 0;
}

WavFileObject::~WavFileObject()
{
    ::pthread_mutex_destroy(&Being_Stopped_mutex);
}

int WavFileObject::SetBeingStopped()
{
    lockobject lock(&Being_Stopped_mutex);
    BeingStopped = true;
    return(0);
}

int WavFileObject::ResetBeingStopped()
{
    lockobject lock(&Being_Stopped_mutex);
    BeingStopped = false;
    return(0);
}

bool WavFileObject::isBeingStopped()
{
    lockobject lock(&Being_Stopped_mutex);
    const bool val = BeingStopped;
    return(val);
}

int WavFileObject::open()
{
    if(fp != NULL){ close(); }
    //fp = ::fopen(filenameWithFullpath.c_str(), "rb");
    fp = ::fopen(filename.c_str(), "rb");
    if(fp == NULL){ return(-1); }
    
    unsigned int size;
    char buf[5];
    buf[4] = 0;
    
    // RIFF
    fread(buf, sizeof(char), 4, fp);
    if(strncmp(buf, "RIFF", 4) != 0){ printf("No RIFF\n"); goto FileFormatErrEnd; }
    
    fread(&size, sizeof(int), 1, fp);
    //printf("size = %d\n", size);
    
    // WAVE
    fread(buf, sizeof(char), 4, fp);
    if(strncmp(buf, "WAVE", 4) != 0){ printf("No WAVE\n"); goto FileFormatErrEnd; }
    
    while(feof(fp) == 0){
        fread(buf, sizeof(char), 4, fp);
        if((strncmp(buf, "fact", 4) == 0) || (strncmp(buf, "LIST", 4) == 0)){
            /* fact/LISTの情報は使用しないのでスキップ */
            fread(&size, sizeof(int), 1, fp);
            fseek(fp, size, SEEK_CUR);
        }
        else if(strncmp(buf, "fmt ", 4) == 0){
            fread(&size, sizeof(int), 1, fp);
            fseek(fp, 2, SEEK_CUR);
            
            fread(&wav_file_info.channels, sizeof(short), 1, fp);
            fread(&wav_file_info.rate, sizeof(int), 1, fp);
            fseek(fp, 6, SEEK_CUR);
            fread(&wav_file_info.bits, sizeof(short), 1, fp);
            //wav_file_info.bytes = wav_file_info.bits / 8;
            
            // 16bit , 24bit以外はサポートしない
            if((wav_file_info.bits != 16) && (wav_file_info.bits != 24)){
                goto FileFormatErrEnd;
            }
            
            // 拡張部はスキップ
            size = size - 16;
            if(size > 0){
                fseek(fp, size, SEEK_CUR);
            }
        }
        else if(strncmp(buf, "data", 4) == 0){
            /* データサイズは使用しないのでスキップ */
            fseek(fp, 4, SEEK_CUR);
            break;
        }
        else{
            goto FileFormatErrEnd;
        }
    }
    
    start_pos = ::ftello(fp);
    
    return(0);
    
FileFormatErrEnd:
    
    close();
    return(-1);
    
}

static void *_wavloop(void *arg)
{
    WavFileObject *thiz = reinterpret_cast<WavFileObject *>(arg);
    int channels = thiz->GetChannels();
    int bits     = thiz->GetBits();
    int bytes    = bits / 8;
    int ct = 0;
    const int block = 512;
    const int iosize = channels * bytes * block;
    std::vector<unsigned char> rbuf(iosize);
    std::vector<floatx8> vdat(block);
    
    while(!thiz->isBeingStopped()){
        ct = 0;
        thiz->rewinddatatop();
        while(!thiz->iseof()){
            const size_t rsize = thiz->read_a_data(&rbuf[0], iosize);
            const int vsize = rsize / (channels * bytes);
            vdat.resize(vsize);

            for(int i = 0; i < vsize; i++){
                vdat[i].convert(&rbuf[i * channels * bytes], channels, bits);
            }
        retry:
            if(thiz->isBeingStopped()){
                goto loopend;
            }
            int ret = thiz->buffer->writeWithTimeout(vdat, 250); // 250ms
            if(ret == 0){
                // OK
            }else if(ret == ETIMEDOUT){
                //LOGI("retrying %d", ct);
                //LOGD("buf in use (%d / %d)", thiz->buffer->getdatnum(), thiz->buffer->getlen());
                goto retry;
            }else{
                LOGE("wavloop write error %d", ret);
                goto loopend;
            }
            ct++;
        }
        //LOGD("record count %d", ct);
        //LOGD("buf in use (%d / %d)", thiz->buffer->getdatnum(), thiz->buffer->getlen());
        //LOGD("sleeping...");
        //::sleep(90);
    }
loopend:
    return(0);
}


int WavFileObject::start(BufferObject &buf)
{
    ResetBeingStopped();
    buffer = &buf;
    int rr = ::pthread_create(&thread_id, NULL, _wavloop, reinterpret_cast<void *>(this));
    if(rr != 0){ LOGE("pthread_create error"); return(-1); }
    return(0);
}

int WavFileObject::stop()
{
    SetBeingStopped();
    void *ret;
    int retval = ::pthread_join(thread_id, &ret);
    return(retval);
}

int WavFileObject::close()
{
    if(fp != NULL){ ::fclose(fp); fp = NULL; }
    wav_file_info.channels = 0;
    wav_file_info.rate = 0;
    wav_file_info.bits = 0;
    start_pos = 0;
    return(0);
}
    
} // end of namespace
