#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define MAX_BUFFER_LENGTH 100

int get_socket(int type) {
	return socket(PF_INET, type, IPPROTO_IP);
}

void init_address(struct sockaddr_in* addr, int port) {
	addr->sin_family = AF_INET;     
    addr->sin_port = htons(port);
    addr->sin_addr.s_addr = INADDR_ANY;
    memset(addr->sin_zero, '\0', sizeof addr->sin_zero);
}

void unpack_data(unsigned char *buffer, unsigned int *a, unsigned int *b)
{
    *a = (buffer[0]<<8) | buffer[1];
    *b = (buffer[2]<<8) | buffer[3];
}

int gcd(int a, int b)
{
    if (b == 0) return a;
    return gcd(b,a%b);
}

void print_gcd(unsigned char *buffer) {
	int a, b;
	unpack_data(buffer, &a, &b);
	printf("a=%d, b=%d\n", a, b);
	printf("Result: %d\n", gcd(a,b));
}

int main(int argc, char* argv[]) {
	// i'm in ur args ... 
	if(argc != 3) {
		fprintf(stderr, "Wrong argc, 1st TCP-Port, 2nd UDP-Port\n");
		exit(1);
	}
	
	int tcp_port = atoi(argv[1]);
	int udp_port = atoi(argv[2]);
	int tcp_fd, udp_fd, new_fd, fd_max = -1;
	struct sockaddr_in tcp_addr, udp_addr, tcp_client_addr, udp_client_addr;
	fd_set read_fds, master;
	unsigned char buffer[MAX_BUFFER_LENGTH];
	

	if((tcp_fd = get_socket(SOCK_STREAM)) < 0) {
		fprintf(stderr, "Failed to create TCP Socket!\n");
		exit(1);
	}
	if((udp_fd = get_socket(SOCK_DGRAM)) < 0) {
		fprintf(stderr, "Failed to create UDP Socket!\n");
		exit(1);
	}
	fd_max = udp_fd;
	
	FD_SET(tcp_fd, &master);
    FD_SET(udp_fd, &master);
    fd_max = udp_fd; 
	
	init_address(&tcp_addr, tcp_port);
	init_address(&udp_addr, udp_port);
	
	if(bind(tcp_fd, (struct sockaddr *)&tcp_addr, sizeof(tcp_addr)) != 0) {
		fprintf(stderr, "Could not bind tcp\n");
		exit(1);
	}
	if(bind(udp_fd, (struct sockaddr *)&udp_addr, sizeof(udp_addr)) != 0) {
		fprintf(stderr, "Could not bind udp\n");
		exit(1);
	}
	
	if ((listen(tcp_fd,5) || listen(udp_fd,5)) == -1) {
		fprintf(stderr, "Listen failed\n");
		exit(1);
	}
	
	printf("Running and ready!\n");
	
	while(1){
        read_fds = master;
        //ACCEPTING
        select(fd_max + 1, &read_fds, NULL, NULL, NULL);
        int i;
        for (i = 0; i <= fd_max; i++){
            if (FD_ISSET(i, &read_fds)){
            	printf("Accepting ...\n");
                if (i == tcp_fd) {
                	printf("TCP...\n");
                	int tcp_client_length = sizeof(tcp_client_addr);
					new_fd = accept(tcp_fd, (struct sockaddr *) &tcp_client_addr, &tcp_client_length);
					FD_SET(new_fd, &master);
					if (new_fd > fd_max) fd_max = new_fd;
					read(new_fd,buffer,MAX_BUFFER_LENGTH);
					print_gcd(buffer);
					FD_CLR(new_fd, &master);
                }
                else if (i == udp_fd) {
                  	printf("UDP...\n");
                	int udp_client_addr_len = sizeof(udp_client_addr);
				    recvfrom(udp_fd,buffer,MAX_BUFFER_LENGTH,0,(struct sockaddr *)&udp_client_addr,&udp_client_addr_len);
					print_gcd(buffer);               
				}
            }
        }
    }
	
	return 0;
}
