#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <netdb.h>


#define BACKLOG 10

int server_socket;
int client_socket;


struct sockaddr_in my_adres;
struct sockaddr_in client_adres;
char *host=NULL;

void sigchld_handler(int s)
{
    while(wait(NULL) > 0);
}

int sendall(int s, char *buf, int *len)
{
	int total=0;
	int pozostalo = *len;
	int n;
	
	while(total < *len)
	{
		n=send(s,buf+total,pozostalo,0);
		if(n==-1) break;
		total += n;
		pozostalo -= n;
	}
	*len=total;
	return n==-1?-1:0;
}

char * gethost(char * data)
{
	char *wyst=NULL;
	char *end=NULL;

	int len=0;
	wyst=strstr(data,"Host:");	//przechodze do Host
	wyst=strchr(wyst,' ');		//Przechodze do spacji
	len=strcspn((char *)wyst+1,"\r\n");	//sprawdzam długość

	host=(char *)malloc((len*sizeof(char))+1);


	if(host==NULL)
	{
		printf("%s","Za mało pamięci");
		exit(1);
	}
	strncpy(host,wyst+1,len);
	host[len]='\0';		//na wszeli wypdaek może nie być
	
	return host;
	//printf("Wydłubałem:%s\n długie na %d\n",host,len);
}

int connect_to_server(const char *name, int * s)
{
	int www_socket;
	struct hostent *he;
	struct sockaddr_in www_server_adres;
	int res_con=-1;
	int mypid=getpid();
	he=(struct hostent *)gethostbyname(name);

	if(he == NULL)
	{
		printf("PID:%d:%s\n",mypid,"gethostbyname");
		return -1;
	}
	

	www_socket=socket(AF_INET,SOCK_STREAM,0);
	
	if(www_socket==-1)
	{
		printf("%s\n","Socket error");
		return -1;
	}

	www_server_adres.sin_family=AF_INET;
	www_server_adres.sin_port=htons(80);
	www_server_adres.sin_addr=*((struct in_addr *)he->h_addr);
	
	memset(&(www_server_adres.sin_zero),'\0',8);

	res_con=connect(www_socket,(struct sockaddr *)&www_server_adres, sizeof(struct sockaddr));

	if(res_con==-1)
	{
		printf("PID:%d:%s\n",mypid,"Connect error");
		return -1;
	}
	printf("PID:%d:%s",mypid,"Połączono z: ");
	printf(" %s ",inet_ntoa(*((struct in_addr *)he->h_addr)));
	printf(" %s\n",he->h_name);

	*s=www_socket;

	return 0;
}


void serve(int client_socket)
{
	char buffer[5000];	//zapytanie
	int rec_res=-1;
	int len;
	int sen_res=-1;
	int mypid=getpid();

	rec_res=recv(client_socket,buffer,sizeof(buffer),0);

	if(rec_res==-1)
	{
		printf("PID:%d:%s\n",mypid,"Błąd odczytu z przeglądarki");
		exit(1);
	}
	if(rec_res==0)
	{
		printf("PID:%d:%s\n",mypid,"Połączenie zamknięte");
		exit(1);
	}	
	//buffer[rec_res]=0;
	printf("PID:%d:Odebrałem: %d bajtów zapytania\n",mypid,rec_res);


	int www_serv=0;
	if(connect_to_server(gethost(buffer), &www_serv)==-1)
	{
		printf("PID:%d:%s\n",mypid,"Nie można się połączyć");
		exit(1);
	}
	len=rec_res;
	free(host);
	host=NULL;
	printf("PID:%d:Socket: %d dlugi na %d\n",mypid,www_serv,len);
	sen_res=sendall(www_serv,buffer,&len);

	if(sen_res==-1)
	{
		printf("%s\n","Nie moge wysłać zapytania");
		exit(1);
	}	
	printf("PID:%d:Wysłałem zapytanie %d\n",mypid,sen_res);


	do{
	rec_res=recv(www_serv,buffer,sizeof(buffer),0);

		if(rec_res==-1)
		{
			printf("%s\n","Błąd odczytu z serwera");
			exit(1);
		}
		printf("PID:%d:Odebrałem %d danych\n",mypid,rec_res);
	len=rec_res;
		sendall(client_socket,buffer,&len);
	}while(rec_res!=0);

	printf("PID:%d:%s\n",mypid,"Koniec pobierania");
	close(www_serv);
	//printf("Liczba bajtów %d Dane:\n%s\n",rec_res,buffer);
	//printf("END\n");
}

int create_server(int port)
{
	int bind_res=0;
	int listen_res=0;
	struct sigaction sa;
	server_socket=socket(AF_INET,SOCK_STREAM,0);

	if(server_socket==-1)
		{
			printf("%s","Socket error");
			return -1;
		}


    int yes=1;
    if(setsockopt(server_socket,SOL_SOCKET, SO_REUSEADDR,&yes,sizeof(int))==-1){
	printf("SETSOCKOPT ERROR\n");
    exit(1);
    }

    //printf("%s","socket\n");
    	my_adres.sin_family = AF_INET;
    	my_adres.sin_port = htons(port);
    	my_adres.sin_addr.s_addr = INADDR_ANY;

    	memset(&(my_adres.sin_zero),'\0',8);
	
    	bind_res=bind(server_socket,(struct sockaddr *)&my_adres,sizeof(struct sockaddr));

	if(bind_res==-1)
		{
			printf("%s","Bind error");
			return -1;
		}
//printf("%s","bind\n");
	listen_res=listen(server_socket,BACKLOG);
	
	if(listen_res==-1)
		{
			printf("%s","Listen error");
			return -1;
		}


    sa.sa_handler=sigchld_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags=SA_RESTART;
    if(sigaction(SIGCHLD,&sa,NULL)== -1){
	printf("sigaction\n");
    exit(1);
    }

//printf("%s","listen\n");
	return 0;
}

void incomig_client()
{
	while(1)
	{
		//printf("%s","Loop\n");
		int client_socket=0;
		int sin_size=sizeof(struct sockaddr_in);

		client_socket=accept(server_socket,(struct sockaddr *)&client_adres,&sin_size);
		//printf("%s","accept\n");
		if(client_socket==-1){
			printf("SERVER: %s","Accept error\n");
			continue;
		}
		
		if(!fork()){
			serve(client_socket);
			close(client_socket);
			exit(0);
		}
		else{
			close(client_socket);
		}
		printf("%s %s\n","SERVER: Incoming connection from: ",inet_ntoa(client_adres.sin_addr));
	}
}

void myinit()
{
    close(server_socket);
    close(client_socket);
    
    exit(1);
}



int main(int argc, char *argv[])
{
        signal(SIGTSTP,myinit);
        signal(SIGINT,myinit);




	//printf("%s","Start\n");
  	if(create_server(9084)==0)
	{
		printf("%s\n", "SERVER: Server started");
		incomig_client();
	}
	else
	{
		exit(1);
	}
	
  return EXIT_SUCCESS;
}
