#include "ttyaccess.h"
#include "tty_bluetooth.h"
#include <helper.h>
#include <config.h>

#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>

#if defined(OS2_API)
/* no bluetooth */

#elif defined(WINDOWS_API)
#include <winsock2.h>
#define sock_handle_t SOCKET
#define VER_MAJOR 2
#define VER_MINOR 2
/* You need the Platform SDK for the includes below.
 * This only works with Microsoft Windows XP or later
 * You must link to Ws2_32.lib.
 */
#if HAVE_WS2BTH_H
#include <ws2bth.h>
#if defined(BTHPROTO_RFCOMM)
#define WINDOWS_BLUETOOTH_API 1
#endif /* BTHPROTO_RFCOMM */
#endif /* ENABLE_WIN_BT */


#else
#include <sys/types.h>
#include <sys/socket.h>
/* support for the linux bluez stack */
#if HAVE_BLUETOOTH_BLUETOOTH_H
#include <bluetooth/bluetooth.h>
#if defined(BTPROTO_RFCOMM)
#include <bluetooth/rfcomm.h>
#define BLUEZ_API 1
#endif /* BTPROTO_RFCOMM */
#endif /* HAVE_BLUETOOTH_BLUETOOTH_H */

/* support for the freebsd netgraph stack */
#if HAVE_BLUETOOTH_H
#include <bluetooth.h>
/* bluetooth.h on FreeBSD already includes the following headers:
 * sys/bitstring.h
 * netgraph/bluetooth/include/ng_btsocket.h
 */
#if defined(BLUETOOTH_PROTO_RFCOMM)
#define NETGRAPH_API 1
#endif /* BLUETOOTH_PROTO_RFCOMM */
#endif /* HAVE_BLUETOOTH_H */

#endif /* API */


#if BLUEZ_API
#define ENABLE_BLUETOOTH 1
#define sock_handle_t int
#define rfcomm_sockaddr_t struct sockaddr_rc
#define SOCKET_ERROR -1
#define bluetooth_socket() socket(PF_BLUETOOTH,SOCK_STREAM,BTPROTO_RFCOMM)
#define print_socket_error() print_error("%s\n",strerror(errno))
#define bluetooth_set_sockaddr(addr,channel,sa) { \
  sa.rc_family = AF_BLUETOOTH; \
  str2ba(addr,&sa.rc_bdaddr); \
  sa.rc_channel = channel; \
}
#define closesocket(s) close(s);
#endif /* BLUEZ_API */


#if NETGRAPH_API
#define ENABLE_BLUETOOTH 1
#define sock_handle_t int
#define rfcomm_sockaddr_t struct sockaddr_rfcomm
#define SOCKET_ERROR -1
#define bluetooth_socket() socket(PF_BLUETOOTH,SOCK_STREAM,BLUETOOTH_PROTO_RFCOMM)
#define print_socket_error() print_error("%s\n",strerror(errno))
#define bluetooth_set_sockaddr(addr,channel,sa) { \
  sa.rfcomm_family = AF_BLUETOOTH; \
  bt_aton(addr,&sa.rfcomm_bdaddr); \
  sa.rfcomm_channel = channel; \
}
#define closesocket(s) close(s);
#endif


#if WINDOWS_BLUETOOTH_API
#define ENABLE_BLUETOOTH 1
#define rfcomm_sockaddr_t SOCKADDR_BTH
#define bluetooth_socket() socket(PF_BTH,SOCK_STREAM,BTHPROTO_RFCOMM)
#define bluetooth_set_sockaddr(addr,channel,sa) { \
  int len = sizeof(rfcomm_sockaddr_t); \
  sa.addressFamily = AF_BTH; \
  WSAStringToAddress(addr,sa.addressFamily,NULL,(SOCKADDR*)&sa,&len); \
  sa.port = channel; \
}
#define print_socket_error() print_error("%s %d\n",_("system error code"),WSAGetLastError())
#define tty_bluetooth_cleanup() WSACleanup()
int tty_bluetooth_init () {
  WORD ver = MAKEWORD(VER_MAJOR,VER_MINOR);
  WSADATA data;
  int err = WSAStartup(ver,&data);

  if (err ||
      LOBYTE(data.wVersion) != VER_MAJOR ||
      HIBYTE(data.wVersion) != VER_MINOR) {
    tty_bluetooth_cleanup();
    return 0;
  }
  return 1;
}
#endif /* WINDOWS_BLUETOOTH_API */


#ifndef sock_handle_t
#define sock_handle_t int
#endif
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
#ifndef tty_bluetooth_init
#define tty_bluetooth_init() 1
#endif
#ifndef tty_bluetooth_cleanup
#define tty_bluetooth_cleanup() {}
#endif


static struct tty_access funcs;
static sock_handle_t bt_sock;


struct tty_access* tty_bluetooth_open(struct port_args_t* args) {
  struct tty_access* retval = NULL;
#if ENABLE_BLUETOOTH
  rfcomm_sockaddr_t sa;
#endif

  bt_sock = INVALID_SOCKET;
  if (!tty_bluetooth_init()) return NULL;

#if ENABLE_BLUETOOTH
  bluetooth_set_sockaddr(args->device,args->channel,sa);
  if ((bt_sock = bluetooth_socket()) == INVALID_SOCKET ||
      connect(bt_sock,(struct sockaddr*)&sa,sizeof(sa))  == SOCKET_ERROR) {
    tty_bluetooth_close();
    print_socket_error();    
    return NULL;
  }
  retval = &funcs;
#else
  args = NULL; /* just to use it */
  print_error("%s\n",_("protocol not implemented"));
#endif

  funcs.read = tty_bluetooth_read;
  funcs.write = tty_bluetooth_write;
  funcs.flush = tty_bluetooth_flush;
  funcs.close = tty_bluetooth_close;
  return retval;
}

void tty_bluetooth_close(void) {
#if ENABLE_BLUETOOTH
  if (bt_sock != INVALID_SOCKET) closesocket(bt_sock);
#endif
  tty_bluetooth_cleanup();
}

/* as argument to tty_write() */
static long socketwrite (const char* data, long len) {
#if ENABLE_BLUETOOTH
  return send(bt_sock,data,len,0);
#else
  return -1;
#endif
}

int tty_bluetooth_write (const char* data, size_t count) {
  return tty_write(data,count,socketwrite);
}

/* as argument to tty_read() */
static long socketread (char* buf, long len) {
#if ENABLE_BLUETOOTH
  return recv(bt_sock,buf,len,0);
#else
  return -1;
#endif
}

char* tty_bluetooth_read (int (*stop_condition)(const char*,const size_t)) {
  return tty_read(stop_condition,socketread);
}

void tty_bluetooth_flush (void) {
  /* nothing to do here */
}
