#pragma once

#include <features.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdint.h>
#include <netinet/in.h>    //defines struct in_addr
#include <arpa/inet.h>

enum tfe_conn_dir
{
	CONN_DIR_DOWNSTREAM = 0,    //From client to proxy, aka client-side.
	CONN_DIR_UPSTREAM        //From proxy to server, aka server-side.
};

/* network-order */
struct tfe_stream_addr_tuple4_v4
{
	struct in_addr saddr;    /* network order */
	struct in_addr daddr;    /* network order */
	in_port_t source;    /* network order */
	in_port_t dest;        /* network order */
};

#ifndef IPV6_ADDR_LEN
#define IPV6_ADDR_LEN    (sizeof(struct in6_addr))
#endif

struct tfe_stream_addr_tuple4_v6
{
	struct in6_addr saddr;
	struct in6_addr daddr;
	in_port_t source;        /* network order */
	in_port_t dest;        /* network order */
};

#define GRE_TAG_LEN        (4)
struct tfe_stream_addr_gre
{
	uint16_t gre_id;
};

#define VLAN_ID_MASK        (0x0FFF)
#define VLAN_TAG_LEN        (4)
struct tfe_stream_addr_vlan
{
	uint16_t vlan_id;    /* network order */
};

struct tfe_stream_addr_pppoe_session
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
	unsigned int ver:4;
	unsigned int type:4;
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
    unsigned int type:4;
    unsigned int ver:4;
#endif
	unsigned char code;
	unsigned short session_id;
};

#ifndef MAC_ADDR_LEN
#define MAC_ADDR_LEN        (6)
#endif

struct tfe_stream_addr_mac
{
	uint8_t src_mac[MAC_ADDR_LEN]; /* network order */
	uint8_t dst_mac[MAC_ADDR_LEN]; /* network order */
};

struct tfe_stream_addr_ipv4
{
	struct in_addr saddr;    /* network order */
	struct in_addr daddr;    /* network order */
	in_port_t source;            /* network order */
	in_port_t dest;            /* network order */
};

struct tfe_stream_addr_ipv6
{
	struct in6_addr saddr; /* network order */
	struct in6_addr daddr; /* network order */
	in_port_t source;/* network order */
	in_port_t dest;/* network order */
};

struct tfe_stream_addr_tcp
{
	in_port_t source; /* network order */
	in_port_t dest;    /* network order */
};

struct tfe_stream_addr_udp
{
	in_port_t source; /* network order */
	in_port_t dest;    /* network order */
};

struct tfe_stream_addr_l2tp
{
	uint32_t tunnelid; /* network order */
	uint32_t sessionid; /* network order */
};

enum tfe_stream_addr_type
{
	TFE_ADDR_INIT = 0,
	/* 1, 基于IPv4地址的四元组信息 */
	TFE_ADDR_IPV4,
	/* 2, 基于IPv6地址的四元组信息 */
	TFE_ADDR_IPV6,
	TFE_ADDR_VLAN,
	TFE_ADDR_MAC,
	TFE_ADDR_ARP = 5,
	TFE_ADDR_GRE,
	TFE_ADDR_MPLS,
	TFE_ADDR_PPPOE_SES,
	TFE_ADDR_TCP,
	TFE_ADDR_UDP = 10,
	TFE_ADDR_L2TP,
	/* 12, 混合地址类型, 基于IPv4地址的四元组信息 */
	TFE_ADDR_STREAM_TUPLE4_V4,
	/* 13, 混合地址类型, 基于IPv6地址的四元组信息 */
	TFE_ADDR_STREAM_TUPLE4_V6,
};

struct tfe_stream_addr
{
	enum tfe_stream_addr_type addrtype;
	uint8_t addrlen; /* 地址结构长度 */

	union
	{
		struct tfe_stream_addr_tuple4_v4 tuple4_v4[0];
		struct tfe_stream_addr_tuple4_v6 tuple4_v6[0];
		struct tfe_stream_addr_ipv4 ipv4[0];
		struct tfe_stream_addr_ipv6 ipv6[0];
		struct tfe_stream_addr_vlan vlan[0];
		struct tfe_stream_addr_mac mac[0];
		struct tfe_stream_addr_gre gre[0];
		struct tfe_stream_addr_tcp tcp[0];
		struct tfe_stream_addr_udp udp[0];
		struct tfe_stream_addr_pppoe_session pppoe_ses[0];
		struct tfe_stream_addr_l2tp l2tp[0];
		unsigned char paddr[0];
	};
};
static inline const char* tfe_stream_conn_dir_to_str(enum tfe_conn_dir dir)
{
	return (dir==CONN_DIR_DOWNSTREAM)?"downstream":"upstream";
}
static inline char * tfe_stream_addr_to_str(const struct tfe_stream_addr * addr)
{
	char * __str_ret = NULL;
	if (addr->addrtype == TFE_ADDR_STREAM_TUPLE4_V4)
	{
		const struct tfe_stream_addr_tuple4_v4 * tuple4_v4 = addr->tuple4_v4;
		char __src_addr[INET_ADDRSTRLEN];
		char __dst_addr[INET_ADDRSTRLEN];
		uint16_t __src_port = ntohs((uint16_t) tuple4_v4->source);
		uint16_t __dst_port = ntohs((uint16_t) tuple4_v4->dest);

		inet_ntop(AF_INET, &tuple4_v4->saddr, __src_addr, sizeof(__src_addr));
		inet_ntop(AF_INET, &tuple4_v4->daddr, __dst_addr, sizeof(__dst_addr));
		asprintf(&__str_ret, "%s %u %s %u", __src_addr, __src_port, __dst_addr, __dst_port);
	}

	if(addr->addrtype == TFE_ADDR_STREAM_TUPLE4_V6)
	{
		const struct tfe_stream_addr_tuple4_v6 * tuple4_v6 = addr->tuple4_v6;
		char __src_addr[INET6_ADDRSTRLEN];
		char __dst_addr[INET6_ADDRSTRLEN];
		uint16_t __src_port = ntohs((uint16_t) tuple4_v6->source);
		uint16_t __dst_port = ntohs((uint16_t) tuple4_v6->dest);

		inet_ntop(AF_INET6, &tuple4_v6->saddr, __src_addr, sizeof(__src_addr));
		inet_ntop(AF_INET6, &tuple4_v6->daddr, __dst_addr, sizeof(__dst_addr));
		asprintf(&__str_ret, "%s %u %s %u", __src_addr, __src_port, __dst_addr, __dst_port);
	}

	return __str_ret;
}
static inline void tfe_stream_addr_free(struct tfe_stream_addr *addr)
{
	free(addr);
	return;
}
static inline struct tfe_stream_addr * tfe_stream_addr_create_by_fd(int fd, enum tfe_conn_dir dir)
{
	struct tfe_stream_addr * __stream_addr = NULL;

	struct sockaddr_storage sk_src_storage{};
	struct sockaddr * sk_src_ptr = (struct sockaddr *) &sk_src_storage;
	socklen_t sk_src_len = sizeof(sk_src_storage);

	struct sockaddr_storage sk_dst_storage{};
	struct sockaddr * sk_dst_ptr = (struct sockaddr *) &sk_dst_storage;
	socklen_t sk_dst_len = sizeof(sk_dst_storage);
	if(dir==CONN_DIR_UPSTREAM)
	{
		int ret = getsockname(fd, sk_src_ptr, &sk_src_len);
		if (ret < 0)
		{
			goto __errout;
		}

		ret = getpeername(fd, sk_dst_ptr, &sk_dst_len);
		if (ret < 0)
		{
			goto __errout;
		}
	}
	else
	{
		int ret = getsockname(fd, sk_dst_ptr, &sk_dst_len);
		if (ret < 0)
		{
			goto __errout;
		}
		
		ret = getpeername(fd, sk_src_ptr, &sk_src_len);
		if (ret < 0)
		{
			goto __errout;
		}
	}

	assert(sk_src_ptr->sa_family == sk_dst_ptr->sa_family);
	if (sk_src_ptr->sa_family == AF_INET)
	{
		__stream_addr = (struct tfe_stream_addr *) malloc(
			sizeof(struct tfe_stream_addr) + sizeof(struct tfe_stream_addr_tuple4_v4));

		struct tfe_stream_addr_ipv4 * st_addr_v4 = __stream_addr->ipv4;
		struct sockaddr_in * sk_v4_src_ptr = (struct sockaddr_in *) sk_src_ptr;
		struct sockaddr_in * sk_v4_dst_ptr = (struct sockaddr_in *) sk_dst_ptr;

		__stream_addr->addrtype = TFE_ADDR_STREAM_TUPLE4_V4;
		__stream_addr->addrlen = sizeof(struct tfe_stream_addr_tuple4_v4);

		st_addr_v4->saddr.s_addr = sk_v4_src_ptr->sin_addr.s_addr;
		st_addr_v4->source = sk_v4_src_ptr->sin_port;
		st_addr_v4->daddr.s_addr = sk_v4_dst_ptr->sin_addr.s_addr;
		st_addr_v4->dest = sk_v4_dst_ptr->sin_port;
	}
	else if (sk_src_ptr->sa_family == AF_INET6)
	{
		assert(0);
	}
	else
	{
		goto __errout;
	}

	return __stream_addr;

__errout:
	if (__stream_addr != NULL) free(__stream_addr);
	return NULL;
}

