#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <assert.h>
#include "trans.h"

// not used anymore
///* for every socket used in v4 code, there is a 'corresponding' v6 socket.. */
//int sockfdv6[sizeof(int)];


/*
 * should be called once before calling any other of the xxxv6-functions.
 */
int initsocketv6 ()
{
	/*int i;
	for (i=0; i<sizeof(int); i++)
		sockfdv6[i] = -1; */
	return 0;
}

int socketv6(int domain, int type, int protocol)
{
	if (domain != AF_INET)
		return socket(domain, type, protocol);

	return socket(AF_INET6, type, protocol);
}
	
/* 
 * input:  binary IPv4 address in network byte order
 * output: binary IPv6 address in network byte order
 */
struct in6_addr v4addr2v6addr (in_addr_t from)
{
	struct in6_addr * retval = (struct in6_addr *) malloc (sizeof(struct in6_addr));
	char * buffer = (char *) malloc (INET6_ADDRSTRLEN);
	retval->s6_addr[0] = 0xfe;
	retval->s6_addr[1] = 0xc0;
	retval->s6_addr[2] = 0;
	retval->s6_addr[3] = 0;
	retval->s6_addr[4] = 0;
	retval->s6_addr[5] = 0;
	retval->s6_addr[6] = 0xff;
	retval->s6_addr[7] = 0xff;
	retval->s6_addr[8] = 0;
	retval->s6_addr[9] = 0;
	retval->s6_addr[10] = 0;
	retval->s6_addr[11] = 0;
	// TODO fix endianness
	retval->s6_addr[12] = *((uint8_t *)&from);
	retval->s6_addr[13] = *((uint8_t *)&from + 1);
	retval->s6_addr[14] = *((uint8_t *)&from + 2);
	retval->s6_addr[15] = *((uint8_t *)&from + 3);
	//retval->s6_addr[12] = 0xd8;
	//retval->s6_addr[13] = 0xef;
	//retval->s6_addr[14] = 0x39;
	//retval->s6_addr[15] = 0x65;
	printf("Address: %s\n", inet_ntop(AF_INET6, retval, buffer, INET6_ADDRSTRLEN));
	return *retval;
}

/*
 * It'd be nice to have the sockaddr_in6 static, to remove the
 * allocation overhead. However, this wouldn't be thread-safe anymore
 * if we did that :(.
 */
int sendtov6 (int sockfd, const void *msg, int len, unsigned int flags,
		               const struct sockaddr *to, int tolen)
{
	struct sockaddr_in6 * tov6;

	if (to->sa_family != AF_INET)
		return sendto(sockfd, msg, len, flags, to, tolen);

	tov6 = (struct sockaddr_in6 *) calloc (1, sizeof(struct sockaddr_in6));

	// this is BSD-specific? (rfc2553 section 3.4)
	// tov6->sin6_len = sizeof(sockaddr_in6);
	
	tov6->sin6_family = AF_INET6;
	tov6->sin6_port = ((struct sockaddr_in *)to)->sin_port;
	tov6->sin6_addr = v4addr2v6addr(((struct sockaddr_in *)to)->sin_addr.s_addr);
	tov6->sin6_flowinfo = 0;
	//tov6->sin6_scope_id = ;
	
	return sendto(sockfd, msg, len, flags, (struct sockaddr *)tov6, sizeof(struct sockaddr_in6));
}

int recvfromv6 (int sockfd, void *msg, int len, unsigned int flags,
		               struct sockaddr *from, socklen_t * fromlen)
{
	struct sockaddr_in6 * fromv6;

	if (from->sa_family != AF_INET)
		return recvfrom (sockfd, msg, len, flags, from, fromlen);

	fromv6 = (struct sockaddr_in6 *) calloc (1, sizeof(struct sockaddr_in6));

	fromv6->sin6_family = AF_INET6;
	fromv6->sin6_port = ((struct sockaddr_in *)from)->sin_port;
	fromv6->sin6_addr = v4addr2v6addr(((struct sockaddr_in *)from)->sin_addr.s_addr);
	fromv6->sin6_flowinfo = 0;
	//fromv6->sin6_scope_id = ;
	
	// TODO fromv6 might change or something??
	return recvfrom(sockfd, msg, len, flags, (struct sockaddr *)fromv6, fromlen); 
}

