

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  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(_NETWORKMANAGER_H_)
#define _NETWORKMANAGER_H_

#pragma once

#include "NetworkConnections.h"
#include "NetworkProtocols.h"


namespace	cmlabs{

struct NetworkEvent {
	uint64 cid;
	uint64 conid;
	uint64 time;
	uint8 type;
	uint8 protocol;
};

/////////////////////////////////////////////////////////////
// Receiver Interface
/////////////////////////////////////////////////////////////

class NetworkChannel;
class NetworkReceiver {
public:
	NetworkReceiver() {}
	virtual bool receiveNetworkEvent(NetworkEvent* evt, NetworkChannel* channel, uint64 conid) {return false;}
	virtual bool receiveHTTPRequest(HTTPRequest* req, NetworkChannel* channel, uint64 conid) {return false;}
	virtual bool receiveMessage(DataMessage* msg, NetworkChannel* channel, uint64 conid) {return false;}
	virtual bool receiveTelnetLine(TelnetLine* line, NetworkChannel* channel, uint64 conid) {return false;}
	virtual bool receiveHTTPReply(HTTPReply* reply, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {return false;}
};

class HTTPTestServer : public NetworkReceiver {
public:
	HTTPTestServer();
	~HTTPTestServer();
	virtual bool receiveHTTPRequest(HTTPRequest* req, NetworkChannel* channel, uint64 conid);
};

/////////////////////////////////////////////////////////////
// Network Manager
/////////////////////////////////////////////////////////////

class NetworkChannel;
class NetworkManager : public Runnable {
	friend class NetworkChannel;
public:
	static bool UnitTest();
	static bool UnitTestHTTP();
	static bool TestHTTP(const char* host, uint32 port, std::vector<std::string> &urls);

	THREAD_RET THREAD_FUNCTION_CALL NetworkManagerRun(THREAD_ARG arg);

	NetworkManager();
	virtual ~NetworkManager();

	bool setSSLCertificate(const char* sslCertPath, const char* sslKeyPath);

	NetworkChannel* createListener(uint16 port, uint8 encryption, uint8 protocol, bool isAsync, uint32 protocolTimeout, bool isDefaultProtocol, uint32 channelID, NetworkReceiver* recv);
	NetworkChannel* createTCPConnection(const char* addr, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, bool autoreconnect, uint32 channelID, NetworkReceiver* recv, uint64& conid, uint64& location);
	NetworkChannel* createTCPConnection(uint64 location, uint8 encryption, uint8 protocol, bool isAsync, bool autoreconnect, uint32 channelID, NetworkReceiver* recv, uint64& conid);
	NetworkChannel* createTCPConnection(const uint32* addresses, uint16 addressCount, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, bool autoreconnect, uint32 channelID, NetworkReceiver* recv, uint64& conid, uint64& location);
	NetworkChannel* createUDPConnection(uint16 port, uint8 protocol, bool isAsync, bool autoreconnect, uint32 channelID, NetworkReceiver* recv, uint64& conid);

	bool stopListener(uint16 port, uint8 protocol);
	bool endConnection(uint64 conid);
	bool endUDPConnection(uint16 port);

	uint8 getConnectionType(uint64 conid);
	uint64 getRemoteAddress(uint64 conid);
	NetworkChannel* getConnection(uint64 conid);
	NetworkChannel* getTCPConnectionByPort(uint16 port);
	NetworkChannel* getUDPConnectionByPort(uint16 port);
	uint64 addConnection(NetworkChannel* channel);

	bool sendUDPMessage(DataMessage* msg, uint64 destination);

	HTTPReply* makeHTTPRequest(const char* url, uint32 timeout, const char* content = NULL, uint32 contentSize = 0);
	HTTPReply* makeHTTPRequest(HTTPRequest* req, const char* addr, uint16 port, uint8 encryption, uint32 timeout);

protected:
	std::map<uint32, NetworkChannel*> channels;
	std::map<uint64, NetworkChannel*> channelsByConnection;
	std::map<uint16, NetworkChannel*> listeners;
	std::map<uint16, NetworkChannel*> udpListeners;

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

	UDPConnection* udpOutputCon;
	utils::Mutex udpOutputConMutex;

	uint32 lastChannelID;
	uint64 lastConnectionID;
	bool run();
};

/////////////////////////////////////////////////////////////
// Connections
/////////////////////////////////////////////////////////////

#define NETWORKEVENT_CONNECT				1
#define NETWORKEVENT_RECONNECT				2
#define NETWORKEVENT_DISCONNECT				3
#define NETWORKEVENT_DISCONNECT_RETRYING	4
#define NETWORKEVENT_BUFFER_LOW				5
#define NETWORKEVENT_BUFFER_FULL			6
#define NETWORKEVENT_UNPROCESSED_DATA		7

class NetworkThread {
public:
	NetworkThread(NetworkChannel* parent, uint64 id);
	~NetworkThread();

	uint64 id;
	uint32 threadID;
	uint16 port;
	TCPListener* listener;
	NetworkConnection* con;
	uint32 defaultProtocol;
	uint32 autoProtocols;
	uint32 autoProtocolTimeout;
	bool autoreconnect;
	bool shouldContinue;
	bool isRunning;
	bool isAsync;
	HTTPRequest* lastRequest;
	NetworkChannel* parent;
};

class NetworkChannel {
	friend class NetworkManager;
public:
	NetworkChannel(NetworkManager* manager);
	virtual ~NetworkChannel();
	uint32 cid;

	bool isConnected(uint64 conid);
	// getStats();

	bool shutdown();

	bool startListener(uint64 cid, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, uint32 protocolTimeout = 3000, bool isDefaultProtocol = false);
	bool stopListener(uint16 port, uint8 protocol);

	uint64 createTCPConnection(const char* addr, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, bool autoreconnect, uint64& location);
	uint64 createTCPConnection(uint64 location, uint8 encryption, uint8 protocol, bool isAsync, bool autoreconnect);
	uint64 createTCPConnection(const uint32* addresses, uint16 addressCount, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, bool autoreconnect, uint64& location);
	uint64 createUDPConnection(uint16 port, uint8 protocol, bool isAsync, bool autoreconnect);
	bool endConnection(uint64 conid);
	bool endUDPConnection(uint16 port);
	uint8 getConnectionType(uint64 conid);
	uint64 getRemoteAddress(uint64 conid);

	uint64 autoDetectConnection(NetworkConnection* con, uint16 port, uint32 autoProtocols, uint32 autoProtocolTimeout, uint32 defaultProtocol, bool isAsync, bool autoreconnect);

	uint64 startConnection(NetworkConnection* con, uint8 protocol, bool isAsync, bool autoreconnect);

	bool setNewReceiver(NetworkReceiver* recv);

	NetworkEvent* waitForNetworkEvent(uint32 ms);

	HTTPRequest* waitForHTTPRequest(uint64& conid, uint32 ms);
	TelnetLine* waitForTelnetLine(uint64& conid, uint32 ms);
	DataMessage* waitForMessage(uint64& conid, uint32 ms);
	HTTPReply* waitForHTTPReply(uint64& conid, uint32 ms);

	bool sendHTTPReply(HTTPReply* reply, uint64 conid);
	bool sendTelnetLine(TelnetLine* line, uint64 conid);
	bool sendMessage(DataMessage* msg, uint64 conid);
	bool sendHTTPRequest(HTTPRequest* req, uint64 conid);
	HTTPReply* sendReceiveHTTPRequest(HTTPRequest* req, uint64 conid, uint32 timeout);
	TelnetLine* sendReceiveTelnetLine(TelnetLine* line, uint64 conid, uint32 timeout, uint32 size = 0);

	uint32 getOutputSpeed(uint64 conid);
	uint32 getInputSpeed(uint64 conid);

protected:
	NetworkManager* manager;
	NetworkReceiver* receiver;
	std::map<uint64, NetworkThread*> connectionThreads;
	std::map<uint16, NetworkThread*> listeners;
	std::map<uint16, NetworkThread*> udpListeners;

	std::queue<NetworkEvent*> eventQueue;
	utils::Semaphore eventQueueSemaphore;
	utils::Mutex eventQueueMutex;

	bool enterNetworkEvent(uint8 type, uint8 protocol, uint64 conid);

	bool enterHTTPRequest(HTTPRequest* req, uint64 conid);
	bool enterHTTPReply(HTTPReply* reply, HTTPRequest* req, uint64 conid);
	bool enterMessage(DataMessage* msg, uint64 conid);
	bool enterTelnetLine(TelnetLine* line, uint64 conid);

	std::queue<HTTPRequest*> queueHTTPRequests;
	utils::Semaphore queueHTTPRequestsSemaphore;
	utils::Mutex queueHTTPRequestsMutex;
	std::queue<HTTPReply*> queueHTTPReplies;
	utils::Semaphore queueHTTPRepliesSemaphore;
	utils::Mutex queueHTTPRepliesMutex;
	std::queue<DataMessage*> queueMessages;
	utils::Semaphore queueMessagesSemaphore;
	utils::Mutex queueMessagesMutex;
	std::queue<TelnetLine*> queueTelnetLines;
	utils::Semaphore queueTelnetLinesSemaphore;
	utils::Mutex queueTelnetLinesMutex;

	static THREAD_RET THREAD_FUNCTION_CALL NetworkListenerRun(THREAD_ARG arg);
	static THREAD_RET THREAD_FUNCTION_CALL ConnectionAutodetectRun(THREAD_ARG arg);

	static THREAD_RET THREAD_FUNCTION_CALL HTTPClientRun(THREAD_ARG arg);
	static THREAD_RET THREAD_FUNCTION_CALL HTTPServerRun(THREAD_ARG arg);
	static THREAD_RET THREAD_FUNCTION_CALL MessageConnectionRun(THREAD_ARG arg);
	static THREAD_RET THREAD_FUNCTION_CALL TelnetServerRun(THREAD_ARG arg);
};


}

#endif // _NETWORKMANAGER_H_
