//
//  bt_rfcomm.c
//
//  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 <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>

#include "bt_const.h"
#include "bt_extern.h"
#include "bt_printf.h"
#include "bt_rfcomm.h"

#include "lockobject.h"

static int s_current_channel;
static int s_sock_rfcomm;

static int read_line(const int sock,
		     char *buf,
		     ssize_t bufsize)
{
  
  int len = 0;
  int ct = 0;
  
  fd_set rfds;
  
  FD_ZERO(&rfds);
  FD_SET(sock, &rfds);
  
  while(select(sock + 1, &rfds, NULL, NULL, NULL) > 0) {
    
    int rlen = 0;
    char c;
    
    if (ct > bufsize) {
      return(ct);
    }
    
    rlen = read(sock, &c, 1);
    if (rlen == 1) {
      if (c == '\r') {
	buf[ct] = 0;
	rlen = read(sock, &c, 1);
	if (rlen == 1) {
	  if (c == '\n') {
	    // OK
	    //debug_printf("cr/lf\n");
	    if (ct == 0) {
	      // empty?
	      FD_ZERO(&rfds);
	      FD_SET(sock, &rfds);
	      continue;
	    } else {
	      return(ct);
	    }
	  } else {
	    return(-1);
	  }
	} else {
	  return(-2);
	}
      } else {
	//debug_printf("%02x %c\n", (int)c, c);
	buf[ct++] = c;
      }
    } else {
      return(-3);
    }
    
    
    FD_ZERO(&rfds);
    FD_SET(sock, &rfds);
    
  }
  
  
  return(ct);
  
}

static EventFlag ReceiveResponse;
static int response;

static void *recv_rfcomm_read_loop(void *param)
{

  int sock_rfcomm = (long int)param;
  
  debug_printf("incoming rfcomm connection %d\n", sock_rfcomm);

  int rsize;
  char buf[1024];
  
  while((rsize = read_line(sock_rfcomm, buf, sizeof(buf))) > 0) {
    
    debug_printf("rfcomm read %d bytes '%s'\n", (int)rsize, buf);

    if (!strcmp("+BVRA: 0", buf)) {
      VoiceRecognition.Reset();
      debug_printf("VoiceRecognition OFF\n");
    }

    if (!strcmp("+BVRA: 1", buf)) {
      VoiceRecognition.Set();
      debug_printf("VoiceRecognition ON\n");
    }

    if (!strcmp("OK", buf)) {
      response = 0;
      ReceiveResponse.Set();
      //debug_printf("OK\n");
    }

    if (!strcmp("ERROR", buf)) {
      response = -1;
      ReceiveResponse.Set();
      //debug_printf("ERROR\n");
    }
    
  }	
  
  close(sock_rfcomm);

  debug_printf("rfcomm %d exit\n", sock_rfcomm);

  return(0);
}


static void *server_rfcomm_loop(void *param)
{
  
  //const int channel = 5;			// todo
  const int channel = (long int)param;			// todo
  
  static char buf[1024];
  int wsize;
  int rsize;
  
  int sock;						// socket descriptor for local listener
  int client;						// socket descriptor for remote client
  unsigned int len = sizeof(struct sockaddr_rc);
  
  struct sockaddr_rc remote;		// local rfcomm socket address
  struct sockaddr_rc local;		// remote rfcomm socket address
  char pszremote[32];
  
  // initialize a bluetooth socket
  sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
  
  static const bdaddr_t bdaddr_any = {0, 0, 0, 0, 0, 0};

  local.rc_family = AF_BLUETOOTH;
  //local.rc_bdaddr = *BDADDR_ANY;
  local.rc_bdaddr = bdaddr_any;
  local.rc_channel = channel;
  
  // bind the socket to a bluetooth device
  if (bind(sock, (struct sockaddr *)&local, sizeof(struct sockaddr_rc)) < 0)
    return((void *)-1);
  
  // set the listening queue length
  if (listen(sock, 1) < 0)
    return((void *)-1);
  
  fd_set rfds;
  
  FD_ZERO(&rfds);
  FD_SET(sock, &rfds);
  
  debug_printf("rfcomm wait for connections on channel: %d\n", channel);
  
  while(select(sock + 1, &rfds, NULL, NULL, NULL) > 0) {
    
    // accept incoming connections; this is a blocking call
    s_sock_rfcomm = client = accept(sock, (struct sockaddr *)&remote, &len);
    s_current_channel = channel;
    
    ba2str(&remote.rc_bdaddr, pszremote);
    
    debug_printf("rfcomm connection from: %s\n", pszremote);
    
    if (channel == hsp_channel) {

      // hsp
      snprintf(buf, sizeof(buf), "AT+VGS=8\r");
      wsize = write(client, buf, strlen(buf));
      rsize = read_line(client, buf, sizeof(buf));
      debug_printf("AT+VGS= read %d %s\n", (int)rsize, buf);

    } else {

      // hfp
      
      //snprintf(buf, sizeof(buf), "AT+BRSF=255\r");
      //snprintf(buf, sizeof(buf), "AT+BRSF=31\r");
      snprintf(buf, sizeof(buf), "AT+BRSF=30\r");
      //snprintf(buf, sizeof(buf), "AT+BRSF=158\r");
      wsize = write(client, buf, strlen(buf));
      rsize = read_line(client, buf, sizeof(buf));
      debug_printf("AT+BRSF= read %d bytes '%s'\n", (int)rsize, buf);
      rsize = read_line(client, buf, sizeof(buf));
      debug_printf("AT+BRSF= read %d bytes '%s'\n", (int)rsize, buf);
      
#if 0
      snprintf(buf, sizeof(buf), "AT+BAC=1\r");
      wsize = write(client, buf, strlen(buf));
      rsize = read_line(client, buf, sizeof(buf));
      debug_printf("AT+BAC= read %d bytes '%s'\n", (int)rsize, buf);
#endif
      snprintf(buf, sizeof(buf), "AT+CIND=?\r");
      wsize = write(client, buf, strlen(buf));
      rsize = read_line(client, buf, sizeof(buf));
      debug_printf("AT+CIND=? read %d bytes '%s'\n", (int)rsize, buf);
      rsize = read_line(client, buf, sizeof(buf));
      debug_printf("AT+CIND=? read %d bytes '%s'\n", (int)rsize, buf);
      
      snprintf(buf, sizeof(buf), "AT+CIND?\r");
      wsize = write(client, buf, strlen(buf));
      rsize = read_line(client, buf, sizeof(buf));
      debug_printf("AT+CIND? read %d bytes '%s'\n", (int)rsize, buf);
      rsize = read_line(client, buf, sizeof(buf));
      debug_printf("AT+CIND? read %d bytes '%s'\n", (int)rsize, buf);
      
      snprintf(buf, sizeof(buf), "AT+CMER=3,0,0,1\r");
      wsize = write(client, buf, strlen(buf));
      rsize = read_line(client, buf, sizeof(buf));
      debug_printf("AT+CMER= read %d bytes '%s'\n", (int)rsize, buf);
      
      snprintf(buf, sizeof(buf), "AT+CHLD=?\r");
      wsize = write(client, buf, strlen(buf));
      rsize = read_line(client, buf, sizeof(buf));
      debug_printf("AT+CHLD=? read %d bytes '%s'\n", (int)rsize, buf);
      rsize = read_line(client, buf, sizeof(buf));
      debug_printf("AT+CHLD=? read %d bytes '%s'\n", (int)rsize, buf);
      
#if 0
      
      // start voice recogintion
      snprintf(buf, sizeof(buf), "AT+BVRA=1\r");
      wsize = write(client, buf, strlen(buf));
      rsize = read_line(client, buf, sizeof(buf));
      debug_printf("AT+BVRA= read %d bytes '%s'\n", (int)rsize, buf);

#endif
      
    }
    
    {
      pthread_t thread;
      pthread_attr_t attr;
      
      pthread_attr_init(&attr);
      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
      
      if (pthread_create(&thread, &attr, recv_rfcomm_read_loop, (void *)(long int)client) != 0) {
	perror("recv rfocomm_read_loop create");
	close(client);
	pthread_attr_destroy(&attr);
	return((void *)-1);
      }
      
    }
    
    FD_ZERO(&rfds);
    FD_SET(sock, &rfds);
    
    debug_printf("rfcomm wait for connections on channel: %d\n", channel);
    
  }
  
  close(sock);
  
  debug_printf("rfcomm loop end\n");
  
  return(NULL);
}

static pthread_mutex_t vr_mutex;

int rfcomm_voice_recognition_activate(void)
{

  lockobject lock(&vr_mutex);

  char buf[64];

  int wsize;
  int retval = 0;

  debug_printf("VoiceRecognition Activate Request\n");

  ReceiveResponse.Reset();
  response = -1;

  // start voice recogintion
  snprintf(buf, sizeof(buf), "AT+BVRA=1\r");
  wsize = write(s_sock_rfcomm, buf, strlen(buf));

  //ReceiveResponse.Wait();
  ReceiveResponse.TimedWait(500);

  if (response == 0) {
    debug_printf("VoiceRecognition Activate OK\n");
    retval = 0;
  } else {
    debug_printf("VoiceRecognition Activate ERROR\n");
    retval = -1;
  }

  return(retval);
}

int make_rfcomm_server(const int ch)
{
  
  pthread_t thread;
  pthread_attr_t attr;
  
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  
  if (pthread_create(&thread, &attr, server_rfcomm_loop, (void *)(long)ch) != 0) {
    perror("server loop create");
    pthread_attr_destroy(&attr);
    return(-1);
  }
  
  pthread_attr_destroy(&attr);
  
  return(0);
}

int init_rfcomm(void)
{

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

}
