#include "posix_sockets.h"

char *remote_ip_address;
static int Inet_Type;


//[INET6_ADDRSTRLEN];

int Net_Connect(const char *ip, const char *port){
	//returns -1 for failure, any non-negative for success
	struct addrinfo hints, *res;
	int sockfd;
	int ret_val;

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

	if( (ret_val = getaddrinfo(ip, port, &hints, &res) ) != 0){
		printf("failed to get address info!\n");
		fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(ret_val));
		return -1;
	}

	if( ( sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol) ) == -1){
		printf("failed to create socket!\n");
		fprintf(stderr, "socket(): %s\n", gai_strerror(errno));
		return -1;
	}

	if( (ret_val = connect(sockfd, res->ai_addr, res->ai_addrlen) ) == -1){
		printf("failed to connect!\n");
		fprintf(stderr, "connect(): %s\n", gai_strerror(errno));
		return -1;
	}

	freeaddrinfo(res);

	return sockfd;
}

int Net_Listen(const char *port){
	//returns -1 for failure, any non-negative for success
	struct addrinfo hints, *res;
	//void *addr;
	int sockfd;
	int ret_val;

	(void)memset(&hints, '\0', sizeof hints);
	hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE;     // fill in my IP for me
	
	if( (ret_val = getaddrinfo(NULL, port, &hints, &res) ) != 0){
		fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(ret_val));
		return -1;
	}

	if( ( sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol) ) == -1){
		printf("failed to make a socket!\n");
		fprintf(stderr, "socket(): %s\n", gai_strerror(errno));
		return -1;
	}

	if( bind(sockfd, res->ai_addr, res->ai_addrlen) == -1){
		printf("failed to bind to port!\n");
		fprintf(stderr, "bind(): %s\n", gai_strerror(errno));
		return -1;
	}

	if( (ret_val = listen(sockfd, 0) ) == -1){
		printf("failed to listen on socket!\n");
		fprintf(stderr, "listen(): %s\n", gai_strerror(errno));
		return -1;
	}

	//set global my_ip_address
	//p = res;

	//for(p = res;p != NULL; p = p->ai_next) {
		//void *addr;
		//char *ipver;
		//if (hints->ai_family == AF_INET) { // IPv4
//			struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
//			addr = &(ipv4->sin_addr);
			//ipver = "IPv4";
		//} else { // IPv6
		//	struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)hints->ai_addr;
		//	addr = &(hints->sin6_addr);
			//ipver = "IPv6";
		//}
	//}
        // convert the IP to a string and print it:

//printf("inet6_addrstrlen: %u\n", INET6_ADDRSTRLEN);

	//my_ip_address = (char *)malloc(INET6_ADDRSTRLEN);

//printf("here 1\n");

//        if(inet_ntop((int)res->ai_family, (const void *)addr, (char *)my_ip_address, (socklen_t)INET6_ADDRSTRLEN) == NULL){
//		printf("failed to get ip address\n");
//		fprintf(stderr, "inet_ntop(): %s\n", gai_strerror(errno));
//		return -1;
//	}
	//(void)strcpy(my_ip_address, (const char *)
	
//printf("here 2\n");

	//if(res->ai_family == AF_INET) {
	//	is_ipv6=0;
	//}else{
	//	is_ipv6=1;
	//}

	Inet_Type = res->ai_family;

	freeaddrinfo(res);	
	return sockfd;
}


void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}


int Grab_Remote_IP(int sockfd, struct sockaddr *addr){
	//returns 0 for success, non-zero for failure

	static int already_here = 0;
	//if(already_here)
	//	return 0;

	struct hostent *temp_hostent;
	//struct sockaddr addr;
	socklen_t addrlen = sizeof(struct sockaddr);

	if(!already_here){
		remote_ip_address = (char *)malloc(INET6_ADDRSTRLEN);
		(void)memset(remote_ip_address, '\0', INET6_ADDRSTRLEN);
	}


        inet_ntop(addr->sa_family,
            get_in_addr(addr),
            remote_ip_address, INET6_ADDRSTRLEN);
        printf("server: got connection from %s\n", remote_ip_address);



/*


	if( getpeername(sockfd, addr, &addrlen) == -1){
		printf("getpeername error\n");
		fprintf(stderr, "getpeername(): %s\n", strerror(errno));
		return 1;
	}
	
printf("here 3\n");	
	if( (temp_hostent = gethostbyaddr((const void *)&addr, addrlen, Inet_Type)) == NULL ){
		printf("gethostbyaddr failed!\n");
		fprintf(stderr, "gethostbyaddr(): %s\n", hstrerror(h_errno));
		return 1;
	}
printf("here 4\n");
printf("remote ip: %s\n", temp_hostent->h_addr);
	
	(void)strncpy(remote_ip_address, temp_hostent->h_addr, temp_hostent->h_length);
//printf("remote ip: %s\n", remote_ip_address);
*/
	already_here = 1;
	return 0;
}

int Send_Message(int sockfd, const char *msg, int len){
	//return 0 for success, non-zero for error
	unsigned int bytes_sent = 0;

	if( ( bytes_sent = send(sockfd, msg, len, 0) ) == -1){
		printf("failed to send data!\n");
		fprintf(stderr, "send(): %s\n", gai_strerror(errno));
		return 2;
	}

	if(bytes_sent == 0){
		printf("send(): remote client closed connection!\n");
		return 1;
	}else{
		printf("send(): %u bytes sent\n", bytes_sent);
		printf("send() message: %s\n", (char *)msg);
		return 0;
	}

}

int Recv_Message(int sockfd, const char *msg, int len){
	//return 0 for success, non-zero for error
	struct sockaddr_storage their_addr;
	//struct sockaddr *for_lookup;
	socklen_t addr_size = sizeof(their_addr);
	int new_fd;
	int flags = 0;
	unsigned int bytes_recv = 0;
	unsigned int bytes_recv_total = 0;

	//return 0 for success, non-zero for error
	if( (new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size) ) == -1){
		printf("failed to accept connection!\n");
		fprintf(stderr, "accept(): %s\n", gai_strerror(errno));
		return 3;
	}

	if(Grab_Remote_IP(sockfd, (struct sockaddr *)&their_addr) != 0){
		printf("Grab_Remote_IP failed!\n");
		return 1;
	}
	while( bytes_recv_total < len){
		if( ( bytes_recv = recv(new_fd, (void *)msg, len, flags) ) == -1){
			printf("recv(): failed to recv data!\n");
			return 2;
		}else{
			bytes_recv_total += bytes_recv;
		}

		if(bytes_recv == 0){
			printf("recv(): remote client closed connection!\n");
			return 1;
		}
	}

	//if( getpeername(sockfd, for_lookup, addr_size) != 0){
	//	printf("getpeername(): failed!\n");
	//	fprintf(stderr, "getpeername(): %s\n", gai_strerror(errno));
	//	return 1;
	//}

	//remote_ip = 

	printf("recv(): %u bytes recieved\n", bytes_recv_total);
	printf("recv() message: %s\n", (char *)msg);
	close(new_fd);
	return 0;
}
