
#include "Includes.h"

#ifndef SocketHPP
#define SocketHPP


// #include "4DPluginAPI.h" // needed
// #include "SocketDefines.h" // not needed, called before

// --- Sockets code ---
/**
 * apr tutorial sample code
 * http://dev.ariel-networks.com/apr/
 */

//#include <stdio.h>
//#include <assert.h>


#include <apr.h>
#include <apr_portable.h>
#include <apr_general.h>
#include <apr_file_io.h>
#include <apr_strings.h>
#include <apr_network_io.h>
#include <apr_poll.h>
#include <apr_errno.h>
#include <apr_version.h>
#include <apr_shm.h>

#include <string>
#include <vector>
using namespace std;


// tec tcp defines START
// #define MAXSOCKETS 101
/* default buffer size */
#define DEFBUFFSIZE 81660 // was: 4096

#define K_SOCKETCLOSED -1
#define K_SOCKETIDNOTVALID -10
#define K_SOCKETNOTCREATED -2
#define K_NOMORESLOTS -3
#define K_NOBUFFERPROVIDED -4
#define K_WRONGSOCKETTYPE -5

#define LISTENERSOCKET 0
#define ACCEPTSOCKET 1


/* default listen port number */
#define DEF_LISTEN_PORT	80

/* default socket backlog number. SOMAXCONN is a system default value */
#define DEF_SOCKET_BACKLOG	SOMAXCONN

/** number of microseconds per second */
// #define APR_USEC_PER_SEC 1000000

/* default socket timeout */
// #define APR_USEC_PER_SEC 1000000
#define APR_DEF_POLL_TIMEOUT 0 //APR_DEF_POLL_TIMEOUT (APR_USEC_PER_SEC * 1)  //PM 2008-01-05, was: (APR_USEC_PER_SEC * 30) 
// APR_USEC_PER_SEC = microseoncs / second
// (APR_USEC_PER_SEC * 1) = 1 second
// (APR_USEC_PER_SEC * 30) = 30 seconds
							
// #define APR_USEC_PER_SEC 1000000
#define		 DEF_SOCK_TIMEOUT_MS	0 // 1 = 0,001 sec		// 500 = 0,5 seconds // 2010-03-10/PM: 1 -> 10 ms
#define		 DEF_LISTEN_SOCK_TIMEOUT_MICROSECONDS 0  // for blocking server
#define		 DEF_SOCK_TIMEOUT_OPEN_MS	3000 // 3000 = 3 seconds
// DEF_SOCK_TIMEOUT_MS = 1/1000 sec =0.001 seconds

/* useful macro */
#define CRLF_STR		"\r\n"

#define FIRST_POLLSET_ID            10001 // starting id number for pollset

#define FIRST_IN_CONNECTION_ID      100000001 // starting id number for incoming socets
#define LAST_IN_CONNECTION_ID       200000000 // ending id number (actually num-1) for incoming socets

#define FIRST_SERVER_CONNECTION_ID  200000001 // starting id number for server socets
#define LAST_SERVER_CONNECTION_ID   300000000 // ending id number (actually num-1) for server socets

#define FIRST_OUT_CONNECTION_ID     300000001 // starting id number for outgoing socets
#define LAST_OUT_CONNECTION_ID      400000000 // ending id number (actually num-1) for outgoing socets

// tec tcp defines END


typedef enum socket_status_enum { 
	/*   NTK:
			 TCP Connection Closed (0)
			 TCP Connection Established (8)
			 TCP Listening (2) 

				The "TCP Listening" state only applies to a server socket. 
				The state only changes to "closed" if:
				You close the connection.    
				The remote end closes the connection and 
					all incoming data has been read from the buffer. 
	 */
	SOCKET_STATUS_DISCONNECTED = -1,
	SOCKET_STATUS_CLOSED = 0, // socket is idle, ready to start new connection
	SOCKET_STATUS_INIT, // socket is busy, establishing a new connection
	SOCKET_STATUS_READ, // socket is busy, reading data
	SOCKET_STATUS_WRITE,  // socket is busy, writing data
	SOCKET_STATUS_LISTEN, // socket is listening for new connections
	SOCKET_STATUS_OPEN, // socket is open (server socket out or normal out)
	SOCKET_STATUS_NEEDS_CLOSE,
	SOCKET_STATUS_CONN_ABORTED
} socket_status_t;

typedef enum out_socket_type_enum {
	SOCKET_TYPE_UNKNOWN = -1, 
	SOCKET_TYPE_SERVER_LISTENER = 1, 
	SOCKET_TYPE_SERVER_ANSWERER,
	SOCKET_TYPE_CLIENT_OUT
} socket_type_t;

class TSocket {
private:
  apr_size_t sendBufferSize;
  

protected:
	long	connectionId;
  

	apr_pool_t					*apr_pool;
	apr_socket_t				*apr_socket;
	apr_sockaddr_t			*apr_socket_address;
	int                 apr_socket_protocol;

	apr_status_t				apr_status;	// usually error codes from apr calls
	socket_status_t			apr_socket_status;

	apr_int32_t					apr_socket_timeout;
	apr_int32_t					apr_socket_timeout_ms;
	
	wstring							lastErrorText;

	long SocketStatusSet();
	long ParseOption( wstring options, wstring optionName );

public:
	
  char *readBlob;
  int   readBufferInUse;
  apr_size_t receiveBufferSize;
  apr_size_t readBlobStartPos;
  apr_size_t readBlobDataLen;
  
	TSocket();
  virtual ~TSocket();
	
	void SetErrorText(apr_status_t status);
  void SocketSet( apr_socket_t *new_opened_socket );
  long MoveReadBlobDataToReadBlobStart( void );
	apr_status_t SocketOptionsSet( wstring options, int callType, apr_int32_t timeout );
	void SetStatus( socket_status_t new_status );
	long ReceiveBytes( apr_size_t maxBytesToRead );
	long SendBytes( const char *data, apr_size_t *bytestosend );
	void SetTimeout( long timeoutIn);
	long Connect( wstring address, long port, wstring options );
  void Close();
	void SetConnectionId( long id );

	long GetTimeout();
	socket_status_t		TestStatus();
  
	inline long GetConnectionId() {
		return connectionId; 
	}
	inline socket_status_t GetStatus(){	
    return apr_socket_status;
  }
	inline apr_socket_t *GetAprSocket() {
		return apr_socket; 
	}
  
  inline apr_interval_time_t DefaultTimeout(){	
   return apr_socket_timeout;
  }
  
	inline apr_size_t SendBufferSize() {
		return sendBufferSize; 
	}	
  inline wstring GetErrorText() {
		return lastErrorText; 
	}

  /*
	inline apr_size_t GetAddBufferSize() { 
		return apr_addBufferSize; 
	}
	
	inline apr_pool_t *GetAprPool() { 
		return apr_pool; 
	}
	*/
	
};

class TListenSocket : public TSocket {
private:
	apr_int32_t		apr_listen_socket_timeout;

protected:
public:
	TListenSocket();
	virtual ~TListenSocket();
    
	void	Listen( wstring address, long port, wstring options );
};

/*
class TServerSocket : public TSocket {
private:
protected:
public:
	TServerSocket();
	~TServerSocket();

	TSocket* Serve(TListenSocket *listen_socket);
};
*/


class TServer {
private:
  wstring  address;
	int     port;
  wstring  options;
  
	int     connections;
	// wstring  lastError;  

	apr_pool_t			*server_pool;
	apr_socket_t		*server_incoming_socket;
	/*
	apr_pollset_t *apr_pollset;
	const apr_pollfd_t *apr_pollfd;
	*/

	TListenSocket		*listen_socket;
	
	// TServerSocket *server_sockets;

protected:
	// void TryNewConnection(); // for pollset only

public:
	TServer (wstring address, int port, wstring options);
	virtual ~TServer();

	apr_socket_t*		NewIncomingSocket(); // returns server_incoming_socket if something is coming
	socket_status_t GetListenStatus();
	
  inline TSocket* GetListenSocket() {
    return listen_socket;
  }

	inline apr_socket_t* GetAprSocket() {
		//GetListenSocket
		return listen_socket->GetAprSocket(); 
	}
    
	inline long ConnectionCount()
	{ 
		return connections;
	};
  
	inline wstring Options()
	{ 
		return options;
	};
  
	/*
	TServerSocket* GetServerSocketAtIndex( long idx )
	{ 
		if(( idx >= 0 ) && ( idx < connections ))
			return &server_sockets[idx];
		else 
			return NULL;
	};
	*/

	/*
	class TTcp; // forward declaration
	void RemoveConnections( TTcp *tcp);
	*/
};



#endif // SocketHPP
