//
//  HA_RaspMXObject.cpp
//  HA_server
//
//  Created by hark on 2014/07/28.
//  Copyright (c) 2014年 hark. All rights reserved.
//


#include <unistd.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include "HA_log.h"
#include "HA_RaspMXObject.h"

#include "drv.h"

namespace HARKAndroid {

RaspMXObject::RaspMXObject() : thread_id(0),
			       BeingStopped(false),
			       fd(-1)
{
    int ret = ::pthread_mutex_init(&Being_Stopped_mutex, NULL);
    if (ret != 0) {
        LOGE("pthread_mutex_init error");
    }
    
    channel_offset = 0;
}

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

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

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

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

int RaspMXObject::open()
{

    close();
    
    fd = ::open("/dev/kt", O_RDWR, 0666);
    
    return(0);
}

static int _SampleDataRead(int fd, char *buffer, const size_t read_req_size, int channel_offset)
{
    
    int readcnt(0);
    size_t remain(read_req_size);
    char *bufp(buffer);

    int tmpbuf[128*16];		// 16ch

#if 0
    while(remain > 0) {

      const int tmp_read_size = read(fd, tmpbuf, sizeof(tmpbuf));
      if (tmp_read_size <= 0) {
	break;
      }

      const int read_size = 8 * sizeof(int); // 8ch分

      memcpy(bufp, tmpbuf, read_size);
      
      remain -= read_size;
      bufp += read_size;
      readcnt += read_size;

    }
#endif

    int req_size = sizeof(tmpbuf);
    int offset = 0;
    while(req_size > 0) {

      int tmp_read_size = read(fd, &tmpbuf[offset/sizeof(int)], req_size);
      if (tmp_read_size <= 0) {
        perror("Read Error.");
        printf("req, read, offset = %d, %d, %d\n", req_size, tmp_read_size, offset);
        return -1;
      }
      offset   += tmp_read_size;
      req_size -= tmp_read_size;

    }

    {
      int count;
      const int read_size = MX_DEVICE_CH_NUM * sizeof(int);

      count = 128;
      for(int i=0; i<count; i++)
      {
//          memcpy(bufp, &tmpbuf[i*16+channel_offset], read_size);
          memcpy(bufp, &tmpbuf[i*16], read_size);
      
          bufp += read_size;
      }

      /* test x16 */
      /*
      int *p;
      p = (int *)buffer;
      for(int i=0; i<128*MX_DEVICE_CH_NUM; i++)
      {
         p[i] = p[i] * 16;
      }
      */
    }
    
    return(read_req_size);
    
}

static void *_mxloop(void *arg)
{

    const int channels = MX_DEVICE_CH_NUM;
    const int blocks   = 128;
    
    RaspMXObject *thiz = reinterpret_cast<RaspMXObject *>(arg);
    int ct = 0;
    std::vector<int> rbuf(channels * blocks);

    const int iosize = channels * sizeof(int) * blocks;
    std::vector<floatx8> vdat(blocks);

    int stat = ::ioctl(thiz->fd, FIO_AD_START, NULL);
    if(stat < 0) {
	printf("ioctl error! (%d)\n", stat);
	return((void *)(-1));
    }
    
    printf("MX : channel offset = %d\n", thiz->channel_offset);

    while (!thiz->isBeingStopped()) {

        if (iosize == _SampleDataRead(thiz->fd, reinterpret_cast <char *>(&rbuf[0]), iosize, thiz->channel_offset)) {

            ct = 0;
            
            for (int i = 0; i < blocks; i++) {
                vdat[i].convert(&rbuf[i * channels], channels);
            }

        retry:

            if (thiz->isBeingStopped()) {
                goto loopend;
            }
            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 retry;
            } else {
                LOGE("mxloop write error %d", ret);
                goto loopend;
            }
            
        } else {
            LOGE("mxloop read size error");
            break;
        }
        //LOGD("record count %d", ct);
        //LOGD("buf in use (%d / %d)", thiz->buffer->getdatnum(), thiz->buffer->getlen());
        //::sleep(90);
    }
    
loopend:

 
    return(0);
    
}
    
int RaspMXObject::start(BufferObject& buf)
{

    int retval = ::ioctl(fd, FIO_AD_STOP, NULL);
    if(retval < 0) {
	printf("ioctl error! (%d)\n", retval);
	return(retval);
    }

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

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

    retval = ::ioctl(fd, FIO_AD_STOP, NULL);
    if(retval < 0) {
	printf("ioctl error! (%d)\n", retval);
	return(retval);
    }

    return(retval);
}

int RaspMXObject::close()
{

  if (fd != -1) {
    ::close(fd);
    fd = -1;
  }
  return(0);

}
    
} // end of namespace
