#ifndef cSocket_h
#define cSocket_h
#include "cygwin.h"	// here only for "struct in_addr"

#ifndef _WIN32
#define INVALID_SOCKET  (-1)
#define SOCKET		int
#endif

extern size_t	 dlSecond;
extern size_t	 ulSecond;
extern size_t	 global_ul_count;
extern size_t	 global_dl_count;
extern int	 ul_left;
extern int	 dl_left;
//extern struct in_addr	 my_ip;
extern char	*socket_db;

typedef enum {
	tNone   	=   0,
	tSocket		=   4,
	tSource 	=   8,
	tServer 	=  16,
	tGui		=  32,
	tCommand	=  64,
	tKademlia 	= 128,
	tServerUDP	= 256
	} listType;

class cSocket;


template <class T> class cDLL {
private:
	cDLL<T>	*prev;
	cDLL<T>	*next;
	T		*data;
private:		 cDLL (cDLL<T> *after, T *neu);
public: 		 cDLL () { prev = next = this; data = NULL; }
			~cDLL ();
	cDLL<T>	*add_front	(T *neu);
	cDLL<T>	*add_back	(T *neu);
	bool		 empty		(void) { return next == this; }
	T 		*peek		(void) { return next->data; }
static	void		 remove		(cDLL<T> **old);
	};

template <class T>      cDLL<T>::cDLL(cDLL<T> *after, T *neu) {{{
    this->next = after->next;
    this->prev = after;
    this->prev->next = this;
    this->next->prev = this;
    this->data = neu;
}}}
template <class T>      cDLL<T>::~cDLL() {{{
    	prev->next = next;
    	next->prev = prev;
    	next = NULL;
    	prev = NULL;
    	data = NULL;
	}}}
template <class T>      cDLL<T> *cDLL<T>::add_front(T *neu) {{{
    	return new cDLL<T>(this, neu);
	}}}
template <class T>      cDLL<T> *cDLL<T>::add_back(T *neu) {{{
    	return new cDLL<T>(this->prev, neu);
	}}}
template <class T> void cDLL<T>::remove(cDLL<T> **old) {{{
    	if (*old == NULL) return;
    	delete *old;
    	(*old) = NULL;
	}}}

typedef cDLL<cSocket> cSocketList;

extern cSocketList timer_list;

class cSocket {
	private:
	SOCKET		 fd_new;	// Socket on new connection ( cSocket(cSocket *) NOT cSocket(int *fd) )
	cSocketList	*event;
	int		 last_err;

static	struct in_addr	 local_internal_ip; // local intranet IP address
static	bool		 local_internal_ip_forced; // using a forced address?
static	struct in_addr	 local_external_ip; // local internet-visible IP
static	bool		 local_external_ip_forced; // using a forced address?
static	int		 local_external_ip_repeats; // repeat count before believe

	public:
	SOCKET		 fd;		// Socket handle (My_IP is missing)
	cSocketList 	*timer;
	time_t		 work_last;
	public:
static	size_t		 count;
	listType	 Type;
	size_t    	 dl_count;
	size_t    	 ul_count;
	unsigned	 mask;		// Mask wat to do
inline	bool		 validSocket (void) const { return (fd != INVALID_SOCKET) && ((mask & 16) != 16);}
	int		 prio;		// 0=Senden ohne account als erstes
					// 1=Normal 
					// rest kann definiert werden
	int		 type;		// UDP/TCP
	int		 connected;	// TCP established
	int		 ready_send;	// send buffer is not full
	struct in_addr 	 this_ip;	// The local  ip
	struct in_addr	 peer_ip;	// the client ip
	unsigned short	 this_port;	// The local  port
	unsigned short	 peer_port;	// The client port
	time_t		 since;		// Time the socket was created
	time_t		 last_conn;	// Time since socket is connected
	time_t		 last_send;	// Time i recived last message
	time_t		 last_read;	// Time i send last message
	unsigned char	*send_buf;
	size_t           send_buf_length;
	size_t		 send_len;
	unsigned char	*read_buf;
	size_t           read_buf_length;
	size_t		 read_len;
	void             (* destr_callback)(cSocket *);
			 cSocket (class cSocket *new_fd);
			 cSocket (unsigned short port, int type);
			 cSocket (unsigned int   ip  , uint16_t port, int type);
virtual			~cSocket ();
	bool		 Write 	 (const unsigned char *buf, size_t len);	// socket is ok
	size_t  	 Read	 (      unsigned char *buf, size_t len);
virtual	void		 Work		(void) = 0;
	bool		 doInternal	(int event);
	void		 Close		(void);
static	const char*	 Strerror	(int err);
private:void		 printError	(int err);
	void		 doUnblock	(void);
	void		 doBlock	(void);
	void		 Init0		(void);
	void		 doAccept	(void);
public:	void		 doRead  	(void);
	void		 doWork  	(int event);
virtual bool		 canWork	(void) { return false; }
virtual void		 readWork	(void) { }
virtual	int   	 	 doRead_high		(void) = 0;
	void		 doSend		(void);

// inline these two?
static	bool		 isPrivateIP(struct in_addr addr);
static	bool		 isInternalIP(struct in_addr addr);
static	void		 setLocalInternalIP(struct in_addr new_addr);
static	void		 setLocalInternalIP(struct in_addr new_addr, bool forcing);
static	void		 setLocalExternalIP(struct in_addr new_addr);
static	void		 setLocalExternalIP(struct in_addr new_addr, bool forcing);
static	void		 setLocalExternalIPRepeatCount(int repeats);
static	struct in_addr	 getLocalInternalIP();
static	struct in_addr	 getLocalExternalIP();
static	struct in_addr	 hostIP(const char *bind_to); 

	bool		 guessSocketLocalIP(void);
	void		 resetAccept	(void);	// close an aktive fd_new
	void		 addEvent	(int msk);
	void             setDestructorCallback(void (*callback)(cSocket *));
static	void 		 hdlEvent  	(void);
static	void		 Start		(void);
static	void		 Stop		(void); 
static  void		 waitEvent	(int usec);
	};


const char *txtType (const cSocket *list);

off_t FD_LSEEK  (int fd, off_t offset, int whence);
bool  FD_READ2  (int fd, unsigned char *buf, size_t len);
void  FD_WRITE2 (int fd, const unsigned char *buf, size_t len);
unsigned char *file (const char *name, size_t *ret_size);

struct sBuffer {
	size_t		 LEN,len;
	unsigned char	*BUF,*buf;
	};

#define	temp_buffer(lbl,size)	struct sBuffer lbl;\
				lbl.LEN = lbl.len = size;	\
				lbl.BUF = lbl.buf = reinterpret_cast<unsigned char*>(alloca(lbl.LEN));

#endif

