#include <client1.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
//#include <pthread.h>
//#include <time.h>
//#include <signal.h>
//#include <unistd.h>
//#include <stdlib.h>
//#include <cstdlib>
#include <direct.h>
#include <dos.h>
#ifdef WIN32
#include <io.h>
#include <conio.h>
#include <windows.h>
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define WINSOCK
/* use winsock functions */
#define access _access
#define close _close
#define open _open
#define read _read
#define write _write
#define vsnprintf _vsnprintf
#define snprintf _snprintf
#endif
#include <string.h>
//#include <iostream>

/* file test operators */
#ifndef R_OK
#define R_OK 4
#endif
#ifndef W_OK
#define W_OK 2
#endif
#ifndef X_OK
# if defined(_WIN32) && !defined(__CYGWIN32__)
# define X_OK 0
# else
# define X_OK 1
# endif
#endif
#ifndef F_OK
#define F_OK 0
#endif

#define strmatch !strcmp
#define SUCCESS 0
#define ERROR   1
#define MAX_MSG 256
#define DEFAULT_PORT 10500
#define DEFAULT_SERVER "localhost"


using namespace std;

static char zodis[MAXLINELEN];  ///< Local work area for string operation
static char sbuff[MAXLINELEN]; ///< Local workarea for message string handling
static char rbuff[MAXLINELEN]; ///< Local workarea for message string handling
static char buff[MAXLINELEN];  ///< Local work area for string operation


int port;
int sd;
WSADATA wdata;
int client;  /* client socket */
int rc;
struct sockaddr_in local_addr, serv_addr;
struct hostent * host;
int readnf (int, char *);
int readline(int, char *, int);
int server;

//Paprogrames
void cleanup();
void usage();
void command_loop(int sd,int vienas_kartas);
char *do_output(int sd);
void do_command(int sd);
void komanda_die(int sd);
void komanda_get_version(int sd);
void komanda_get_status(int sd);
void komanda_pause_recog(int sd);
void komanda_terminate_recog(int sd);
void komanda_resume_recog(int sd);
void komanda_set_input_handler_on_change(int sd, char *arg);
static int check_grammar_path(char *prefix);
static int send_grammar(int sd, char *prefix);
static int send_idlist(int sd, char *idstr);
void komanda_change_grammar(int sd, char *prefixpath);
void komanda_activate_grammar(int sd, char *idlist);
void komanda_deactivate_grammar(int sd, char *idlist);
void komanda_sync_grammar(int sd);
void komanda_delete_grammar(int sd, char *idlist);
void komanda_add_grammar(int sd, char *prefixpath);
void do_send(int sd, char *buff);
char *do_receive(int sd, char *buff, int maxlen);
void do_sendf(int sd, char *fmt, ...);
int do_connect(char *hostname, int portnum);
void do_disconnect(int sd);
void do_julius();
void *do_deshifre(char *buff, int maxlen);


//***  readnf() - reading from a file descriptor but a bit smarter

int readnf (int fd, char *line)
{
    if (readline(fd, line, MAX_MSG) < 0)     return ERROR;

    return SUCCESS;
}


int readline(int fd, char *str, int maxlen)
{
  int n;           /* no. of chars */
  int readcount;   /* no. characters read */
  char c;

  for (n = 1; n < maxlen; n++) {
    /* read 1 character at a time */
    readcount = read(fd, &c, 1); /* store result in readcount */
    if (readcount == 1) /* 1 char read? */
    {
      *str = c;      /* copy character to bufffer */
      str++;         /* increment bufffer index */
      if (c == '\n') /* is it a newline character? */
         break;      /* then exit for loop */
    }
    else if (readcount == 0) /* no character read? */
    {
      if (n == 1)   /* no character read? */
        return (0); /* then return 0 */
      else
        break;      /* else simply exit loop */
    }
    else
      return (-1); /* error in read() */
  }
  *str=0;       /* null-terminate the bufffer */
  return (n);   /* return number of characters read */
} /* readline() */


// cleanup() is called to kill the thread upon SIGINT.

void cleanup()
{
    close(server);
//    pthread_exit(NULL);
    return;
} /* cleanup() */


void do_command(int sd)
{
  char *p, *com, *arg1, *arg2;

    com = strtok(sbuff, " \t\n");
    if (com == NULL) return;    /* avoid segv when no command given */
    arg1 = arg2 = NULL;
    if ((p = strtok(NULL, " \t\n")) != NULL) {
      arg1 = p;
      if ((p = strtok(NULL, " \t\n")) != NULL) {
	arg2 = p;
      }
    }
    if (strmatch(com, "die")) {
      komanda_die(sd);
    } else if (strmatch(com, "version")) {
      komanda_get_version(sd);
    } else if (strmatch(com, "status")) {
      komanda_get_status(sd);
    } else if (strmatch(com, "pause")) {
      komanda_pause_recog(sd);
    } else if (strmatch(com, "terminate")) {
      komanda_terminate_recog(sd);
    } else if (strmatch(com, "resume")) {
      komanda_resume_recog(sd);
    } else if (strmatch(com, "inputparam")) {
      komanda_set_input_handler_on_change(sd, arg1);
    } else if (strmatch(com, "changegram")) {
      komanda_change_grammar(sd, arg1);
    } else if (strmatch(com, "addgram")) {
      komanda_add_grammar(sd, arg1);
    } else if (strmatch(com, "deletegram")) {
      komanda_delete_grammar(sd, arg1);
    } else if (strmatch(com, "activategram")) {
      komanda_activate_grammar(sd, arg1);
    } else if (strmatch(com, "deactivategram")) {
      komanda_deactivate_grammar(sd, arg1);
    } else if (strmatch(com, "syncgram")) {
      komanda_sync_grammar(sd);
    } else if (strmatch(com, "julius")) {
      do_julius();
    }
     else {
      fprintf(stderr,"No such command: [%s]\n", com);
    }
}

/*  Receive data: dump a message from server to standard out.
  @param sd [in] socket to send data */
char *do_output(int sd)
{
  while(do_receive(sd, rbuff, MAXLINELEN) != NULL) {
    if (rbuff[0] == '.' && rbuff[1] == '\0') break;
    //printf("> %s\n", rbuff); // visa likusi zodziu atpazinimo informacija
    do_deshifre(rbuff,MAXLINELEN);

   // printf("JULIUS:>> %s\n", zodis);
  }
return zodis;
}

//#############################################################################################
void command_loop(int sd,int vienas_kartas )
{

#if defined(_WIN32) && !defined(__CYGWIN32__)
  /* win32 version: read console input using conio.h */
  fd_set readfds;
  struct timeval tv;
  int status;
  int i, nfd;
  int ch;
  int slen;
  int fix=0;

  slen = 0;
//if ( !vienas_kartas)
    printf("'JULIUS' serverio, klijento Nr. =  %i \r \n",sd);


  for(;;)
  {
    /* watch socket by select() and check keyboard input by _kbhit() if timeout */
    FD_ZERO(&readfds);
    FD_SET(sd, &readfds);
    tv.tv_sec = 0;
    tv.tv_usec = 50000;
 //  if (fix) goto zz;
   {nfd = select(sd+1, &readfds, NULL, NULL, &tv);}// if (fix==0)

    if ((nfd < 0)){ // winsock error // && (fix=0 )
      switch(WSAGetLastError()) {
      case WSANOTINITIALISED: printf(" A successful WSAStartup must occur before using this function. \n"); break;
      case WSAEFAULT: printf(" The Windows Sockets implementation was unable to allocate needed resources for its internal operations, or the readfds, writefds, exceptfds, or timeval parameters are not part of the user address space.  \n"); break;
      case WSAENETDOWN: printf(" The network subsystem has failed.  \n"); break;
      case WSAEINVAL: printf(" The timeout value is not valid, or all three descriptor parameters were NULL.  \n"); break;
      case WSAEINTR: printf(" A blocking Windows Socket 1.1 call was canceled through WSACancelBlockingCall.  \n"); break;
      case WSAEINPROGRESS: printf(" A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.  \n"); break;
      case WSAENOTSOCK: printf(" One of the descriptor sets contains an entry that is not a socket.  \n"); break;
      }
      perror("Error: select");
     // fix=1;
        printf(" \n");printf(" \n");printf("REIKIA PALEISTI 'JULIUS' SERVERI !!! \n");

      //exit(1);
      Sleep(3000);
      break;
    }
    //else {fix=0;}

  if ((FD_ISSET(sd, &readfds))) {  do_output(sd);}     /*(!fix)&& from server */
  else {  /* timeout, check for keyboard input */
 //zz: if (fix) {Sleep(2000);}
    if (_kbhit()) {
	//ch = _getche();
    //if (fix) Sleep(3000);
	ch = getch(); if (ch==27) {} //uzkomentavau 2012121207 break; jei "esc" iseiti
	if (ch == '\r') ch = '\n';
	sbuff[slen] = (char)ch;
	slen++;
	if (ch == '\n') {
	  sbuff[slen] = '\0';
	 // printf("do_command" );
     // printf("\r\n");
      //fix=0;

	  do_command(sd);	/* execute command */
	  slen = 0;
	}
   }
  }
   if ( vienas_kartas) break;
  printf("JULIUS:>> %s\n", zodis);
  // fix=0;
 }

#else

  /* unix version: watch both stdin and socket */
  fd_set readfds;
  int nfd;
  for(;;) {
    /* watch socket (fd = sd) and stdin (fd = 0) */
    FD_ZERO(&readfds);
    FD_SET(sd, &readfds);
    FD_SET(0, &readfds);

    nfd = select(sd+1, &readfds, NULL, NULL, NULL);

    if (nfd < 0) { perror("Error: select"); exit(1);}

    if (FD_ISSET(0, &readfds)) {
      if (fgets(sbuff, MAXLINELEN, stdin) != NULL) {do_command(sd);} /* stdin */
	}
    if (FD_ISSET(sd, &readfds)) {  do_output(sd);} /* from server */
  }
#endif /* WIN32 */

}

/*Output usage to stdout.*/
void usage()
{printf("usage: jcontrol host [portnum (def=%d)]\n", DEFAULT_PORT);}

/* Command "DIE": kill the recognition server.
  @param sd [in] socket to send data */
void komanda_die(int sd)
{  do_send(sd,(char*) "DIE\n");}

/* Command "VERSION": let the server send version information.
 * @param sd [in] socket to send data*/
void komanda_get_version(int sd)
{  do_send(sd,(char*) "VERSION\n");}



/* Command "STATUS": ask server about its current status (run/stop)
@param sd [in] socket to send data*/
void komanda_get_status(int sd)
{  do_send(sd,(char*) "STATUS\n");}

/* Command "PAUSE": tell server to pause recognition.  If audio input is
 processing at that time, recognition will stop after the current input
 has ended.
 @param sd [in] socket to send data*/
void komanda_pause_recog(int sd)
{  do_send(sd,(char*) "PAUSE\n");}

/* Command "TERMINATE": tell server to pause recognition immediately,
 even if audio input is processing at that time.
 @param sd [in] socket to send data */
void komanda_terminate_recog(int sd)
{  do_send(sd, (char*) "TERMINATE\n");}

/*Command "RESUME": tell server to restart recognition.
 * @param sd [in] socket to send data
 */
void komanda_resume_recog(int sd)
{
  do_send(sd,(char*) "RESUME\n");
}

/**
 * Command "INPUTONCHANGE": specify grammar changing timing policy when
 * input is being recognized.
 *
 * @param sd [in] socket to send data
 * @param arg [in] "TERMINATE" for immediate rejection of current input,
 * "PAUSE" for immediate input segmentation followed by recognition, or
 * "WAIT" for waiting the input to be segmented.
 */
void komanda_set_input_handler_on_change(int sd, char *arg)
{
  /* argument should be checked here... */
  /* one of TERMINATE, PAUSE, WAIT */

  /* send */
do_sendf(sd,(char*) "INPUTONCHANGE\n%s\n", arg);

}


/**
 * Check if the grammar files exist on the given prefix.
 *
 * @param prefix [in] prefix string of grammar file path
 *
 * @return 0 if exist, or -1 if such files not exist.
 */
static int check_grammar_path(char *prefix)
{
  snprintf(buff, MAXLINELEN, "%s.dfa", prefix);
  if (access(buff, R_OK) < 0) {
    perror("komanda_change_grammar"); return -1;
  }
  snprintf(buff, MAXLINELEN, "%s.dict", prefix);
  if (access(buff, R_OK) < 0) {
    perror("komanda_change_grammar"); return -1;
  }
  return 0;
}

/**
 * Send a grammar (.dfa and .dict file) to server.
 *
 * @param sd [in] socket to send data
 * @param prefix [in] grammar prefix path
 *
 * @return 0 on success, or -1 on failure.
 */
static int send_grammar(int sd, char *prefix)
{
  FILE *fp;
  int i;

  i = strlen(prefix) - 1;
  while(prefix[i] != '.' && i >= 0) i--;
  if (i < 0 && strcmp(&(prefix[i]), ".dict") != 0) {
    snprintf(buff, MAXLINELEN, "%s.dfa", prefix);
    if ((fp = fopen(buff, "r")) == NULL) {
      perror("komanda_change_grammar"); return -1;
    }
    while(fgets(buff, MAXLINELEN, fp) != NULL) {
      do_send(sd, buff);
    }
    do_send(sd,(char*) "DFAEND\n");
    fclose(fp);
    snprintf(buff, MAXLINELEN, "%s.dict", prefix);
  } else {
    snprintf(buff, MAXLINELEN, "%s", prefix);
  }
  if ((fp = fopen(buff, "r")) == NULL) {
    perror("komanda_change_grammar"); return -1;
  }
  while(fgets(buff, MAXLINELEN, fp) != NULL) {
    do_send(sd, buff);
  }
  do_send(sd,(char*)"DICEND\n");
  fclose(fp);
  return 0;
}

/**
 * Send comma-separated grammar ID list (ex. "1,3,5").
 *
 * @param sd [in] socket to send data
 * @param idstr [in] string of comma separated grammar ID list
 *
 * @return always 0.
 */
static int send_idlist(int sd, char *idstr)
{
  char *p;

  /* convert comma to space */
  strcpy(buff, idstr);
  p = buff;
  while(*p != '\0') {
    if (*p == ',') *p = ' ';
    p++;
  }
  /* send them */
  do_sendf(sd,(char*) "%s\n", buff);
  return 0;
}



/**
 * Command "CHANGEGRAM": send specified grammar to server and swap grammar.
 *
 * @param sd [in] socket to send data
 * @param prefixpath [in] prefix string of grammar file path
 */
void komanda_change_grammar(int sd, char *prefixpath)
{
  /* file existing check */
  if (check_grammar_path(prefixpath) < 0) return;
  /* send data */
  /* do_send(sd, "CHANGEGRAM\n"); */
  /* send data with its name */
  snprintf(buff, MAXLINELEN, "CHANGEGRAM %s\n", prefixpath);
  do_send(sd, buff);
  send_grammar(sd, prefixpath);
}

/**
 * Command "ADDGRAM": send specified grammar to server and add it to the current
 * grammar list.
 *
 * @param sd [in] socket to send data
 * @param prefixpath [in] prefix string of grammar file path
 */
void komanda_add_grammar(int sd, char *prefixpath)
{
  /* file existing check */
  if (check_grammar_path(prefixpath) < 0) return;
  /* send data */
  /* do_send(sd, "ADDGRAM\n"); */
  /* send data with its name */
  snprintf(buff, MAXLINELEN, "ADDGRAM %s\n", prefixpath);
  do_send(sd, buff);
  send_grammar(sd, prefixpath);
}

/**
 * Command "DELGRAM": delete grammars on the server specified by the ID.
 *
 * @param sd [in] socket to send data
 * @param idlist [in] string of comma separated grammar ID list
 */
void komanda_delete_grammar(int sd, char *idlist)
{
  do_send(sd,(char*) "DELGRAM\n");
  send_idlist(sd, idlist);
}

/**
 * Command "ACTIVATEGRAM": activate grammars on the server specified by the ID.
 *
 * @param sd [in] socket to send data
 * @param idlist [in] string of comma separated grammar ID list
 */
void komanda_activate_grammar(int sd, char *idlist)
{
  do_send(sd,(char*) "ACTIVATEGRAM\n");
  send_idlist(sd, idlist);
}

/**
 * Command "DEACTIVATEGRAM": temporary de-activate grammars on the server
 * specified by the ID.  The deactivated ones can be activated again
 * by ACTIVATEGRAM command.
 *
 * @param sd [in] socket to send data
 * @param idlist [in] string of comma separated grammar ID list
 */
void komanda_deactivate_grammar(int sd, char *idlist)
{
  do_send(sd,(char*) "DEACTIVATEGRAM\n");
  send_idlist(sd, idlist);
}

/**
 * Command "SYNCGRAM": tell Julian to update the grammar to ready for
 * recognition.  When updaing grammars while paused, calling this just
 * before "resume" may improve the delay caused by update process of
 * grammar at the beginning of recognition restart.
 *
 * @param sd [in] socket to send data
 */
void komanda_sync_grammar(int sd)
{
  do_send(sd,(char*) "SYNCGRAM\n");
}

void do_sendf(int sd, char *fmt, ...)
{
  static char buff[MAXLINELEN];
  va_list ap;
  int n;

  va_start(ap, fmt);
  vsnprintf(buff, MAXLINELEN, fmt, ap);
#ifdef WINSOCK
  n = send(sd, buff, strlen(buff), 0);
#else
  n = write(sd, buff, strlen(buff));
#endif
  if (n < 0) {
    perror("Error: do_sendf");
  }
  va_end(ap);
}

/**
 * Send content of the bufffer to server.
 *
 * @param sd [in] socket to send data
 * @param buff [in] string to send
 */
void do_send(int sd, char *buff)
{
  int n;

#ifdef WINSOCK
  n = send(sd, buff, strlen(buff), 0);
#else
  n = send(sd, buff, strlen(buff), 0);//n = write(sd, buff, strlen(buff));
#endif
  if (n < 0) {
    perror("Error: do_send");
  }
}

/**
 * Receive message from server for one line, and store it to bufffer.
 * The newline character at end will be stripped.
 *
 * @param sd [in] socket descriptor to receive data
 * @param buff [out] bufffer to store the received message string.
 * @param maxlen [in] maximum allowed length of @a buff
 *
 * @return pointer equal to @a buff, or NULL if error.
 */
char *do_receive(int sd, char *buff, int maxlen)
{
  int cnt;
  char *p;

  p = buff;
  while(1) {
#ifdef WINSOCK
   cnt = recv(sd, p, 1, 0);
#else
  cnt = recv(sd, p, 1, 0); //cnt = read(sd, p, 1);//1
#endif
    //printf("%i",cnt);
    if (cnt <= 0) return NULL;		/* eof or error */
    if (*p == '\n' && p > buff) {
      *p = '\0';
      break;
    } else {
      if (++p >= buff + maxlen) {
	fprintf(stderr,"Error: do_receive: line too long (> %d)\n", maxlen);
	exit(1);
      }
    }
  }
  return buff;
}


int do_connect(char *hostname, int portnum)
{port = portnum;
int result = WSAStartup(0x1010, &wdata);

    // get host address from specified server name
    host = gethostbyname(hostname);//       localhost

    if (host == NULL)
    {printf("%s: unknown host '%s'\n",port,hostname);  return(-1);}

    // now fill in sockaddr_in for remote address
    serv_addr.sin_family = host->h_addrtype;
    // get first address in host, copy to serv_addr
    memcpy((char *) &serv_addr.sin_addr.s_addr, host->h_addr_list[0], host->h_length);
    serv_addr.sin_port = htons(DEFAULT_PORT);
    memset(serv_addr.sin_zero, 0, 8);

    // create local stream socket
    client = socket(PF_INET, SOCK_STREAM, 0);
    if (client < 0) {perror("cannot open socket "); return(-1);}

    // bind local socket to any port number
    local_addr.sin_family = AF_INET;
    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    local_addr.sin_port = htons(0);
    memset(local_addr.sin_zero, 0, 8);

    rc = bind(client, (struct sockaddr *) &local_addr, sizeof(local_addr));

    if (rc < 0) {printf("%s: cannot bind port TCP %u\n",DEFAULT_SERVER,DEFAULT_PORT);  perror("error "); return(1);}

    // connect to server
    rc = connect(client, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
   if (rc < 0)  {perror("cannot connect ");   return(1); }

    sd = client;
  return(sd);
}

/**
 * Disconnect the server.
 *
 * @param sd [in] socket descriptor
 */
void do_disconnect(int sd)
{
komanda_die(sd);
}

void do_julius()
{HWND handle;

ShellExecute(handle,"open","julian.exe ","-C server_robot.jconf  -input mic -module","../Julius/bin/",SW_SHOWDEFAULT);
}

//*****************************************************************************
// Atpazinto zodzio isskyrimas
void *do_deshifre(char *buff, int maxlen)
{ int i,j;

if (((buff[0]==' ')&&(buff[1]==' ')&&(buff[2]==' ')&&(buff[3]==' ')))
{
   if ((buff[15]=='=')&&(buff[16]=='"')&&(buff[17]!='<'))
    { //yrazodis=1;
        for(i=0;i<17;i++)  {buff[i]=' '; } //pratrinam masyvus iki zodzio
                //trinam masyva po reikiamo zodzio
            for (i=18;i<maxlen;i++)
                { if (buff[i]=='"') {j=i; break;} }
             for (i=j;i<maxlen;i++) {buff[i]= NULL;}
             for (i=17;i<j+1;i++) {buff[i-17]=buff[i];buff[i]=NULL;}

       for (i=0;i<maxlen;i++) {zodis[i] = buff[i];}
       // zod_ilgis= j-17;
    }

   // else {*buff=NULL; }//yrazodis=0;
}
 return zodis;
}


