//
//  inet_server.cpp
//
//  Created by hark on 2014/06/13.
//  Copyright (c) 2014 hark. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <semaphore.h>

#include "bt_extern.h"
#include "bt_printf.h"
#include "bt_rfcomm.h"
#include "bt_sco.h"
#include "buffer.h"
#include "lockobject.h"
#include "eventflag.h"
#include "inet_server.h"

static pthread_mutex_t inet_send_mutex;
static sem_t max_thread_sem;

//
// データ受信してためる版
//
static void *sloop(void *arg)
{

  class dummy {
  public:
    ~dummy() {
      if (sem_post(&max_thread_sem) != 0) {
	perror("sem_post");
	exit(-1);
      }
    }
  };

  dummy d;

  const long int l_fd = reinterpret_cast<long int>(arg);
  const int fd = static_cast<int>(l_fd);

  rawdata rdata;

  fd_set rfds;
  
  int ct = 0;
  int remain = sizeof(rdata.data);
  char *ptr = &rdata.data[ct];

  while(1){
    
    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);

    int ret = ::select(fd + 1, &rfds, NULL, NULL, NULL);
    if(ret < 0){
      if(errno == EINTR){
	continue;
      }
      else{
	perror("select Error");
	break;
      }
    }
    else if(ret > 0){

      if(remain > 0){
	ssize_t read_count = ::recv(fd, ptr, remain, 0);
	if(read_count <= 0){
	  break;
	}
	remain -= read_count;
	ct += read_count;
	ptr += read_count;
      }
      else{
	char dummy_buffer[1024];
	ssize_t read_count = ::recv(fd, dummy_buffer, sizeof(dummy_buffer), 0);
	if(read_count <= 0){
	  break;
	}
      }

    }
    else{
      // close
      break;
    }

  }

  close(fd);

  if (ct > 0) {

    lockobject lock(&inet_send_mutex);

    if (bt_wait_voice_recognition) {
      rfcomm_voice_recognition_activate();
    }

    rdata.num_bytes = ct;
    debug_printf("write to buf %d\n", rdata.num_bytes);
    buf_write(&rdata);

    tell_data_arrived();

  }

  //debug_printf("---- inet thread exit\n");

  return(NULL);
}

static void *loop(void *arg)
{
  
  const long int l_port = reinterpret_cast<long int>(arg);
  const int port = (int)l_port;
  
  debug_printf("inet server port %d\n", port);

  int fd = ::socket(AF_INET, SOCK_STREAM, 0);
  if(fd <= 0){
    perror("socket");
    return(NULL);
  }
  
  const int reuseaddr = 1;
  ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));
  
  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr   = INADDR_ANY;
  addr.sin_port   = htons(port);
  
  if(::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0){
    ::close(fd);
    perror("bind Error");
    return(NULL);
  }
  
  if(::listen(fd, 5) != 0){
    ::close(fd);
    perror("listen Error");
    return(NULL);
  }
  
  fd_set rfds;
  
  while(1){
    
    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);
    
    int ret = ::select(fd + 1, &rfds, NULL, NULL, NULL);
    if(ret < 0){
      if(errno == EINTR){
	continue;
      }
      else{
	perror("select Error");
	break;
      }
    }
    else if(ret > 0){
      
      struct sockaddr_in caddr;
      int client_len;

      sem_wait(&max_thread_sem);
      
      int csock_fd = ::accept(fd, (struct sockaddr *)&caddr, (socklen_t*)&client_len);
      if(csock_fd != -1){
	
	::pthread_attr_t thread_attr;
	::pthread_attr_init(&thread_attr);
	::pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
	::pthread_attr_setstacksize(&thread_attr, sizeof(rawdata) * 2);
	//int ret = ::pthread_attr_setstacksize(&thread_attr, sizeof(rawdata) * 2);
	//printf("setstacksize = %d, %d\n", ret, sizeof(rawdata)*2);
	
	pthread_t thread0;
	int rr = ::pthread_create(&thread0, &thread_attr, ::sloop, reinterpret_cast<void *>(csock_fd));
	
	::pthread_attr_destroy(&thread_attr);
	
	if(rr != 0){
	  perror("pthread_create inet loop\n");
	  break;
	}
	
      }
      else{
	perror("accept\n");
	break;
      }
    }
    else{
      // timeout
      break;
    }
    //FD_ZERO(&rfds);
    //FD_SET(fd, &rfds);
  }
  
  close(fd);
  
  return(NULL);
}

//
//  TODO:データ受信してためない版
//
static void *async_sloop(void *arg)
{

  class dummy {
  public:
    ~dummy() {
      if (sem_post(&max_thread_sem) != 0) {
	perror("sem_post");
	exit(-1);
      }
    }
  };

  dummy d;

  const long int l_fd = reinterpret_cast<long int>(arg);
  const int fd = static_cast<int>(l_fd);

  rawdata_v rdata;
  rdata.data.resize(::bt_send_size_byte);

  fd_set rfds;
  
  int ct = 0;
  int total = 0;
  int remain = ::bt_send_size_byte;
  char *ptr = &rdata.data[0];
  int seq_no = 0;

  typedef short int int16;

  dbuf_lock();

  //
  // 一回目は指定分ためる
  //
  while(1) {
    
    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);

    int ret = ::select(fd + 1, &rfds, NULL, NULL, NULL);
    if(ret < 0){
      if(errno == EINTR){
	continue;
      }
      else{
	perror("select Error");
	goto FunctionEnd;
      }
    }
    else if(ret > 0){

      if(remain > 0){
	ssize_t read_count = ::recv(fd, ptr, remain, 0);
	if(read_count <= 0){

	  // close
	  if (rdata.num_bytes > 0) {
	    rdata.seq_no = seq_no++;
	    rdata.isLast = false;	
	    dbuf_write(&rdata);
	    
	    total += rdata.num_bytes;
	  }

	  // delimiter
	  rdata.seq_no = seq_no++;
	  rdata.isLast = true;
	  *(int16 *)&rdata.data[0] = 0;
	  rdata.num_bytes = sizeof(int16);
	  dbuf_write(&rdata);

	  goto FunctionEnd;

	}
	remain -= read_count;
	ct += read_count;
	ptr += read_count;

	//debug_printf("* receivesize %d\n", read_count);

	if (remain == 0) {
	
	  rdata.seq_no = seq_no++;
	  rdata.num_bytes = ct;
	  rdata.isLast = false;
	  dbuf_write(&rdata);
	
	  total += ct;
	  
	  break;

	}

      }

    }
    else{

      // close
      if (rdata.num_bytes > 0) {
	rdata.seq_no = seq_no++;
	rdata.isLast = false;	
	dbuf_write(&rdata);

	total += rdata.num_bytes;
      }

      // delimiter
      rdata.seq_no = seq_no++;
      rdata.isLast = true;
      *(int16 *)&rdata.data[0] = 0;
      rdata.num_bytes = sizeof(int16);
      dbuf_write(&rdata);

      goto FunctionEnd;
    }

  }

  //
  // あとは逐次
  //
  ptr = &rdata.data[0];
  remain = ::bt_send_size_byte;
  rdata.num_bytes = 0;
  while(1) {
    
    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);

    int ret = ::select(fd + 1, &rfds, NULL, NULL, NULL);
    if(ret < 0){
      if(errno == EINTR){
	continue;
      }
      else{
	perror("select Error");
	break;
      }
    }
    else if(ret > 0){

      ssize_t read_count = ::recv(fd, ptr, remain, 0);
      if(read_count <= 0){

	// close
	if (rdata.num_bytes > 0) {
	  rdata.seq_no = seq_no++;
	  rdata.isLast = false;	
	  dbuf_write(&rdata);
	  
	  total += rdata.num_bytes;
	}
	
	// delimiter
	rdata.seq_no = seq_no++;
	rdata.isLast = true;
	*(int16 *)&rdata.data[0] = 0;
	rdata.num_bytes = sizeof(int16);
	dbuf_write(&rdata);

	break;
      }

      //debug_printf("receivesize %d\n", read_count);

      rdata.seq_no = seq_no++;
      rdata.isLast = false;

      //
      // 奇数バイトだと具合が悪い
      //
      const int trans_count = rdata.num_bytes + read_count;
      if ((trans_count % 2) == 0) {

	rdata.num_bytes = trans_count;
	dbuf_write(&rdata);

	total += rdata.num_bytes;

	ptr = &rdata.data[0];
	remain = ::bt_send_size_byte;

	rdata.num_bytes = 0;

      } else {

	const char save = rdata.data[trans_count - 1];
	rdata.num_bytes = trans_count - 1;
	dbuf_write(&rdata);

	total += rdata.num_bytes;

	ptr = &rdata.data[1];
	remain = ::bt_send_size_byte - 1;

	rdata.data[0] = save;
	rdata.num_bytes = 1;

      }
      
    }
    else{

      // close
      if (rdata.num_bytes > 0) {
	rdata.seq_no = seq_no++;
	rdata.isLast = false;	
	dbuf_write(&rdata);
	total += rdata.num_bytes;
      }

      // delimiter
      rdata.seq_no = seq_no++;
      rdata.isLast = true;
      *(int16 *)&rdata.data[0] = 0;
      rdata.num_bytes = sizeof(int16);
      dbuf_write(&rdata);

      break;
    }

  }

FunctionEnd:

  dbuf_unlock();

  close(fd);

  //debug_printf("---- inet thread exit\n");

  return(NULL);
}

static void *async_loop(void *arg)
{
  
  const long int l_port = reinterpret_cast<long int>(arg);
  const int port = (int)l_port;
  
  debug_printf("inet server port %d\n", port);

  int fd = ::socket(AF_INET, SOCK_STREAM, 0);
  if(fd <= 0){
    perror("socket");
    return(NULL);
  }
  
  const int reuseaddr = 1;
  ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));
  
  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr   = INADDR_ANY;
  addr.sin_port   = htons(port);
  
  if(::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0){
    ::close(fd);
    perror("bind Error");
    return(NULL);
  }
  
  if(::listen(fd, 5) != 0){
    ::close(fd);
    perror("listen Error");
    return(NULL);
  }
  
  fd_set rfds;
  
  while(1){
    
    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);
    
    int ret = ::select(fd + 1, &rfds, NULL, NULL, NULL);
    if(ret < 0){
      if(errno == EINTR){
	continue;
      }
      else{
	perror("select Error");
	break;
      }
    }
    else if(ret > 0){
      
      struct sockaddr_in caddr;
      int client_len;

      sem_wait(&max_thread_sem);
      
      int csock_fd = ::accept(fd, (struct sockaddr *)&caddr, (socklen_t*)&client_len);
      if(csock_fd != -1){
	
	::pthread_attr_t thread_attr;
	::pthread_attr_init(&thread_attr);
	::pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
	::pthread_attr_setstacksize(&thread_attr, sizeof(rawdata) * 2);
	//int ret = ::pthread_attr_setstacksize(&thread_attr, sizeof(rawdata) * 2);
	//printf("setstacksize = %d, %d\n", ret, sizeof(rawdata)*2);
	
	pthread_t thread0;
	int rr = ::pthread_create(&thread0, &thread_attr, ::async_sloop, reinterpret_cast<void *>(csock_fd));
	
	::pthread_attr_destroy(&thread_attr);
	
	if(rr != 0){
	  perror("pthread_create inet loop\n");
	  break;
	}
	
      }
      else{
	perror("accept\n");
	break;
      }
    }
    else{
      // timeout
      break;
    }
    //FD_ZERO(&rfds);
    //FD_SET(fd, &rfds);
  }
  
  close(fd);
  
  return(NULL);
}

//
//
//
int inet_init()
{
  
  int ret = ::pthread_mutex_init(&inet_send_mutex, NULL);
  if (ret != 0) {
    return(ret);
  }

  ret = ::sem_init(&max_thread_sem, 0, 128);
  if (ret != 0) {
    return(ret);
  }
  
  ::pthread_t thread0;
  ::pthread_attr_t thread_attr;
  ::pthread_attr_init(&thread_attr);
  ::pthread_attr_setdetachstate(&thread_attr , PTHREAD_CREATE_DETACHED);
  
  int rr = ::pthread_create(&thread0, &thread_attr, ::loop, reinterpret_cast<void *>(server_port));
  if(rr != 0){
    perror("inet_init\n");
    return(-1);
  }

  ::pthread_t thread1;
  ::pthread_attr_t thread_attr1;
  ::pthread_attr_init(&thread_attr1);
  ::pthread_attr_setdetachstate(&thread_attr1, PTHREAD_CREATE_DETACHED);
  
  rr = ::pthread_create(&thread1, &thread_attr1, ::async_loop, reinterpret_cast<void *>(server_port_async));
  if(rr != 0){
    perror("inet_init\n");
    return(-1);
  }


  return(0);
  
}
