#include <sys/socket.h>

#include <string.h>

#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>


#include "usbip_common.h"
#include "usbip_network.h"
#include "usbip_my_net.h"

#define GETSERIAL_ERR 0xFFFFFFFFFFFFFFFF
//#define CONF_FILE   "/home/pi/nastavenie"
//#define CONF_FILE_NEW  "/home/pi/nastavenie.new"
//#define CONF_FILE_BU  "/home/pi/nastavenie.bu"



// do a nonblocking connect 
//  return -1 on a system call error, 0 on success
//  sa - host to connect to, filled by caller
//  sock - the socket to connect
//  timeout - how long to wait to connect
long long get_ID()
{
//	int f;
//	f = open("/proc/cpuinfo",O_RDONLY);
	
	return 0x123456789ABCDE01;
}
int conn_nonb(struct  addrinfo *sa, int sock, int timeout)
{   
    int flags = 0, error = 0, ret = 0;
    fd_set  rset, wset;
    socklen_t   len = sizeof(error);
    struct timeval  ts;
    
    ts.tv_sec = timeout;
    
    //clear out descriptor sets for select
    //add socket to the descriptor sets
    FD_ZERO(&rset);
    FD_SET(sock, &rset);
    wset = rset;    //structure assignment ok
    
    //set socket nonblocking flag
    if( (flags = fcntl(sock, F_GETFL, 0)) < 0)
        return -1;

    if(fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0)
        return -1;
    
    //initiate non-blocking connect
    if( (ret = connect(sock, sa->ai_addr, sa->ai_addrlen)) < 0 )
        if (errno != EINPROGRESS)
            return -1;
    if(ret == 0)    //then connect succeeded right away
        goto done;
    
    //we are waiting for connect to complete now
    if( (ret = select(sock + 1, &rset, &wset, NULL, (timeout) ? &ts : NULL)) < 0)
        return -1;
    if(ret == 0){   //we had a timeout
        errno = ETIMEDOUT;
        return -1;
    }

    //we had a positivite return so a descriptor is ready
    if (FD_ISSET(sock, &rset) || FD_ISSET(sock, &wset)){
        if(getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
            return -1;
    }else
        return -1;

    if(error){  //check if we had a socket error
        errno = error;
        return -1;
    }

done:
    //put socket back in blocking mode
    if(fcntl(sock, F_SETFL, flags) < 0)
        return -1;

    return 0;
}

int usbip_my_net_tcp_connect(char *hostname, char *service)
{
	struct addrinfo hints, *res, *rp;
	int sockfd;
	int ret;

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;

	/* get all possible addresses */
	ret = getaddrinfo(hostname, service, &hints, &res);
	if (ret < 0) {
		dbg("getaddrinfo: %s service %s: %s", hostname, service,
		    gai_strerror(ret));
		return ret;
	}

	/* try the addresses */
	for (rp = res; rp; rp = rp->ai_next) {
		sockfd = socket(rp->ai_family, rp->ai_socktype,
				rp->ai_protocol);
		if (sockfd < 0)
			continue;

		/* should set TCP_NODELAY for usbip */
		usbip_net_set_nodelay(sockfd);
		/* TODO: write code for heartbeat */
		usbip_net_set_keepalive(sockfd);

		if (conn_nonb(rp, sockfd,20) == 0) //non blocking connect timeout 20
			break;

		close(sockfd);
	}

	freeaddrinfo(res);

	if (!rp)
		return EAI_SYSTEM;

	return sockfd;
}
ssize_t usbip_my_net_recv(int sockfd, void *buff, size_t bufflen)
{
	return usbip_my_net_xmit(sockfd, buff, bufflen, 0);
}
ssize_t usbip_my_net_xmit(int sockfd, void *buff, size_t bufflen, int sending)
{
	ssize_t nbytes;
	ssize_t total = 0;

	if (!bufflen)
		return 0;

	do {
		if (sending)
			nbytes = send(sockfd, buff, bufflen, 0);
		else
			nbytes = recv(sockfd, buff, bufflen, MSG_DONTWAIT);

		if (nbytes <= 0)
			return nbytes; //return -1;

		buff	 = (void *)((intptr_t) buff + nbytes);
		bufflen	-= nbytes;
		total	+= nbytes;

	} while (bufflen > 0);

	return total;
}

int SaveNastavenia(struct Sdata * data){
  FILE *subor = NULL;
  int i;
    if (data->pocet_serverov >0 && data->pocet_serverov <=8){
        if ((subor = fopen(CONF_FILE_NEW,"w")) != NULL)
        {
            fprintf(subor,"%d\n",data->pocet_serverov);
            fprintf(subor,"%hu\n",data->verzia);
            for(i=0;i < data->pocet_serverov;i++)
                fprintf(subor,"%s\n",data->servers[i]);
        }
        fclose(subor);
        if(Zalohuj(CONF_FILE_NEW,CONF_FILE)==0)
             return 0;
        else
             return -2;
    }
    printf("Chyba ukladanej strukturu \n");
    return -1;
}

int GetNastavenia(struct Sdata * data){
  FILE *subor = NULL;
  int i;
  int err=0;
  //char buff[1024];
  //struct Sdata data;
  //data.servers = NULL;
  if ((subor = fopen(CONF_FILE,"r")) == NULL)
    {
        if ((subor = fopen(CONF_FILE_BU,"r")) == NULL)
        {
           printf("Chyba otvarania suboru (nastavenia) :(\n");
           return -1;
        }
    }
    fscanf(subor,"%d",&(data->pocet_serverov));
    fscanf(subor,"%hu",&(data->verzia));
    if (data->pocet_serverov<=8 && data->pocet_serverov>0)
        for(i=0;i < data->pocet_serverov;i++)
            if (fscanf(subor,"%s",data->servers[i])==EOF){
                err=1;
                break;
            }
    fclose(subor);
    if(err){
        return -1 ;
    }
    return 0;

}

unsigned long long GetSerial (void){
  FILE *info_file = NULL;
  char buff[1024];
  unsigned long long ret=GETSERIAL_ERR;
  if ((info_file = fopen("/proc/cpuinfo","r")) != NULL)
   {
    while (fscanf(info_file,"%s",buff) != EOF)
    {
      if (strcmp(buff,"Serial") == 0)
      {
        fscanf(info_file,"%s",buff);
        fscanf(info_file,"%s",buff);
        ret = strtoll(buff,NULL,16);
        //printf("%s\n",buff);
        //printf("%llu\n",ret);
      }
    }
    fclose(info_file);
    return ret;
    }
    printf("Chyba otvarania suboru (/proc/cpuinfo) :(\n");
    return GETSERIAL_ERR;
}

int Zalohuj (char* odkial, char* kam){
    int childExitStatus;
    pid_t pid;
    int status;
    if( access( odkial , F_OK ) != -1 ) {

      status = remove(kam);
      if(status == -1)
        printf("WARNING: BACKUP NEEXISTOVAL\n");
      pid = fork();

      if (pid == 0) { /* child */
        execl("/bin/cp", "/bin/cp",odkial, kam, (char *)0);
      }
      else if (pid < 0) {
        /* error - couldn't start process - you decide how to handle */
      }
      else
      {
        /* parent - wait for child - this has all error handling, you
         * could just call wait() as long as you are only expecting to
         * have one child process at a time.
         */
        pid_t ws = waitpid( pid, &childExitStatus, WUNTRACED | WCONTINUED /*(int)NULL*/);
        if (ws == -1)
        {
        printf("Code: ERR Wait for copy neuspesny\n");
 	}
        else
        {
          if( WIFEXITED(childExitStatus)) /* exit code in childExitStatus */
          {
            status = WEXITSTATUS(childExitStatus); /* zero is normal exit */
            if (status == 0)
                return 0;
          }
          else if (WIFSIGNALED(childExitStatus)) /* killed */
          {
                return -3;
          }
          else if (WIFSTOPPED(childExitStatus)) /* stopped */
          {
                return -2;
          }
        }
      }
   }
   else {
   // file doesn't exist
        printf("ERROR: konfiguracny subor neexistuje\n");
        return -1;
   }
   return -10;
}
void Vytlac(struct Sdata * data){
   int i;
   printf("pocet serverov:%d\n",data->pocet_serverov);
   printf("verzia:%hu\n",data->verzia);
   printf("servery:\n");
   for (i=0;i < data->pocet_serverov;i++)
	printf("%s\n",data->servers[i]);
}
