#ifndef NET_SOCK_H
#define NET_SOCK_H

///@name sock
//@{

#include "misc/compat.h"
#ifdef _WINDOZE_
	#include <winsock.h>
	#define SOCKETBUFFERCAST (char *)
#else
	#include <sys/types.h>
	#include <sys/time.h>
	#include <unistd.h>
	#include <sys/socket.h>
	#include <sys/select.h>
	typedef int SOCKET;
	#include <netinet/in.h>
	#include <netdb.h>
	#include <arpa/inet.h>
	#include <sys/types.h>
	#define closesocket(s) close(s)
	#define SOCKETBUFFERCAST
#endif

#ifndef SOCKET_ERROR
	#define SOCKET_ERROR -1
#endif

#include "base/dblock.h"
#include "net/noderef.h"
#include "pipe/pipe.h"

#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#define INADDR_ANY ((in_addr_t) 0x00000000)
#endif

#define INADDR_NONE_CHAR {0xff, 0xff, 0xff, 0xff} //there is probably a better way to do this
#define INADDR_ANY_CHAR {0x00, 0x00, 0x00, 0x00} //there is probably a better way to do this

#ifdef _WINDOZE_
	#define BLANKSOCKADDR_IN {AF_INET, 0, INADDR_NONE_CHAR, {0, 0, 0, 0,  0, 0, 0, 0}}
	#define LOCALSOCKADDR_IN {AF_INET, 0, INADDR_ANY_CHAR, {0, 0, 0, 0,  0, 0, 0, 0}}
#elif defined(__FreeBSD__) || defined(__MACH__) || defined(__OpenBSD__)
	#define BLANKSOCKADDR_IN {sizeof(struct sockaddr_in), AF_INET, 0, {INADDR_NONE}}
	#define LOCALSOCKADDR_IN {sizeof(struct sockaddr_in), AF_INET, 0, {INADDR_ANY}}
#else
	#define BLANKSOCKADDR_IN {AF_INET, 0, {INADDR_NONE}}
	#define LOCALSOCKADDR_IN {AF_INET, 0, {INADDR_ANY}}
#endif

#define SOCK_MAX_WRITE 256 //todo: lookup the size of smallest packet of data

///socket at last checking is open and ok.
#define SOCK_STATUS_OK		0
///socket at last checking is open and ok.
#define SOCK_STATUS_OPEN	0
///socket is closed but there are still things on the input buffer
#define SOCK_STATUS_CLOSING	1
///socket is closed
#define SOCK_STATUS_CLOSED	2
///socket is new and has never been used yet
#define SOCK_STATUS_NEW		3
///socket is trying to open a connection
#define SOCK_STATUS_OPENING	4
///socket is listening for connections
#define SOCK_STATUS_LISTENING	5

///no error
#define SOCK_ERR_NONE		0
///bad port number
#define SOCK_ERR_BADPORT	-1
///host name lookup failed
#define SOCK_ERR_BADHOST	-2
///noderef bad or NULL
#define SOCK_ERR_BADNODEREF -3
///no sockhandle was given
#define SOCK_ERR_NOSOCKHANDLE -4
///error occurred with the sockets protocol
#define SOCK_ERR_SOCKETERROR -5
///bind function failed
#define SOCK_ERR_BINDFAILED -6
///listen function failed
#define SOCK_ERR_LISTENFAILED -7
///making a connection failed
#define SOCK_ERR_NOCONNECT -8

///socket direction is unknown (new or closed socket)
#define SOCK_DIR_UNKNOWN 0
///socket connection direction is inbound
#define SOCK_DIR_IN -1
///socket connection direction is outbound
#define SOCK_DIR_OUT 1

/**
//todo: describe this
*/
typedef struct SockHandle {
///The pipe of the SockHandle.
	Pipe IOPipe;
	//DataBlock *InBuffer;		//data received from the socket.
	//DataBlock *OutBuffer;		//data to be sent to the socket.
	struct sockaddr_in SockAddress;
	struct sockaddr_in LocalSockAddress;
	SOCKET Socket;
	NodeRef *noderef;
	NodeRef *localnoderef;
	int status;
	int direction;
	int socketerror;			//records the last socket error
	int RegCount;				//How many register users of this socket are there
} SockHandle;

void SocketAtStart(void);

void sockInit(SockHandle *sh);
SockHandle *sockMake(void);
void sockMakeAt(SockHandle **sh);
void sockRegister(SockHandle *sh);
void sockFree(SockHandle *sh);

int sockSetNodeRef(SockHandle *sh, NodeRef *nr);
int sockSetLocalNodeRef(SockHandle *sh, NodeRef *nr);
int sockSetSockAddress(SockHandle *sh, struct sockaddr_in *sain);
int sockSetLocalSockAddress(SockHandle *sh, struct sockaddr_in *sain);

int sockOpen(SockHandle *sh);
int sockClose(SockHandle *sh);
int sockListen(SockHandle *sh);
SockHandle *sockAccept(SockHandle *sh);


void sockPipeAttach(SockHandle *sh, Pipe *newpipe);
void sockPipeDetach(SockHandle *sh);
void sockPipeClose(SockHandle *sh);


DataBlock *socketRead(SOCKET so, DataBlock *db, int *status);
DataBlock *socketWrite(SOCKET so, DataBlock *db, int *status);
void sockRead(SockHandle *sh);
void sockWrite(SockHandle *sh);


int socketGetError(SOCKET so);
int sockRecordError(SockHandle *sh);
void sockPrintError(SockHandle *sh, char *msg);
int nodeRefToSockAddr(NodeRef *nr, struct sockaddr_in *sai);

//@}

#endif //NET_SOCK_H
