#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h> 
#include <fcntl.h> 
#include <iostream>
#include <stdlib.h>
#include <sys/times.h>  
#include <string.h>
#include <signal.h>
#include "Socket_Request.h"
#include <Command_Type.h>

#define TIMETOWAIT 200 // ticks, 0.01 sec = 1 tick, wait for clients
#define WAITFORHEADER 10 
#define WAITFORINFO 1500 // ticks, wait for info from gl server

extern int net_timeout;


// class to connect socket clients to GL server's ipc queue 

Robot_Socket::Robot_Socket( Message *ptr, int port ) {

  char name[80];
  int server_len;
  struct sockaddr_in server_address;
  
  msg_ptr = ptr;

  // creating socket 
  server_socket = socket(AF_INET, SOCK_STREAM, 0);
  if( server_socket == -1 ) {
    perror( " Error in creation of Socket Interface ");
    return;
  }

  int flags = fcntl( server_socket, F_GETFL); 
  fcntl( server_socket, F_SETFL, flags | O_NONBLOCK ); 

  server_address.sin_family = AF_INET;
  gethostname( name, 79 );
  hostent *hostinfo = gethostbyname( name);
  if( hostinfo == NULL ) {
    perror( " Cannot get host by name for Socket Interface ");
    return;
  }
  cerr << endl << endl << "official host name is " 
       << hostinfo->h_name << endl;


  // tricky conversation, isn't it ?
  server_address.sin_addr = * ( ( struct in_addr * ) 
				( * hostinfo->h_addr_list ) );
  server_address.sin_port = htons( (unsigned short) (++port) );
  cerr << endl << "Attention : port number for socket clients is " 
       << port << endl << endl;
  server_len = sizeof(server_address);
  if( bind( server_socket, 
	    (struct sockaddr *) &server_address, 
	    server_len) ) {
    perror("Cannot bind to Socket Interface ");
    return;
  }
  listen(server_socket, 3);

}


Robot_Socket::~Robot_Socket() {

  cerr << "closing Socket Interface" << endl;
  close(server_socket);

}


// checking socket and serving to it's clients 
int Robot_Socket::check_socket() {
  
  int client_socket;
  struct sockaddr_in client_address;
  int len, i, nbytes;
  tms tmbuf;

  len = sizeof(client_address);
  client_socket = accept(server_socket, 
			 (struct sockaddr *) & client_address, 
			 (size_t*) &len );
  if( client_socket != -1){

    int tm = times(&tmbuf);
    ////// here could be check for intruders, #:0)
    // cerr << "connection from " 
    // << inet_ntoa(client_address.sin_addr) << endl;
    int flags = fcntl( client_socket, F_GETFL); 
    fcntl( client_socket, F_SETFL, flags | O_NONBLOCK );
    
    My_Message mes;
    unsigned char ch;
    bool got_header  = false;
    while( ( ( times(&tmbuf) - tm ) < WAITFORHEADER ) &&
	   ( got_header == false) ) {
      if( read( client_socket, &ch, 1) == 1 ) {
	got_header = true;
      } else {
	usleep(100);
      }
    }
    if( got_header == false ){
      close(client_socket);
      cerr << "ERROR : could not get length of package" << endl;
      return -1;
    }

    len = ch;
    char* commandline = new char[len + 1];
    i = 0;
    while( i < len ) {
      nbytes = read( client_socket, &commandline[i], (len - i) );
      if( ( nbytes == -1 ) || 
	  ( ( times(&tmbuf) - tm ) > TIMETOWAIT ) ) {
	close(client_socket);
	cerr << "ERROR : could not read commandline" << endl;
	delete[] commandline;
	return -1;
      }
      i += nbytes;
    }
    commandline[i] = '\0';
    //cerr << "commandline : \"" << commandline << "\"" << endl;
    sscanf( commandline, 
	    "%ld,%ld,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
	    &mes.command,
	    &mes.lparam, &mes.param[0], &mes.param[1],
	    &mes.param[2], &mes.param[3], &mes.param[4],
	    &mes.param[5], &mes.param[6], &mes.param[7],
	    &mes.param[8], &mes.param[9], &mes.param[10],
	    &mes.param[11], &mes.param[12], &mes.param[13],
	    &mes.param[14], &mes.param[15], &mes.param[16],
	    &mes.param[17], &mes.param[18], &mes.param[19] );

    //cerr << mes.command << endl;
    //cerr << mes.lparam << endl;
    //for( int k = 0; k < 20 ; k++ ) cerr << mes.param[k] << endl;

    // before sending - clearing all old messages from gl server

    My_Message oldmsg;
    while( msg_ptr->receive( oldmsg, 2) != -1 );

    mes.message_type = 1;
    msg_ptr->send( mes );
    
    if( mes.command == GET_INFO ){
      reply_to_client( client_socket);
    } 
    if( mes.command == SET_NET_TIMEOUT ){
      net_timeout = mes.lparam;
    } 

    close(client_socket);
    delete[] commandline;

    return 0;
  }

  return -1;
}


// i don't want server to be killed by sistem's SIGPIPE signal 
// during reply, #:0|
void cannotwrite(int sig){
    cerr << "Ohhh, I cannot write to socket !!!" << endl;
}

// sending requested information to client
int Robot_Socket::reply_to_client(int client_socket) {

  My_Message inf;
  unsigned char packlen = 0;
  tms tmbuf;

  signal(SIGPIPE, cannotwrite );


  inf.command = 0;
  int tm = times( &tmbuf);
  while( ( (times( &tmbuf) - tm) < WAITFORINFO ) &&
	 ( msg_ptr->receive( inf, 2) == -1) ) {
    usleep(100);
  }

  if( inf.command != 0 ){ 

    char reply[255];
    sprintf( reply, 
	     "%ld %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f",
	     inf.lparam,    inf.param[0],  inf.param[1],
	     inf.param[2],  inf.param[3],  inf.param[4],
	     inf.param[5],  inf.param[6],  inf.param[7],
	     inf.param[8],  inf.param[9],  inf.param[10],
	     inf.param[11], inf.param[12], inf.param[13],
	     inf.param[14], inf.param[15], inf.param[16],
	     inf.param[17], inf.param[18], inf.param[19]  );
    packlen =  strlen(reply) + 1;
    write( client_socket, &packlen, 1);
    write( client_socket, reply, packlen);

  } else { 

    write( client_socket, &packlen, 1);

  }

  return 0;
}

