//#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <resolv.h>
#include <unistd.h>
#include <time.h>
#include <sys/mman.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include "aes.h"
#include <signal.h>



#define PORT_FTP        4444           /* port */
#define SERVER_ADDR     "192.168.1.101"     /* localhost */
#define MAXBUF 1024

using namespace std;



void connect_GetPass(int sock, uint *heslo);
long long modpow(long long b, long long e, long long m) {
    long long result = 1;

    while (e > 0) {
        if ((e & 1) == 1) {
            /* multiply in this bit's contribution while using modulus to keep
             * result small */
            result = (result * b) % m;
        }
        b = (b * b) % m;
        e >>= 1;
    }

    return result;
}

void zahesluj(char* vstup, uint heslo, int lenght)
        {
            unsigned char* ivstup=(unsigned char*)vstup;
            unsigned char rot[32];
            for (int i = 0; i < 32; i++)
                rot[i] = (unsigned char)(((heslo >> i) % 7) + 1);

            for (int j = 0; j < 32; j += 2)
            {
                unsigned char pomocna = ivstup[lenght - 1];
                for (int i = lenght - 1; i >= 0; i--)
                {
                    if (i == 0)
                        ivstup[i] = (unsigned char)((pomocna << (8 - rot[j])) | (ivstup[i] >> rot[j]));
                    else
                        ivstup[i] = (unsigned char)((ivstup[i - 1] << (8 - rot[j])) | (ivstup[i] >> rot[j]));
                }
                for (int i = 0; i < lenght; i++)
                {
                    ivstup[i] = (unsigned char)((ivstup[i] << (8 - rot[j + 1])) | (ivstup[i] >> rot[j + 1]));
                }
            }
            return;
        }
void zahesluj(char* vstup, uint heslo, int start_index, int lenght)
        {
            unsigned char* ivstup=(unsigned char*)vstup;
            unsigned char rot[32];
            for (int i = 0; i < 32; i++)
                rot[i] = (unsigned char)(((heslo >> i) % 7) + 1);

            for (int j = 0; j < 32; j += 2)
            {
                unsigned char pomocna = ivstup[start_index + lenght - 1];
                for (int i = start_index + lenght - 1; i >= start_index; i--)
                {
                    if (i == start_index)
                        ivstup[i] = (unsigned char)((pomocna << (8 - rot[j])) | (ivstup[i] >> rot[j]));
                    else
                        ivstup[i] = (unsigned char)((ivstup[i - 1] << (8 - rot[j])) | (ivstup[i] >> rot[j]));
                }
                for (int i = start_index; i < start_index + lenght; i++)
                {
                    ivstup[i] = (unsigned char)((ivstup[i] << (8 - rot[j + 1])) | (ivstup[i] >> rot[j + 1]));
                }
            }
            return;
        }
void odhesluj(char* vstup, uint heslo, int lenght)
        {
            unsigned char* ivstup=(unsigned char*)vstup;
            unsigned char rot[32];
            for (int i = 0; i < 32; i++)
                rot[i] = (unsigned char)(((heslo >> i) % 7) + 1);

            for (int j = 31; j >= 0; j -= 2)
            {
                for (int i = 0; i < lenght; i++)
                {
                    ivstup[i] = (unsigned char)((ivstup[i] >> (8 - rot[j])) | (ivstup[i] << rot[j]));
                }
                unsigned char pomocna = ivstup[0];
                for (int i = 0; i < lenght; i++)
                {
                    if (i == (lenght - 1))
                        ivstup[i] = (unsigned char)((pomocna >> (8 - rot[j - 1])) | (ivstup[i] << rot[j - 1]));
                    else
                        ivstup[i] = (unsigned char)((ivstup[i + 1] >> (8 - rot[j - 1])) | (ivstup[i] << rot[j - 1]));
                }

            }
            return;
        }
void odhesluj(char* vstup, uint heslo, int start_index, int lenght)
        {
            unsigned char* ivstup=(unsigned char*)vstup;
            unsigned char rot[32];
            for (int i = 0; i < 32; i++)
                rot[i] = (unsigned char)(((heslo >> i) % 7) + 1);

            for (int j = 31; j >= 0; j -= 2)
            {
                for (int i = start_index; i < start_index + lenght; i++)
                {
                    ivstup[i] = (unsigned char)((ivstup[i] >> (8 - rot[j])) | (ivstup[i] << rot[j]));
                }
                unsigned char pomocna = ivstup[start_index];
                for (int i = start_index; i < start_index + lenght; i++)
                {
                    if (i == (start_index + lenght - 1))
                        ivstup[i] = (unsigned char)((pomocna >> (8 - rot[j - 1])) | (ivstup[i] << rot[j - 1]));
                    else
                        ivstup[i] = (unsigned char)((ivstup[i + 1] >> (8 - rot[j - 1])) | (ivstup[i] << rot[j - 1]));
                }

            }
            return;
        }

void connect_GetPass(int sock,uint *heslo){

    *heslo=0;

    srand((uint)time((time_t *)NULL));
    uint p = rand()/2+INT_MAX/2;
    uint q = rand()/2+INT_MAX/2;
    uint A = rand()/2+INT_MAX/2;
    uint N = modpow(q,A,p);
    char mesage[12];

    *((uint *)(&(mesage[0])))= p;
    *((uint *)(&(mesage[4])))= q;
    *((uint *)(&(mesage[8])))= N;
    //printf("poslane data:%d\n\r%d\n\r%d\n\r",p,q,N);

    send(sock,(void*)mesage,12,(int)NULL);
    //printf("poslane data:%d\n\r",poslane);
    int c=0;
    while (c++<10)
    {
        usleep(200000);
        int priate =recv(sock, (void*)mesage, 12,MSG_DONTWAIT );
    //printf("priate data:%d\n\r",priate);
        if (priate>11)
            c=20;
    }
    if (c>15){
            uint M = *((uint *)(&(mesage[4])));
            *heslo = modpow(M,A,p);
    }
}

void status_port(bool* status,int* VIDA,int *PIDA, int *VIDB, int *PIDB){

    FILE *fp;
    char path[1035];
    *VIDA=*PIDA=*VIDB=*PIDB=0;
    status[0]=false;
    status[1]=false;
    status[2]=false;
    status[3]=false;
    /* Open the command for reading. */
    fp = popen("usbip list -l", "r");
    if (fp == NULL) {
      printf("Failed to run command\n" );
      exit(1);
    }
    /* Read the output a line at a time - output it. */
    char* portA,*portB;
    while (fgets(path, sizeof(path)-1, fp) != NULL) {
        portA=strstr(path,"1-1.2 (");
        portB=strstr(path,"1-1.3 (");
      if (strstr(path,"1-1.2")!=NULL && strstr(path,"usbip-host")!=NULL)
      {
          printf("PORTA:BINDED\n\r");
          status[1]=true;
      }
      if (strstr(path,"1-1.3")!=NULL && strstr(path,"usbip-host")!=NULL)
      {
          printf("PORTB:BINDED\n\r");
          status[3]=true;
      }
      if(portA!=NULL)
      {
          portA[16]='\0';
          printf("PORTA: %s\n\r", portA+7);
          sscanf(portA+7,"%X:%X",VIDA,PIDA);
          status[0]=true;
      }
      if(portB!=NULL)
      {
          portB[16]='\0';
          printf("PORTB: %s\n\r", portB+7);
          sscanf(portB+7,"%X:%X",VIDB,PIDB);
          status[2]=true;
      }

    }
    /* close */
    pclose(fp);
    if (status[0] && !status[1]){
        fp = popen("sudo usbip bind -b 1-1.2", "r");
        if (fp == NULL) {
          printf("Failed to run command\n" );
          //exit(1);
        }
        else{
        /* close */
            pclose(fp);
            status[1]=true;
            printf("BINDED_PORTB\n\r" );
        }
    }
    if (status[2] && !status[3]){
        fp = popen("sudo usbip bind -b 1-1.3", "r");
        if (fp == NULL) {
          printf("Failed to run command\n" );
          //exit(1);
        }
        else{
        /* close */
            pclose(fp);
            status[3]=true;
            printf("BINDED_PORTB\n\r" );
        }
    }
   // return 0;

}

int Comunication_thread(int *mem_shr, int sock){

    char buffer[32];
    int VIDA,PIDA,VIDB,PIDB;
    int rcv;
    //char * IP;

    while(true){

        rcv=recv(sock,buffer,32,(int)NULL);
        if (rcv==0){
            close(sock);
            printf("koniec_Klient_lost_connection\n\r");
            munmap(mem_shr, sizeof(int));
            return 0;
        }
        switch(buffer[0]){
        case 0:	//VRAT STAV NA PORTOCH
                status_port((bool*)buffer,&VIDA,&PIDA,&VIDB,&PIDB);

                *(unsigned short*)(buffer+4)=(unsigned short)VIDA;
                *(unsigned short*)(buffer+6)=(unsigned short)PIDA;
                *(unsigned short*)(buffer+8)=(unsigned short)VIDB;
                *(unsigned short*)(buffer+10)=(unsigned short)PIDB;

                send(sock,buffer,12,(int)NULL);
                break;
        case 1:	
                break;
        default:
                close(sock);
                printf("koniec_Klient\n\r");
                munmap(mem_shr, sizeof(int));
                return 0;
                break;
        }
        close(sock);
	printf("koniec_Klient_odpovedal\n\r");
	return 0;
	
        if (*mem_shr ==1){
            close(sock);
            printf("koniec_Klient\n\r");
            munmap(mem_shr, sizeof(int));
            return 0;
        }
    }
}

int Listener_thread(int * koniec){
    int sockfd,newsockfd;
    socklen_t clilen;
    struct sockaddr_in serv_addr, cli_addr;
    struct timeval timeout;

    int portno;

   
        if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
        {
            printf("error creating listen socket\n\r");
            //exit(-1);
            return -1;//error
        }
        bzero((char *) &serv_addr, sizeof(serv_addr));
        portno = 4444;
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = INADDR_ANY;
        serv_addr.sin_port = htons(portno);

        if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
        {
		  printf("ERROR on binding\n\r");
		  return -1;//error
        }
        
        timeout.tv_sec = 10;
        timeout.tv_usec = 0;
        if (setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout)) < 0)
	{
		  printf("ERROR setsockopt failed\n");
		  return -1;//error
	}
        listen(sockfd,5);
        clilen = sizeof(cli_addr);
	while(true){
		newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr,&clilen);
		if (newsockfd < 0)
		
			printf("Listener living \n\r");
		else{
			int ppid=fork();

			if (ppid!=0)
			    return Comunication_thread(koniec,newsockfd);

			close(newsockfd);
		}
		if (*koniec ==1){
			close(sockfd);
			printf("koniec_listener\n\r");
			munmap(koniec, sizeof(int));
			return 0;
		}
	  
	}
}

unsigned long long get_ID(){
    char path[512];
    char *ptr;
    int f;
    unsigned long long serial=0;

    f = open("/proc/cpuinfo",O_RDONLY);
    if (f<=0)
        return serial;
    //f = open("/home/matus/Downloads/cpuinfo",O_RDONLY);
    read(f,path,512);
    ptr=strstr(path,"Serial");
    ptr=strstr(ptr,":");
    serial=strtoull(ptr+2,NULL,16);
    return serial;
}

int main()
{
    int sockfd;
    struct sockaddr_in dest;
    char buffer[MAXBUF];
    int Com_str,ret;
    int *mem_shr, * koniec;
    int ppid;
    uchar in[16];
    uchar key[16];
    uchar out[16];
    uchar expkey[4 * Nb * (Nr + 1)];
    unsigned long long serial;
    int VIDA,PIDA,VIDB,PIDB;
    
    
    serial = get_ID();
    printf("%Lu\n\r",serial);
    //TEST AES
    ExpandKey (key, expkey);
    strcpy((char*)in,"AHOJKy");
    Encrypt (in, expkey, out);
    strcpy((char*)in,"DELETE:IN");
    Decrypt(out,expkey,in);

    
    //TEST AES



   //************OTVORENIE ZDIELANEJ PAMäTE *************

    Com_str = shm_open("/_USB_IP_SHR_MEM_", O_CREAT | O_RDWR ,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP| S_IROTH | S_IWOTH);
    if (Com_str == -1)
    {
        printf("Nepodarilo sa vytvorit zdielaný zdroj\n\r");
        return -1;
    }

    if (ftruncate(Com_str, 32) == -1)
    {
        printf("Nepodarilo sa vytvorit Velkosť pre zdielaný zdroj\n\r");
        return -1;
    }   /* Handle error */;

    /* Map shared memory object */
    koniec=mem_shr = (int*)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
                   MAP_SHARED, Com_str, 0);
    
    ret = msync(mem_shr,32,MS_INVALIDATE | MS_SYNC);
    if (ret!=0){
	printf("chyba vytvorenia pamate");
	return -1;
    }
   printf("pred %d\n\r",mem_shr[0]);
   // mem_shr[0]=1234;
   // printf("po %d\n\r",mem_shr[0]);
   printf("USBIP ID: %d\n\r",mem_shr[9]);

    //kill(,SIGTERM);
    msync(mem_shr,32,MS_INVALIDATE | MS_SYNC);
    //printf("return %d\n\r",ret);

    *koniec=0;
     //*****END****OTVORENIE ZDIELANEJ PAMäTE *****END*****

    //vytvorenie listeneru (vlakno potrebné pre prvotnú konfiguráciu zariadenia)
    ppid=fork();


    if (ppid!=0){
        return Listener_thread(koniec);
    }
    
    //*********** NACITANIE DOSTUPNYCH SERVEROV SO SUBORU ***********
    int pocet_serverov=0;
    char servers[10][256];
	   FILE *stream;
           size_t len = 0;
           ssize_t read;
	   char *line = NULL;

           stream = fopen("/home/pi/usbip_linux/client_app/config", "r");
           if (stream == NULL)
               return -1;
	   
           while ((read = getline(&line, &len, stream)) != -1) {
               printf("Retrieved line of length %zu :\n", read);
               printf("%s", line);
	       strncpy(servers[pocet_serverov++],line,read);
           }
	   free(line);
           fclose(stream);
  
    //****END**** NACITANIE DOSTUPNYCH SERVEROV SO SUBORU ****END****
    
    
    //************ PRIPAJANIE SA NA SERVER *************
    //pripojenie na jeden z dostupných serverov

      printf("Otvaranie soketu\n\r");
	  /*---Open socket for streaming---*/
      if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
      {
	  perror("Socket");
	  *koniec=1;
	  //exit(errno);
	  return -1;
      }
      
uint pass;   
int i=0;
while ((*koniec & 0x01) >=0){

      if (i>=pocet_serverov)
	i=0;
      
	  /*---Initialize server address/port struct---*/
      bzero(&dest, sizeof(dest));
      dest.sin_family = AF_INET;
      dest.sin_port = htons(PORT_FTP);
      if ( inet_aton(servers[i++], (in_addr*)&dest.sin_addr.s_addr) == 0 )
      {
	  perror(SERVER_ADDR);
	  continue;
	  //koniec=1;
	  //exit(errno);
	  //return -1;
      }
      printf("connecting\n\r");
	  /*---Connect to server---*/
      if ( connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0 )
      {
	  printf("ERROR nepodarilo sa pripojit \n\r"); // nastane po uplinutí timeoutu
	  usleep(10000000);
	  continue;
	  //*koniec=1;
	  //exit(errno);
	  //return -1;
      }
      
      
      connect_GetPass(sockfd,&pass);	//vytvorenie zabezepečeného spojenia

      printf("heslo %d\n\r",pass);

      int ParentID=fork();
      
      while (ParentID==0){
	  sprintf(buffer,"R_ADD_DEVI");                           //prihlasenie o existencii
	  *(unsigned long long*)(&buffer[10])=get_ID();    //ID zariadenia
	  
	  status_port((bool*)buffer+20,&VIDA,&PIDA,&VIDB,&PIDB);

          *(unsigned short*)(buffer+20)=(unsigned short)VIDA;
          *(unsigned short*)(buffer+22)=(unsigned short)PIDA;
          *(unsigned short*)(buffer+24)=(unsigned short)VIDB;
          *(unsigned short*)(buffer+26)=(unsigned short)PIDB;
		
	  zahesluj(buffer,pass,0,32);                             //zaheslovanie
	  usleep(10000);
	  
	  int sended = send(sockfd,buffer,32,MSG_DONTWAIT);                      //odoslanie
	  usleep(10000000);
	  printf("poslane koniec=%d\n\r",*koniec);
	  printf("poslane SENDED=%d\n\r",sended);

	  if ((*koniec & 0x02) >0){
	      *koniec=*koniec & 0xFD;
	      printf("koniec_perioda\n\r");
	      munmap(koniec, sizeof(int));
	      close(sockfd);
	      return 0;
	  }
      }
      int received;
      char buf[256];
      char* IP;
      int VIDA,PIDA,VIDB,PIDB;
      do{
	  received = recv(sockfd,buf,256,(int)NULL);
	  odhesluj(buf,pass,0,received);
	  printf("Priate data:%d",received);
	  printf("Priate DATA %u:%u.%u.%u.%u\n\r",buf[0],buf[1],buf[2],buf[3],buf[4]);
	  switch(buf[0]){
	  case 0:	//VRAT STAV NA PORTOCH
		  status_port((bool*)buf,&VIDA,&PIDA,&VIDB,&PIDB);
		  
		  *(unsigned short*)(buf+4)=(unsigned short)VIDA;
		  *(unsigned short*)(buf+6)=(unsigned short)PIDA;
		  *(unsigned short*)(buf+8)=(unsigned short)VIDB;
		  *(unsigned short*)(buf+10)=(unsigned short)PIDB;
		  
		  send(sockfd,buf,12,(int)NULL);
		  break;
	  case 1:	//PRIPOJ SA NA IP
		  IP=(char*)(mem_shr+18);
		  
		  IP[0]=buf[1];
		  IP[1]=buf[2];
		  IP[2]=buf[3];
		  IP[3]=buf[4];
		  mem_shr[19]=1;
		  msync(mem_shr,32,MS_INVALIDATE | MS_SYNC);
		  kill(mem_shr[9],SIGIO);
		  printf("Priata poziadavka na %u.%u.%u.%u\n\r",IP[0],IP[1],IP[2],IP[3]);
		  break;
	  case 2:	  
		  popen("sudo reboot", "r");
		  break;
	  default:
		  break;
        }
        
      }while (received>0);
      close(sockfd);
      *koniec=*koniec|0x02;
      printf("koniec_primanie\n\r");
      usleep(20000000);
      //munmap(koniec, sizeof(int));
      //close(sockfd);
      printf("Otvaranie soketu\n\r");
	  /*---Open socket for streaming---*/
      if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
      {
	  perror("Socket");
	  continue;//*koniec=1;
	  //exit(errno);
	  return -1;
      }
      //return 0;
}
/*
int received;
char buf[256];
do{
    received = recv(sockfd,buf,256,(int)NULL);
    odhesluj(buf,pass,0,received);
    //co tu spraviť?
    //poslať príkaz hlavnej aplikácií aby sa pripojila na požadovanú adresu,



}while (received==0);
*koniec=1;
    printf("koniec_primanie\n\r");
    munmap(koniec, sizeof(int));
    close(sockfd);
return 0;
*/
   return 0;
}


