////////////////////////////////////////////////////////////////////////
//
//  TobiiClient.c: こじ研 TobiiClient API ソースファイル
//      version 1.1 (August 25, 2010)
//      xkozima@myu.ac.jp (subject to GPLv2)
//
//  document: http://www.myu.ac.jp/~xkozima/lab/gaze-tobii2.html

#ifdef _WIN32
//  Windows
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <string.h>
#include "TobiiClient.h"
#define  UnivSocket  SOCKET
#define  UnivSockaddrIn  SOCKADDR_IN
#define  UnivHostentP  LPHOSTENT
#define  UnivInAddrP  LPIN_ADDR
#define  UnivInvalidSocket  INVALID_SOCKET
#define  UnivSocketError  SOCKET_ERROR
#define  univWrite(s,b,l)  send(s,b,l,0)
#define  univRead(s,b,l)  recv(s,b,l,0)
#define  univClose(s)  {closesocket(s);WSACleanup();}
#define  univSleep(msec) Sleep(msec)
//
#else
//  Mac/Linux
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "TobiiClient.h"
#define  UnivSocket  int
#define  UnivSockaddrIn  struct sockaddr_in
#define  UnivHostentP  struct hostent*
#define  UnivInAddrP  struct in_addr*
#define  UnivInvalidSocket  -1
#define  UnivSocketError  -1
#define  univWrite(s,b,l)  write(s,b,l)
#define  univRead(s,b,l)  read(s,b,l)
#define  univClose(s)  close(s)
#define  univSleep(msec) usleep((msec)*1000)
//
#endif

#define TOBII_PORT  16439

////////////////////////////////////
//
//  要求データ Request

#define  TOBII_NONE   0                 //  何もしない
#define  TOBII_GETGAZE  1               //  視線データをゲット

struct _TobiiRequest {
    int  action;                        //  0:TOBII_NONE, 1:TOBII_GETGAZE
    int  param;                         //  未使用（拡張機能のために予約）
};

typedef  struct _TobiiRequest  TobiiRequest;

////////////////////////////////////
//
//  API 関数群

static int  Connected = 0;
static UnivSocket  Socket;

//
//  TobiiServer に接続する（最初に１回これを呼ぶ）
//      int  res = TobiiConnect("10.0.1.6");
//      int  res = TobiiConnect("tobii.myu.ac.jp");
//      (戻り値：0=成功, -1=失敗)

int  TobiiConnect (char *server_name)
{
    UnivHostentP  entry;
    UnivSockaddrIn  address;
    int  res;

    //  もし既にサーバに接続していたら何もしない
    if (Connected) return 0;

    #ifdef _WIN32
    //  ウインドウズ特有のオマジナイ（ソケット関係の初期化）
    WORD  wVersionRequested = MAKEWORD(2, 0);
    WSADATA  wsaData;
    res = WSAStartup(wVersionRequested, &wsaData);
    if (res != 0) return -1;
    #endif

    //  ホスト名からアドレスを引く
    entry = gethostbyname(server_name);
    if (entry == NULL) {
        fprintf(stderr, "TobiiConnect: server not found\n");
        return -1;
    }
    if (entry->h_addrtype != AF_INET) {
        fprintf(stderr, "TobiiConnect: not an IP host\n");
        return -1;
    }

    //  ソケットに与えるアドレスの設定
    memset(&address, 0, sizeof(address));
    address.sin_family = AF_INET;
    #ifdef _WIN32
    address.sin_addr = * ((UnivInAddrP) *entry->h_addr_list);
    #else
    address.sin_addr = * ((UnivInAddrP) entry->h_addr);
    #endif
    address.sin_port = htons(TOBII_PORT);


    //  ソケットを生成
    Socket = socket(AF_INET, SOCK_STREAM, 0);
    if (Socket == UnivInvalidSocket) {
        fprintf(stderr, "TobiiConnect: cannot creat a socket\n");
        return -1;
    }

    //  サーバにソケットを接続
    res = connect(Socket, (struct sockaddr *) &address, sizeof(address));
    if (res == UnivSocketError) {
        fprintf(stderr, "TobiiConnect: cannot connect to the server\n");
        univClose(Socket);
        return -1;
    }

    //  接続完了
    Connected = 1;
    return 0;
}

//
//  パケットを送信・受信（内部使用）

int  tobii_send (char *pac, int len)
{
    int  res;

    //  未接続なら何もしない
    if (! Connected) return 0;

    //  サーバへデータを送信（最初）
    res = univWrite(Socket, pac, len);
    if (res == 0 || res == UnivSocketError) {
        //  サーバと切断されたらしい
        fprintf(stderr, "tobii_send: writing to the socket failed\n");
        univClose(Socket);
        Connected = 0;
        return -1;
    }

    //  全バイト送り出すまで繰り返し
    while (res < len) {
        int  res_rem;
    
        //  残りデータを送信
        res_rem = univWrite(Socket, pac+res, len-res);
        if (res_rem == -1) {
            //  サーバと切断されたらしい
            fprintf(stderr, "tobii_send: writing the remainder failed\n");
            univClose(Socket);
            Connected = 0;
            return -1;
        }
        res += res_rem;
    }

    //  送信完了
    return  res;
}

//
//  パケットを受信（内部使用）

int  tobii_receive (char *pac, int len)
{
    int  res;

    //  未接続なら何もしない
    if (! Connected) return 0;

    //  サーバからデータを受信（最初）
    res = univRead(Socket, pac, len);
    if (res == 0 || res == UnivSocketError) {
        //  サーバと切断されたらしい
        fprintf(stderr, "tobii_receive: reading from the socket failed\n");
        univClose(Socket);
        Connected = 0;
        return -1;
    }

    //  全バイト受け取るまで繰り返し
    while (res < len) {
        int  res_rem;

        //  reading the remainder
        res_rem = univRead(Socket, pac+res, len-res);
        if (res_rem == -1) {
            //  サーバと切断されたらしい
            fprintf(stderr, "tobii_receive: reading the remainder failed\n");
            univClose(Socket);
            Connected = 0;
            return -1;
        }
        res += res_rem;
    }

    //  受信完了
    return  res;
}

//
//  TobiiServer から視線データを取得する
//      TobiiGazeData  data;
//      TobiiGetData(&data);
//      float X = (data.x_gazepos_lefteye + data.x_gazepos_righteye) / 2.0;
//      float Y = (data.y_gazepos_lefteye + data.y_gazepos_righteye) / 2.0;
//      doSomething(X, Y);

void  TobiiGetData (TobiiGazeData *gaze_data)
{
    TobiiRequest  request;
    int  res;

    //  未接続なら何もしない
    if (! Connected) return;

    //  要求パケットを用意
    request.action = TOBII_GETGAZE;
    request.param  = 0;
    res = tobii_send((char *) &request, sizeof(TobiiRequest));
    if (res <= 0) {
        fprintf(stderr, "TobiiGetData: failed to send a request\n");
        return;
    }

    //  receive gaze data
    res = tobii_receive((char *) gaze_data, sizeof(TobiiGazeData));
    if (res <= 0) {
        fprintf(stderr, "TobiiGetData: failed to receive data\n");
        return;
    }
}

//
//  TobiiServer から切断する（最後に１回これを呼ぶ）

void  TobiiDisconnect ()
{
    //  未接続なら何もしない
    if (! Connected) return;

    //  サーバへの接続ソケットを閉じる
    univClose(Socket);
    Connected = 0;
}

//
//  TobiiServer との接続の有無を調べる
//      if (TobiiIsConnected())
//          printf("Yes, we can!\n");
//      else
//          printf("I'm disconnected from the world...\n");
//      (戻り値：0=接続なし, 1=接続あり)

int  TobiiIsConnected ()
{
    return Connected;
}

//
//  ミリ秒単位のスリープ（時間調整用）

void  TobiiSleep (int msec)
{
    univSleep(msec);
}

////////////////////////////////////////////////////////////////////////

