//
//  HA_SocketDeviceObject.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_SocketDeviceObject.h"

namespace HARKAndroid {

SocketDeviceObject::SocketDeviceObject() :
    thread_id(0),
    BeingStopped(false),
    hostname("192.168.1.1"),
    SocketRaspDeviceHandle(NULL),
    SocketKmDeviceHandle(NULL)
{
    int ret = ::pthread_mutex_init(&Being_Stopped_mutex, NULL);
    if(ret != 0){ LOGE("pthread_mutex_init error"); }
}

SocketDeviceObject::SocketDeviceObject(const char * const arghostname) :
    thread_id(0),
    BeingStopped(false),
    hostname(arghostname),
    SocketRaspDeviceHandle(NULL),
    SocketKmDeviceHandle(NULL)
{
    int ret = ::pthread_mutex_init(&Being_Stopped_mutex, NULL);
    if(ret != 0){ LOGE("pthread_mutex_init error"); }
}

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

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

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

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

int SocketDeviceObject::open()
{
    close();
    LOGD("connect to %s", hostname.c_str());
    // checking connectivity for all known API.
    socket_device_info.api_type = SELECTED_NONE;
    SocketRaspDeviceHandle = ::WsHandleOpen(const_cast<char *>(hostname.c_str()));
    SocketKmDeviceHandle   = ::WsKmHandleOpen(const_cast<char *>(hostname.c_str()));
    if(SocketRaspDeviceHandle != NULL){
        //SocketRaspDeviceHandle = ::WsHandleOpen(const_cast<char *>(hostname.c_str()));
        //if(SocketRaspDeviceHandle == NULL){ LOGE("WsHandleOpen Failure!"); return(-1); }
        LOGI("RASP device found.");
        socket_device_info.device_type = DEVICE_RASP_ZX; // test value for ZX
        socket_device_info.api_type = RASP_API;
        socket_device_info.channels = 8; // test value for ZX
        socket_device_info.bits     = 24; // test value for ZX
        const int channels(AD_CH0_CAPTURE | AD_CH1_CAPTURE | AD_CH2_CAPTURE | AD_CH3_CAPTURE |
                           AD_CH4_CAPTURE | AD_CH5_CAPTURE | AD_CH6_CAPTURE | AD_CH7_CAPTURE);
        if(::WsSetCaptureChannels(SocketRaspDeviceHandle, 0, channels) != 0){ LOGE("WsSetCaptureChannels Failure!"); return(-1); }
        if(::WsSetAdDataFormat(SocketRaspDeviceHandle, DATA_FORMAT_32BIT_PCM) != 0){ LOGE("WsSetAdDataFormat Failure!"); return(-1); }
        if(::WsSetHeaderFlag(SocketRaspDeviceHandle, 0) != 0){ LOGE("WsSetHeaderFlag Failure!"); return(-1); }
        //if (::WsSetSamplingFreq(SocketRaspDeviceHandle, CLOCK_FREQ_48K) != 0) { LOGE("WsSetSamplingFreq Failure!"); return(-1); }
        if(::WsSetSamplingFreq(SocketRaspDeviceHandle, CLOCK_FREQ_16K) != 0){ LOGE("WsSetSamplingFreq Failure!"); return(-1); }
    }
    else if(SocketKmDeviceHandle != NULL){
        //SocketKmDeviceHandle = ::WsKmHandleOpen(const_cast<char *>(hostname.c_str()));
        //if(SocketKmDeviceHandle == NULL){ LOGE("WsKmHandleOpen Failure!"); return(-1); }
        LOGI("KM device found.");
        socket_device_info.device_type = DEVICE_HRI_SP_KM;
        socket_device_info.api_type = KM_API;
        socket_device_info.channels = 4;
        socket_device_info.bits     = 16;
        ////const int channels(AD_CH0_CAPTURE | AD_CH1_CAPTURE | AD_CH2_CAPTURE | AD_CH3_CAPTURE);
        ////if(::WsKmSetCaptureChannels(SocketKmDeviceHandle, 0, channels) != 0){ LOGE("WsKmSetCaptureChannels Failure!"); return(-1); }
        if(::WsKmSetAdDataFormat(SocketKmDeviceHandle, DATA_FORMAT_16BIT_PCM) != 0){ LOGE("WsKmSetAdDataFormat Failure!"); return(-1); }
        if(::WsKmSetHeaderFlag(SocketKmDeviceHandle, 1) != 0){ LOGE("WsKmSetHeaderFlag Failure!"); return(-1); }
        //if(::WsKmSetSamplingFreq(SocketKmDeviceHandle, CLOCK_FREQ_48K) != 0){ LOGE("WsKmSetSamplingFreq Failure!"); return(-1); }
        if(::WsKmSetSamplingFreq(SocketKmDeviceHandle, CLOCK_FREQ_16K) != 0){ LOGE("WsKmSetSamplingFreq Failure!"); return(-1); }
    }
    else{
        LOGI("Unsupported device or device not found.");
        LOGE("WsHandleOpen/WsKmHandleOpen Failure!");
        return(-1);
    }
    
    return(0);
}

static int _WsSampleDataRead(WS_HANDLE_t *h_ws, char *buffer, const size_t read_req_size)
{
    int readcnt(0);
    size_t remain(read_req_size);
    char *bufp(buffer);
    while(remain > 0){
        const int read_size = ::WsSampleDataRead(h_ws, bufp, remain);
        if(read_size == 0){ break; }
        remain -= read_size;
        bufp += read_size;
        readcnt += read_size;
    }
    return(readcnt);
}

static void *_socketdeviceloop(void *arg)
{
    SocketDeviceObject *thiz = reinterpret_cast<SocketDeviceObject *>(arg);
    int ct = 0;
    
    if(thiz->GetApiType() == RASP_API){
        const int channels = 8;
        const int blocks   = 128;
        const int iosize = channels * sizeof(int) * blocks;
        std::vector<int> rbuf(channels * blocks);
        if(::WsADStart(thiz->SocketRaspDeviceHandle) != 0){ LOGE("WsAdStart Failure!"); thiz->close(); return(0); }
        std::vector<floatx8> vdat(blocks);
        while(!thiz->isBeingStopped()){
            if(iosize == _WsSampleDataRead(thiz->SocketRaspDeviceHandle, reinterpret_cast <char *>(&rbuf[0]), iosize)) {
                ct = 0;
                for(int i=0; i<blocks; i++){ vdat[i].convert(&rbuf[i * channels], channels); }
            retryrasp:
                if(thiz->isBeingStopped()){ goto loopendrasp; }
                int ret = thiz->buffer->writeWithTimeout(vdat, 250); // 250ms
                if(ret == 0){
                    // OK
                }else if(ret == ETIMEDOUT){
                    ct++;
                    //LOGI("retrying %d", ct);
                    //LOGD("buf in use (%d / %d)", thiz->buffer->getdatnum(), thiz->buffer->getlen());
                    goto retryrasp;
                }else{
                    LOGE("socketdeviceloop write error %d", ret);
                    goto loopendrasp;
                }
            }
            else{ LOGE("socketdeviceloop _WsSampleDataRead size error"); break; }
            //LOGD("record count %d", ct);
            //LOGD("buf in use (%d / %d)", thiz->buffer->getdatnum(), thiz->buffer->getlen());
            //::sleep(90);
        }
        loopendrasp:
        if(::WsADStop(thiz->SocketRaspDeviceHandle) != 0){ LOGE("WsAdStop Failure!"); thiz->close(); return(0); }
    }
    else if(thiz->GetApiType() == KM_API){
    	// Packet :: ID(2Byte) + Size(2Byte) + CRC(2Byte) + Data(2048Byte : 4ch * 256sample * signed short)
    	// RecvData :: Data(2048Byte : 4ch * 256sample * signed short) only.
        const int channels = 4;
        const int bytes    = 2;
        const int nbits    = 16;
        const int blocks   = 256;
        const int iosize = channels * bytes * blocks;
        std::vector<unsigned char> rbuf(iosize);
        if(::WsKmADStart(thiz->SocketKmDeviceHandle) != 0){ LOGE("WsKmAdStart Failure!"); thiz->close(); return(0); }
        std::vector<floatx8> vdat(blocks);
        while(!thiz->isBeingStopped()){
            //if(iosize == _WsKmSampleDataRead(thiz->SocketKmDeviceHandle, reinterpret_cast <char *>(&rbuf[0]), iosize)){
            if(iosize == ::WsKmFrameRead(thiz->SocketKmDeviceHandle, reinterpret_cast <char *>(&rbuf[0]), iosize)){
                ct = 0;
                for(int i=0; i<blocks; i++){ vdat[i].convert(&rbuf[i * channels * bytes], channels, nbits); }
            retrykm:
                if(thiz->isBeingStopped()){ goto loopendkm; }
                int ret = thiz->buffer->writeWithTimeout(vdat, 250); // 250ms
                if(ret == 0){
                    // OK
                }else if(ret == ETIMEDOUT){
                    ct++;
                    //LOGI("retrying %d", ct);
                    //LOGD("buf in use (%d / %d)", thiz->buffer->getdatnum(), thiz->buffer->getlen());
                    goto retrykm;
                }else{
                    LOGE("socketdeviceloop write error %d", ret);
                    goto loopendkm;
                }
            }
            else{ LOGE("socketdeviceloop _WsKmSampleDataRead size error"); break; }
            //LOGD("record count %d", ct);
            //LOGD("buf in use (%d / %d)", thiz->buffer->getdatnum(), thiz->buffer->getlen());
            //::sleep(90);
        }
        loopendkm:
        if(::WsKmADStop(thiz->SocketKmDeviceHandle) != 0){ LOGE("WsKmAdStop Failure!"); thiz->close(); return(0); }
    }
    else{
        LOGE("Unknown API type or selected none."); thiz->allclose(); return(0);
    }
    return(0);
}
    
int SocketDeviceObject::start(BufferObject& buf)
{
    ResetBeingStopped();
    buffer = &buf;
    int rr = ::pthread_create(&thread_id, NULL, _socketdeviceloop, reinterpret_cast<void *>(this));
    if(rr != 0){ LOGE("pthread_create error"); return(-1); }
    return(0);
}

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

int SocketDeviceObject::close()
{
    switch(socket_device_info.api_type){
    case RASP_API:
        if(SocketRaspDeviceHandle != NULL){ ::WsHandleClose(SocketRaspDeviceHandle); SocketRaspDeviceHandle = NULL; }
        break;
    case KM_API:
        if(SocketKmDeviceHandle != NULL){ ::WsKmHandleClose(SocketKmDeviceHandle); SocketKmDeviceHandle = NULL; }
        break;
    default:
        break;
    }
    return(0);
}

int SocketDeviceObject::allclose()
{
    if(SocketRaspDeviceHandle != NULL){ ::WsHandleClose(SocketRaspDeviceHandle); SocketRaspDeviceHandle = NULL; }
    if(SocketKmDeviceHandle != NULL){ ::WsKmHandleClose(SocketKmDeviceHandle); SocketKmDeviceHandle = NULL; }
    return(0);
}
    
} // end of namespace
