#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>
#include <bluetooth/sco.h>
#include <arpa/inet.h>   
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>

#include "server.h"
#include "socket.h"

#define BILLION 1000000000

int receive_file(int socket, int len, bt_intfr_config * config)
{ // Zacatek funkce

        struct timespec start, stop;
   	double accum;
   	int i;
   	int j = 0;
   
   	if( clock_gettime( CLOCK_REALTIME, &start) == -1 ) {
      	perror( "clock gettime" );
      	exit( EXIT_FAILURE );
    	}
	
	int buffersize = 0, recv_size = 0, read_size, write_size, packet_index =1,stat;
	long size = 0;

	char filearray[len];
	FILE *filer;

	// Nalezeni velikosti souboru
	do {
		stat = read(socket, &size, sizeof(long));
	} while(stat<0);

	printf("Packet has been received.\n");
	printf("Packet size: %lu B\n", sizeof(filearray));
	printf("File size: %li B\n", size);
	printf(" \n");

	char verification_signal[] = "mam to";

	printf("Sending verification signal.\n");
	do {
		stat = write(socket, &verification_signal, sizeof(int));
	} while(stat<0);

	printf("Answer was sent.\n");
	printf(" \n");

	filer = fopen(config->filename, "w");

	if( filer == NULL) {
		printf("Error: File cannot be opened.\n");
		return -1; }

	//Smycka dokud neobdrzime vsechny pakety


	int need_exit = 0;
	struct timeval timeout = {60,0};

	fd_set fds;
	int buffer_fd, buffer_out;

	while(recv_size < size)
	{
		FD_ZERO(&fds);
		FD_SET(socket,&fds);

		buffer_fd = select(FD_SETSIZE,&fds,NULL,NULL,&timeout);

		if (buffer_fd < 0)
			printf("Error: Bad file descriptor set.\n");

		if (buffer_fd == 0)
			printf("Error: Buffer read timeout expired.\n");

		if (buffer_fd > 0)
		{
			do{
				read_size = read(socket,filearray, len);
			}while(read_size <0);

			printf("Received packet number: %i\n",packet_index);
			printf("Received packet size: %i B\n",read_size);


			// Zapsani obdrzenych dat do souboru
			write_size = fwrite(filearray,1,read_size, filer);
			printf("Written packet size: %i B\n",write_size); 

			if(read_size !=write_size) {
				printf("Error: By read and write.\n");    }


			//Zvyseni poctu paketu
			recv_size += read_size;
			packet_index++;
			printf("Received file size: %i B\n",recv_size);
			printf(" \n");
		}

	}


	fclose(filer);
	printf("File was successfully accepted.\n");

        if( clock_gettime( CLOCK_REALTIME, &stop) == -1 ) {
      	perror( "clock gettime" );
      	exit( EXIT_FAILURE );
     	}

	accum = stop.tv_nsec - start.tv_nsec;
	accum /= BILLION;
	accum += stop.tv_sec - start.tv_sec;
    	printf( "Receiving - transmission time was: %lf s\n", accum );
	double rychlost;
	rychlost = recv_size/accum;
	printf("Receiving - transmission speed was: %lf B/s\n", rychlost);
	return 1;
}

void accept_l2cap_server(int s, bt_intfr_config * config)
{
	struct sockaddr_l2 loc_addr = { 0 }, rem_addr = { 0 };
	int client, bytes_read;
	int mtu = 1021;
    	char bdaddr_str [20];
    	socklen_t opt = sizeof(rem_addr);
	
	// prepnuti socketu do rezimu poslouchani
	listen(s, 1);

	// prijeti jednoho spojeni
	client = accept(s, (struct sockaddr *)&rem_addr, &opt);

	ba2str( &rem_addr.l2_bdaddr, bdaddr_str);
	fprintf(stderr, "Accepted l2cap connection from %s\n", bdaddr_str);

	receive_file(client, mtu, config);

	// ukoncit spojeni
	close(client);
}

void accept_sco_server(int s, bt_intfr_config * config)
{
	struct sockaddr_sco loc_addr1 = { 0 }, rem_addr1 = { 0 };
    	int client, bytes_read;
	int mtu = 64;
    	char bdaddr_str [20];
	socklen_t remaddrlen = sizeof(rem_addr1);
	
	// prepnuti socketu do rezimu poslouchani
	listen(s, 1);

	// prijeti jednoho spojeni
	client = accept(s, (struct sockaddr *)&rem_addr1, &remaddrlen);

	printf("accept returned %d\n", client);
	if (client > 0) {
		ba2str( &rem_addr1.sco_bdaddr, bdaddr_str);
		fprintf(stderr, "Accepted sco connection from %s\n", bdaddr_str);

		receive_file(client, mtu, config);

		// ukoncit spojeni
		close(client);
	} else {
		perror("Server: Accept failed");
	}
}



void run_server(bt_intfr_config * config)
{
    	int s;

	printf("Running the server\n");

	printf("Protocol = %s\n", protocol_to_string(config->protocol));
	if (config->protocol == L2CAP) {
		printf("Mode = %s\n", mode_to_string(config->mode));
	}

    	// prideleni socketu
 	
	s = create_bound_socket(config);
	
	switch(config->protocol) {

		case L2CAP:
		accept_l2cap_server(s, config);
		break;

		case SCO:
		accept_sco_server(s, config);
			break;
		default:
			break;
	}
	close(s);
}
