//================================================================
// ENet/AppLink Inter Process Communication library header file
//
// AUTHOR:  Gabor Nagy
// DATE:    2023-Dec-14 17:27:22
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================
#ifndef _ENet_h
#define _ENet_h


#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include <netinet/in.h>

#include <EClass.h>

#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
extern "C" {
#endif

//#ifdef sgi
#define _BSD_SIGNALS
//#endif


// Socket / protocol types
//
enum
{
	EnRAW = 1,
	EnUDP,
	EnTCPIP
};


// How to monitor incoming and outgoing data
//
enum
{
	EnSIGNAL = 1,
	EnTHREAD
};


#define EnMAX_IP_ADDRESS_CHARACTERS  40  // IP address as ASCII up to IPv6 8 uint16s in hexa (4*8) + 7 dots + terminating 0

#define EnIPv4_ADDRESS_WORDS  4
#define EnIPv6_ADDRESS_WORDS  8
#define EnIP_ADDRESS_MAX_WORDS  EnIPv6_ADDRESS_WORDS
typedef uint8_t               EnIPv4AddressWord;
typedef uint16_t              EnIPv6AddressWord;

#define EnIPv4  0x01
#define EnIPv6  0x02


// Define the Token type and Get/Put macros in one place,
// so it's easy to change it later (e.g. to 32 bits)
//
typedef short         EnToken;
#define EM_GetTokenI  EM_GetInt16I_be
#define EM_PutTokenI  EM_PutInt16I_be

#define EnM_PutTimeI(v, p)  { EM_PutInt32I_be((v).Seconds, (p));EM_PutInt32I_be((v).Nanoseconds, (p)); }

typedef struct EnHost_S        EnHost;
typedef struct EnConnection_S  EnConnection;

typedef int  (*EnCallbackFunc)(EnHost* PHost, EnConnection* PConnection, const int PReason);
//typedef void (*EnHostCallbackProc)(EnHost* PHost, EnConnection* PConnection, const int PReason);



// This is an opaque structure, its content will depend on architecture
//
typedef struct
{
	unsigned int  Seconds;
	unsigned int  Nanoseconds;
} EnTime;


#define EnINBUFFER_INCREMENT	1024  // If the InBuffer of a connection is full when a new packet is received, increase the sice by this much

// Basic tokens 
//
enum
{
	EnTkNONE = 0,
	EnTkTIME,     // Absolute time in microseconds (8 bytes)
	EnTkNEXT_FREE
};


// First port # we can use
//
#define EnFIRSTPORT  IPPORT_RESERVED


#define	EnMAX_NUM_OF_CONNECTIONS       64
#define EnMIN_INBUFFER_SIZE         16384
#define EnINBUFFER_SIZE_INCREMENT	  16384
#define EnMAXOUTBLOCKSIZE          524288

#define	EnMAX_RELAYNUM                 16

#define EnMAIN_SHM_KEY         0x42434445
#define EnSHM_KEY              0x42434446
#define EnMAX_APPNUM                   32  // Maximum # of applications on one host that can use AppLink max # of port numbers
#define EnSHMS_OK                       0  // Status flags for the main shared memory accessibility
#define EnSHMS_LOCKED                   1
#define EnSHMS_LOCK_RQ             (1<<1)
#define EnSHMS_ALL                      3


enum { EnDB_PRINT_ALL = 0, EnDB_PRINT_SERVER_PORT_NUMBER };


typedef struct
{
	uint8_t  Type;  // OR-ed flags of EnIPv4 and EnIPv6, whichever exists
	union
	{
		EnIPv4AddressWord  IPv4[EnIPv4_ADDRESS_WORDS];
		EnIPv6AddressWord  IPv6[EnIPv6_ADDRESS_WORDS];
	};
} EnIPAddress;


// Host record
//
struct EnHost_S
{
	EObjectCore(EObject);

	int             IPAddressCount;           // There may be relays...	
	EnIPAddress     IPAddresses[EnMAX_RELAYNUM];
	int             PortNumber;               // Preferred port number on which to contact this host

//	EnHostCallbackProc  Callback;                 // Function to call when a connection is established, terminated or an error occured...
};


#define EnCONTENTUINT32(p)   *(uint32_t*)(p)

//--------------------------------------------------------
// How to poll to the server port in every n seconds
//--------------------------------------------------------
enum
{
	EnTmALARM_SIGNAL,  // With alarm signals
	EnLtXe_TIMER       // With Xe timer
};


#define EnONLINE           0x100
#define EnNOT_ESTABLISHED  0x200
#define EnSENDING          0x400
#define EnRECEIVING        0x800

#define EnCONNECTION_NOT_FOUND  (-1)
#define EnCONNECTION_BUSY       (-2)

#define EnBLKHDROFFS_TOTALLENGTH  4
#define EnBLOCKHEADERLENGTH       8

#define EnBLOCKRCVLENGTH          8  // Length of 'handshake' message
#define EnNETNAMELEN             64  // Max length of host name
#define EnAPPNAMELEN             32  // Max length of application name


enum
{
	EnStatusNONE = 0,
	EnStatusCONNECTION_OUT_ESTABLISHED,
	EnStatusCONNECTION_IN_ESTABLISHED
};

typedef struct
{
	pid_t  ProcessID;
	int    ServerPort;
	char   AppName[EnAPPNAMELEN];
} EnAppLinkRec;


// Read status values
//
enum
{
	EnCLEAR = 0,         // No data had been evaluated yet
	EnREADING_HEADER,    // Reading packet header
	EnREADING_PACKET,    // Reading packet payload
	EnPROCESSING_PACKET  // Calling the application to process the packet
};


#define EnGARBAGE_ON_READ	-1


#define EnConnectionCore\
	int             RefCnt;            /* Reference count  */\
\
	EnHost*         Host;\
\
	int             SocketType;        /* EnRAW, EnUDP or EnTCPIP                          */\
	EBool           SocketConnected;   /* connect() was used. This is optional for EnUDP   */\
	int             ConnStatus;\
	unsigned int    PortNumber;\
\
	int             Socket;\
\
	int             NewBytesReceived;  /* Bytes just read                                 */\
	int             InOffset;          /* ENet's packet reader will store the next incoming packet at this position  */\
	uint8_t*        InBuffer;          /* On-demand-allocated receiving buffer            */\
	unsigned int    InBufferSize;      /* Allocated size of InBuffer                      */\
\
/* This part is used mainly by the application's packet evaluator  */\
\
	int             ReadStatus;\
	int             ReadPtr;             /* Next byte to read (evaluate_                  */\
	uint32_t        ReadPacketLength;    /* Packet length read from the ENet header       */\
	int             BytesToRead;         /* Bytes to read from the incoming packet (can be negative if we got a part of the next packet)  */\
\
/* This part is used by the application's packet sender  */\
\
	int             BytesToSend;          /* Bytes to send of the outgoing block           */\
	int             BytesReceivedByPeer;  /* Bytes of the outgoing block actually received by the receiver             */\
	int             OutOffset;            /* Bytes already sent of the outgoing block (current position in OutBuffer)  */\
	const uint8_t*  OutBuffer;\
\
	void*           UserData;\
	EBool           Lock;\
	EBool           UseENetHeader         /* Insert ENet header with identifier (ENet), message type and length before transmission and parse header during read */

struct EnConnection_S
{
// EnConnection fields
//
	EnConnectionCore;
};



typedef struct
{
// EnConnection fields
//
	EnConnectionCore;

// AppLink fields
//
	char           AppName[EnAPPNAMELEN];  // Name of the remote application
	uid_t          RAppProcID;             // Process id of the other application
	int            SharedMemoryID;
	void*          SharedMemoryAddress;
	unsigned long  SharedMemorySize;
} EnAppLinkConnection;


// Block types
//
enum
{
	EnBT_CONNECTION_REPLY0_FROM_CALLED=1,
	EnBT_CONNECTION_CONFIRM,
	EnBT_CONNECTION_CANCEL,
	EnBT_CONNECTION_ESTABLISHED,
	EnBT_CONNECTION_CANCELLED,
	EnBT_BLOCK_RECEIVED,
	EnBT_COME_IN,           // 'Come-in' message (usually broadcasted)
	EnBT_I_AM_HERE,         // Reply to 'Come-in' message
	EnBT_EXIT,              // Exit command to the remote application
	EnBT_CANNOT_EXIT,       // Replies to EXIT command
	EnBT_EXITED,
	EnBT_IMAGE,             // An incoming image
	EnBT_VIDEO_SETUP,       // Commands for EVideoDaemon
	EnBT_VIDEO_SEND_IMAGE,
	EnBT_VIDEOD_MESSAGE,    // Message from EVideoDaemon
	EnBT_3DMODEL,           // 3D system 'messages'
	EnBT_3DTRANSFORMATION
};

#define EnBT_CUSTOM	EnBT_EXIT  // The first 'Custom' message type

// VideoDaemon messages
//
enum
{
	EnVIDEO_COULD_NOT_CONNECT_TO_DAEMON = 1
};


// Callback reasons
//
enum
{
	EnRSN_NETWORKING_INITIALIZED             =  -1,
	EnRSN_CANNOT_CONNECT_TO_HOST             =  -2,
	EnRSN_THERE_IS_ONLY_ME                   =  -3,
	EnRSN_CONNECTION_REQUEST_OUT             =  -4,
	EnRSN_CONNECTION_OUT_SHALL_I_ACCEPT_IT   =  -5,
	EnRSN_CONNECTION_OUT_CANCELLED           =  -6,
	EnRSN_CONNECTION_OUT_REGISTERED          =  -7,  // Socket is connected to an address. For RAW, UDP (non-TCP/IP) sockets, connect() doesn't actually create a connection with the other side
	EnRSN_CONNECTION_OUT_ESTABLISHED         =  -8,  // Socket is connected to an address  and connection is established. TCP/IP) sockets only.
	EnRSN_CONNECTION_OUT_TERMINATED          =  -9,
	EnRSN_CONNECTION_IN_REGISTERED           = -10,
	EnRSN_CONNECTION_IN_ESTABLISHED          = -11,
	EnRSN_CONNECTION_IN_TERMINATED           = -12,
	EnRSN_CANNOT_CONNECT                     = -13,
	EnRSN_BLOCK_HEADER_RECEIVED              = -14,
	EnRSN_PART_OF_BLOCK_SENT                 = -15,
	EnRSN_BLOCK_SENT                         = -16,
	EnRSN_PART_OF_BLOCK_RECEIVED             = -17,
	EnRSN_BLOCK_RECEIVED                     = -18,
	EnRSN_DO_YOU_NEED_THIS_BLOCK             = -19,
	EnRSN_LOCAL_LIST_CHANGED                 = -20,
	EnRSN_REMOTE_LIST_CHANGED                = -21,
	EnRSN_APPLINK_CONNECTION_OUT_ESTABLISHED = -22
};


// Callback replies from the application
//
enum
{
	EnREPLY_ACKNOWLEDGED = 1,
	EnREPLY_ACCEPT_CONNECTION,
	EnREPLY_CANCEL_CONNECTION,
	EnREPLY_GET_THAT_BLOCK,
	EnREPLY_FORGET_IT,
	EnREPLY_ERROR
};



typedef struct
{
	char*                    Name;
	int                      Protocol;          // EnRAW, EnUDP or EnTCPIP
	int                      SocketType;        // SOCK_RAW, SOCK_DGRAM or SOCK_STREAM
	int                      Socket;
	EnCallbackFunc           CallbackFunc;
	int                      IOMonitoringMode;  // EnSIGNAL, or EnTHREAD
	struct sockaddr_storage  Address;
	uint16_t                 Port;
	EBool                    SocketBound;

	EListA(, EnConnection*, Connections);
} EnServer;



// Registered hosts
//
EList(extern, EnHost*, En_Hosts);

extern char*           En_MyHostName;
extern EnIPAddress     En_MyIPAddress;

extern unsigned int    En_MaxInBufferSize;

extern int             En_BroadcastSocket;

extern EBool           En_Verbose;


extern void     En_IPAddressInit(EnIPAddress* PA);
extern EBool    En_IPAddressIsLocal(const EnIPAddress* PIPAddress);
extern int      En_NameToIPAddress(const char* PName, EnIPAddress* PIPAddress);
extern int      En_StringToIPAddress(const char* PIPAddressString, EnIPAddress* PIPAddress);
extern int      En_IPAddressToString(const EnIPAddress* PIPAddress, char* PIPAddressString);

extern EBool    En_IPAddressesEqual(const EnIPAddress* P0, const EnIPAddress* P1);
extern void     En_IPAddressSetFrom_sockaddr_in(EnIPAddress* PIPAddress, const struct sockaddr_in* PIn);
extern void     En_IPAddressSetFrom_sockaddr_in6(EnIPAddress* PIPAddress, const struct sockaddr_in6* PIn);
extern int      En_IPAddressSetFrom_sockaddr(EnIPAddress* PIPAddress, const struct sockaddr* PSockAddr);


extern uint8_t*  En_InitNetPacket(uint8_t* PBuffer, uint8_t** PContentStartRet);
extern int       En_CloseAndSendPacket(EnConnection* PConnection, uint8_t* PBuffer, uint8_t* PContentStart, uint8_t* PPtr);


extern EnConnection*   En_RegisterConnectionRequest(EnConnection* PConnectionTmplt, unsigned int PConnStatus);

extern void       En_ConnectionRemove(EnServer* PServer, EnConnection* PConnection);
extern EIndex     En_ConnectionReadENetPacket(EnConnection* PConnection);

extern EIndex     En_SendBlockToConn(EnConnection* PConnection, const EIndex PBlockNumber, const EIndex PBlocksRemaining, uint8_t* PBuffer, const EIndex PLength);
extern EIndex     En_SendBlock(EnConnection* PConnection, const EIndex PBlockNumber, const EIndex PBlocksRemaining, uint8_t* PBuffer, const EIndex PLength);
extern EIndex     En_Broadcast(EnServer* PServer, uint8_t* PBuffer, const EIndex PLength);
extern int        En_ConnectToHost(EnHost* PHost, const int PProtocol, u_short PPortNumber,  EnConnection** PConnectionP);
extern int        En_Debug(const int POperation);

extern int        En_HostNameToIPAddress(EnHost* PHost);

extern int        En_Initialize(const char* PNetName, const char* PAppName, EnCallbackFunc PAppCallback);
extern void	      En_Shutdown(void);

extern EnServer*  En_ServerAdd(const char* PName, EnCallbackFunc PCallbackFunc, const int PProtocol, const u_short PServerPort, const uint8_t PMonitoringMode);
extern int        En_ServerRemove(EnServer* PServer);
extern void	      En_ServerFree(EnServer* PServer);

extern char*   En_ConnectionToString(EnConnection* PConnection, char* PStr);


extern const  EObjectClass  EnHost_class[];


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
}	// Close scope of 'extern "C"' declaration
#endif


#endif	// _ENet_h
