

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.0.0.170915_Beta4 (15 September 2017)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2017
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
#if !defined(_NETWORKCONNECTIONS_H_)
#define _NETWORKCONNECTIONS_H_

#pragma once

// #define UDPCON_PRINT_DEBUG
// #define UDPCON_PRINTBINARY_DEBUG
// #define TCPCON_PRINT_DEBUG
// #define TCPCON_PRINTBINARY_DEBUG

#include "ThreadManager.h"
#include "HTML.h"
//#include "PsyTime.h"

#ifdef _USE_SSL_
	#include "openssl/bio.h"
	#include <openssl/ssl.h>
	#include <openssl/err.h>
#endif // _USE_SSL_

namespace	cmlabs{

#define NOENC			1
#define SSLENC			2
#define AESENC			3

#define TCPCON			1
#define UDPCON			2
#define SSLCON			3
#define AESCON			4

#define INITIALBUFFERSIZE	4096

#define NETWORKERROR_ACCEPT			1
#define NETWORKERROR_RECEIVE		2
#define NETWORKERROR_SEND_ERROR		3
#define NETWORKERROR_SEND_TIMEOUT	4
#define NETWORKERROR_MEMORYFULL		5

class TCPListener;
class TCPConnection;
class UDPConnection;
class NetworkConnection;

class NetworkConnectionReceiver {
public:
	NetworkConnectionReceiver() {}
	virtual bool receiveNetworkConnection(NetworkConnection* con) = 0;
	virtual bool registerError(uint16 error, TCPListener* con) = 0;
};

class NetworkDataReceiver {
public:
	NetworkDataReceiver() {}
	virtual bool receiveData(char* data, uint32 size, NetworkConnection* con) = 0;
	virtual bool registerError(uint16 error, NetworkConnection* con) = 0;
};







class TCPListener : public Runnable {
public:
	friend THREAD_RET THREAD_FUNCTION_CALL TCPListenerRun(THREAD_ARG arg);

	TCPListener();
	virtual ~TCPListener();
	bool setSSLCertificate(const char* sslCertPath, const char* sslKeyPath);
	bool init(uint16 port, uint8 encryption, NetworkConnectionReceiver* receiver = NULL, NetworkDataReceiver* dataReceiver = NULL);
	bool disconnect(uint16 error = 0);
	bool isConnected();
	uint64 getLocalAddress();

	NetworkConnection* acceptConnection(uint32 timeout);

private:
	uint8 encryption;
	uint64 localAddress;
	uint64 lastActivity;
	SOCKET socket;
	NetworkConnectionReceiver* receiver;
	NetworkDataReceiver* dataReceiver;
	utils::Mutex mutex;

	std::string sslCertPath;
	std::string sslKeyPath;

	bool run();
};

class NetworkConnection : public Runnable {
public:
	NetworkConnection();
	~NetworkConnection();
	virtual bool disconnect(uint16 error = 0);
	virtual bool reconnect() = 0;
	virtual bool isConnected(int timeout = 0);
	virtual bool isRemote();
	virtual uint64 getRemoteAddress();

	virtual bool send(char* data, uint32 size, uint64 receiver = 0) = 0;
//	virtual int32 peekStream();
	virtual bool receive(char* data, uint32 size, uint32 timeout, bool peek = false);
	virtual bool receiveAvailable(char* data, uint32& size, uint32 maxSize, uint32 timeout, bool peek = false);
	virtual bool discard(uint32 size);
	virtual uint32 clearBuffer();

	virtual bool waitForDataToRead(uint32 timeout);
	virtual bool waitForDataToBeWritten(uint32 timeout);

	virtual uint32 getOutputSpeed();
	virtual uint32 getInputSpeed();

	virtual uint8 getConnectionType();

protected:
	uint8 type;
	uint64 remoteAddress;
	uint64 localAddress;
	uint64 lastActivity;
	uint32 threadID;
	SOCKET socket;
	bool remote;
	uint32 bufferLen;
	uint32 bufferContentLen;
	uint32 bufferContentPos;
	NetworkDataReceiver* receiver;
	utils::Mutex mutex;
	utils::Mutex sendMutex;
	char* buffer;

	uint32 inputSpeed;
	uint32 outputSpeed;
	uint64 inputBytes;
	uint64 outputBytes;

	virtual int32 readIntoBuffer();
	virtual bool resizeBuffer(uint32 len);
	virtual bool run();
};


class UDPConnection : public NetworkConnection {
public:
	friend THREAD_RET THREAD_FUNCTION_CALL UDPConnectionRun(THREAD_ARG arg);

	UDPConnection();
	~UDPConnection();
	bool initForOutputOnly();
	bool connect(uint16 port, NetworkDataReceiver* receiver = NULL);
	bool send(char* data, uint32 size, uint64 receiver = 0);
	bool reconnect();
	bool setDefaultReceiver(uint64 receiver);
	uint64 getLocalAddress();

protected:
	uint64 defaultReceiver;
};


class TCPConnection : public NetworkConnection {
public:
	friend THREAD_RET THREAD_FUNCTION_CALL TCPConnectionRun(THREAD_ARG arg);

	TCPConnection();
	~TCPConnection();
	bool connect(SOCKET s, uint64 localAddr, NetworkDataReceiver* receiver = NULL);
	bool connect(uint64 addr, NetworkDataReceiver* receiver = NULL);
	bool connect(const char* addr, uint16 port, uint64& location, NetworkDataReceiver* receiver = NULL);
	bool connect(const uint32* addresses, uint16 addressCount, uint16 port, uint64& location, NetworkDataReceiver* receiver = NULL);
	bool send(char* data, uint32 size, uint64 receiver = 0);
	bool reconnect();

protected:
	bool findRemoteAddress(uint64& addr);
};

THREAD_RET THREAD_FUNCTION_CALL SSLConnectionRun(THREAD_ARG arg);

class SSLConnection : public NetworkConnection {
public:
	friend THREAD_RET THREAD_FUNCTION_CALL SSLConnectionRun(THREAD_ARG arg);

	SSLConnection();
	~SSLConnection();
	bool init();
	bool init(const char *certFile, const char *keyFile);
	bool connect(SOCKET s, uint64 localAddr, NetworkDataReceiver* receiver = NULL);
	bool connect(uint64 addr, NetworkDataReceiver* receiver = NULL);
	bool connect(const char* addr, uint16 port, uint64& location, NetworkDataReceiver* receiver = NULL);
	bool connect(const uint32* addresses, uint16 addressCount, uint16 port, uint64& location, NetworkDataReceiver* receiver = NULL);
	bool send(char* data, uint32 size, uint64 receiver = 0);
	bool reconnect();
	bool isConnected(int timeout = 0);
	bool disconnect(uint16 error = 0);

	int32 peekStream();
	int32 readIntoBuffer();
	bool receive(char* data, uint32 size, uint32 timeout, bool peek = false);
	bool receiveAvailable(char* data, uint32& size, uint32 maxSize, uint32 timeout, bool peek = false);

	std::string certinfo;

protected:
	bool findRemoteAddress(uint64& addr);
	#ifdef _USE_SSL_
		SSL_CTX *ctx;
		SSL *ssl;
		BIO *certbio;
		BIO *outbio;
#endif //_USE_SSL_
};

THREAD_RET THREAD_FUNCTION_CALL TCPListenerRun(THREAD_ARG arg);
THREAD_RET THREAD_FUNCTION_CALL TCPConnectionRun(THREAD_ARG arg);
THREAD_RET THREAD_FUNCTION_CALL UDPConnectionRun(THREAD_ARG arg);

bool NetworkTest_TCPServer(uint16 port, uint32 count = 0);
bool NetworkTest_TCPClient(const char* address, uint16 port, uint32 count = 0);
bool NetworkTest_SendReceiveData(TCPConnection* con, char* data, uint32 dataLen, uint32 c, bool receiveFirst = false);

}

#endif // _NETWORKCONNECTIONS_H_
