#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <pthread.h>
#include <time.h>
//#include <boost/thread/mutex.hpp>
//#include <boost/thread/thread.hpp>
#include <signal.h>
//#include <vector>
#include <unistd.h>
#include <stdlib.h>
//
//#include <cstdlib>

#include <direct.h>
#include <dos.h>
#include <string.h>
#ifdef WIN32
#include <conio.h>
#include <windows.h>
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#endif
//#include <iostream>

#define MAXLINELEN 255
#define strmatch !strcmp
#define SUCCESS 0
#define ERROR   1
#define SERVER_PORT 10500
#define MAX_MSG 256
#define DEFAULT_PORT 10500
//#define do_send send
#define DEFAULT_SERVER "localhost"


static char sbuf[MAXLINELEN]; ///< Local workarea for message string handling
static char rbuf[MAXLINELEN]; ///< Local workarea for message string handling
  int port;
  int sd;
char buf[4096];

    WSADATA wdata;
    int client;  /* client socket */
    int rc;
    struct sockaddr_in local_addr, serv_addr;
    struct hostent * host;
    char message[15] = {'.','l','i','s','t','p','r','o','c','e','s','s','/','n'};
    char date[25];

int readnf (int, char *);
int readline(int, char *, int);
int server;         /* listening socket descriptor */

void cleanup();
void handler(void * paramsd);
void usage();
void command_loop(int sd);
void do_output(int sd);
void do_command(int sd);
void japi_die(int sd);
void japi_get_version(int sd);
void japi_get_status(int sd);
void japi_pause_recog(int sd);
void japi_terminate_recog(int sd);
void japi_resume_recog(int sd);
void japi_set_input_handler_on_change(int sd, char *arg);
void do_send(int sd, char *buf);
char *do_receive(int sd, char *buf, int maxlen);
void do_sendf(int sd, char *fmt, ...);



/* main() */
int main (int argc, char *argv[])
{
char* gauta[255];
  if (argc < 2) {
    usage();
    return 1;
  }
  if (argc < 3) {
    port = DEFAULT_PORT;
  } else {
    port = atoi(argv[2]);

  }

   time_t serverio_patikr=clock(); //tipo ar serveris neluzo
int result = WSAStartup(MAKEWORD(2,2), &wdata);

    if(argc < 2) {
        printf("usage: %s <server>\n",argv[0]);
        exit(-1);
    }

    /* get host address from specified server name */
    host = gethostbyname(argv[1]);//       localhost

    if (host == NULL)
    {
        printf("%s: unknown host '%s'\n",argv[0],argv[1]);
        exit(-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(SERVER_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 ");
        exit(-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",argv[0],SERVER_PORT);
        perror("error ");
        exit(1);
    }

    /* connect to server */
    rc = connect(client, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
    if (rc < 0)
    {
        perror("cannot connect ");
        exit(1);
    }
  //Sleep(2000);
    rc = send(client, message, strlen(message) + 1, 0);
    if (rc < 0)
    {
        perror("cannot send data ");
        close(client);
        exit(-1);
    }

send(sd, "SYNCGRAM\n",10,0);


//do_send(client,  "RESUME\n",8,0);

 //message[2] = "R";message[3] = "e";message[2] = "n";message[4] = "  "; message[5] = " 1"; message[6] = " 3";

for(;;)
    {



 // if ((clock()-serverio_patikr > 5000)) {


    //    rc = send(client, message, strlen(message) + 1, 0);
   //  Sleep(200);
    // if (rc < 0)   { perror("Julius serveris neatsako "); close(client); exit(-1);}
    //  serverio_patikr=clock();
      // }
     //   readline(client,gauta,255);

  //  printf(gauta);
command_loop(0);
    }

    close(client);

 // command_loop(rc);
    return 0;

} /* main() */


/**
 * 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;
}

/**
 * readline() - read an entire line from a file descriptor until a newline.
 * functions returns the number of characters read but not including the
 * null character.
**/
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 buffer */
      str++;         /* increment buffer 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 buffer */
  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() */



// Thread handler for incoming connections...

void handler(void * paramsd) {
    struct sockaddr_in cliAddr;
    char line[MAX_MSG];
    char reply[MAX_MSG];
    int i;
    int client_local;   // keep a local copy of the client's socket descriptor
    int addr_len;       // used to store length (size) of sockaddr_in
    time_t currtime;
    char time_msg1[6] = {'/', 'T', 'I', 'M', 'E', '\n'};
    char time_msg2[6] = {'/', 'T', 'I', 'M', 'E', 13};
    char quit_msg1[6] = {'/', 'Q', 'U', 'I', 'T', '\n'};
    char quit_msg2[6] = {'/', 'Q', 'U', 'I', 'T', 13};
    char cmd[6];

    client_local = *((int *)paramsd); // store client socket descriptor
    addr_len = sizeof(cliAddr); // store value of size of sockaddr_in

    // get clients name and store in cliAddr
    getpeername(client_local, (struct sockaddr*)&cliAddr, &addr_len);
    // reset line
    memset(line, 0, MAX_MSG);

    // now read lines from the client socket
    while(readnf(client_local, line)!=ERROR)  // loop - read from socket
    {
        if (!strcmp(line,""))   // string must not be null string
            break;

        for (i = 0; i<6; i++)   // get first 6 chars of string, capitalize
            cmd[i] = toupper(line[i]);
        // Did client ask for time?
        if (strncmp(cmd, time_msg1, 6) == 0 || strncmp(cmd, time_msg2, 6) == 0) {
            printf("Received /TIME from %s:%d\n", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port));
            time(&currtime);                  // get current time
            strcpy(reply, ctime(&currtime));  // copy into string
            send(client_local, reply, strlen(reply), 0); // return current time to client
        }
        // Does client want to quit?
        else if (strncmp(cmd, quit_msg1, 6) == 0 || strncmp(cmd, quit_msg2, 6) == 0)
        {
            printf("Received /QUIT from %s:%d\n", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port));
            break;
        }
        else if (strlen(line) > 2)
        {
            strcpy(reply, "You:");
            strcat(reply, line);
            send(client_local, reply,strlen(reply),0);
        }
        // reset line
        memset(line,0,MAX_MSG);
    } // while(readnf)

    close(client_local);
    return;
} // handler()

/**
 * <EN>
 * Send command: process user command string in sbuf buffer.
 *
 * @param sd [in] socket to send data
 * </EN>
 */
void do_command(int sd)
{
  char *p, *com, *arg1, *arg2;

    com = strtok(sbuf, " \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")) {
      japi_die(sd);
    } else if (strmatch(com, "version")) {
      japi_get_version(sd);
    } else if (strmatch(com, "status")) {
      japi_get_status(sd);
    } else if (strmatch(com, "pause")) {
      japi_pause_recog(sd);
    } else if (strmatch(com, "terminate")) {
      japi_terminate_recog(sd);
    } else if (strmatch(com, "resume")) {
      japi_resume_recog(sd);
    } else if (strmatch(com, "inputparam")) {
      japi_set_input_handler_on_change(sd, arg1);
    } else if (strmatch(com, "changegram")) {
      japi_change_grammar(sd, arg1);
    } else if (strmatch(com, "addgram")) {
      japi_add_grammar(sd, arg1);
    } else if (strmatch(com, "deletegram")) {
      japi_delete_grammar(sd, arg1);
    } else if (strmatch(com, "activategram")) {
      japi_activate_grammar(sd, arg1);
    } else if (strmatch(com, "deactivategram")) {
      japi_deactivate_grammar(sd, arg1);
    } else if (strmatch(com, "syncgram")) {
      japi_sync_grammar(sd);
    } 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
 */
void do_output(int sd)
{
  while(read(sd, rbuf, MAXLINELEN) != NULL) {
    if (rbuf[0] == '.' && rbuf[1] == '\0') break;
    printf("> %s\n", rbuf);
  }
}

/* handle input event */


/**
 * @brief Main event loop
 *
 * This is main loop to watch events from server (message of recognition
 * results etc.) and tty (user keyboard input), catch and process them.
 *
 * @param sd [in] socket to send data
 */
void command_loop(int sd)
{
#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;

  slen = 0;

  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;

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

    if (nfd < 0) { /* winsock error */
      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");
      //exit(1);
    }
    if (FD_ISSET(sd, &readfds)) {      /* from server */
      do_output(sd);
    } else {  /* timeout, check for keyboard input */
      if (_kbhit()) {
	ch = _getche();
	if (ch == '\r') ch = '\n';
	sbuf[slen] = (char)ch;
	slen++;
	if (ch == '\n') {
	  sbuf[slen] = '\0';
	  do_command(sd);	/* execute command */
	  slen = 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)) {
      /* stdin */
      if (fgets(sbuf, MAXLINELEN, stdin) != NULL) {
	do_command(sd);
      }
    }
    if (FD_ISSET(sd, &readfds)) {
      /* from server */
      do_output(sd);
    }
  }

#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 japi_die(int sd)
{
  do_send(sd, "DIE\n");
}

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

/**
 * Command "STATUS": ask server about its current status (run/stop)
 *
 * @param sd [in] socket to send data
 */
void japi_get_status(int sd)
{
  do_send(sd, "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 japi_pause_recog(int sd)
{
  do_send(sd, "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 japi_terminate_recog(int sd)
{
  do_send(sd, "TERMINATE\n");
}

/**
 * Command "RESUME": tell server to restart recognition.
 *
 * @param sd [in] socket to send data
 */
void japi_resume_recog(int sd)
{
  do_send(sd, "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 japi_set_input_handler_on_change(int sd, char *arg)
{
  /* argument should be checked here... */
  /* one of TERMINATE, PAUSE, WAIT */

  /* send */
do_sendf(sd, "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(buf, MAXLINELEN, "%s.dfa", prefix);
  if (access(buf, R_OK) < 0) {
    perror("japi_change_grammar"); return -1;
  }
  snprintf(buf, MAXLINELEN, "%s.dict", prefix);
  if (access(buf, R_OK) < 0) {
    perror("japi_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(buf, MAXLINELEN, "%s.dfa", prefix);
    if ((fp = fopen(buf, "r")) == NULL) {
      perror("japi_change_grammar"); return -1;
    }
    while(fgets(buf, MAXLINELEN, fp) != NULL) {
      do_send(sd, buf);
    }
    do_send(sd, "DFAEND\n");
    fclose(fp);
    snprintf(buf, MAXLINELEN, "%s.dict", prefix);
  } else {
    snprintf(buf, MAXLINELEN, "%s", prefix);
  }
  if ((fp = fopen(buf, "r")) == NULL) {
    perror("japi_change_grammar"); return -1;
  }
  while(fgets(buf, MAXLINELEN, fp) != NULL) {
    do_send(sd, buf);
  }
  do_send(sd, "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(buf, idstr);
  p = buf;
  while(*p != '\0') {
    if (*p == ',') *p = ' ';
    p++;
  }
  /* send them */
  do_sendf(sd, "%s\n", buf);
  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 japi_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(buf, MAXLINELEN, "CHANGEGRAM %s\n", prefixpath);
  do_send(sd, buf);
  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 japi_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(buf, MAXLINELEN, "ADDGRAM %s\n", prefixpath);
  do_send(sd, buf);
  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 japi_delete_grammar(int sd, char *idlist)
{
  do_send(sd, "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 japi_activate_grammar(int sd, char *idlist)
{
  do_send(sd, "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 japi_deactivate_grammar(int sd, char *idlist)
{
  do_send(sd, "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 japi_sync_grammar(int sd)
{
  do_send(sd, "SYNCGRAM\n");
}

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

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

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

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

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

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






