

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

#pragma once

#include "NetworkManager.h"
#include "MovingAverage.h"

namespace	cmlabs{

/////////////////////////////////////////////////////////////
// Request Reply
/////////////////////////////////////////////////////////////

class RequestReply;
typedef void (* RequestCallbackFunction)(RequestReply& reply);

enum RequestStatus {IDLE, QUEUED, SENT, SUCCESS, FAILED, TIMEOUT, LOCALERROR, NETWORKERROR, SERVERERROR};

class RequestReply {
protected:
	// stats?
	RequestCallbackFunction callback;
	RequestStatus status;
	DataMessage* replyMsg;
	DataMessage* requestMsg;
	utils::Mutex mutex;
	utils::Semaphore semaphore;

public:
	RequestReply() { requestMsg = replyMsg = NULL; callback = NULL; status = IDLE; startTime = GetTimeNow(); origin = processor = clientRef = gatewayRef = execRef = finishTime = customRef = 0; isLongReq = false; }
	virtual ~RequestReply() { delete(requestMsg); delete(replyMsg); callback = NULL; status = IDLE; }

	uint64 startTime;
	uint64 finishTime;
	uint64 origin;
	uint64 processor;
	uint64 clientRef;
	uint64 execRef;
	uint64 gatewayRef;
	uint64 customRef;
	bool isLongReq;

	bool setStatus(RequestStatus status);
	RequestStatus getStatus();
	std::string getStatusText();
	bool setCallback(RequestCallbackFunction callback);
	RequestCallbackFunction getCallback();

	bool replyToRequest(DataMessage* msg, RequestStatus status);

	bool giveRequestMessage(DataMessage* msg);
	bool setRequestMessageCopy(DataMessage* msg);
	DataMessage* getRequestMessageCopy();
	DataMessage* peekRequestMessage();
	bool giveReplyMessage(DataMessage* msg);
	bool setReplyMessageCopy(DataMessage* msg);
	DataMessage* getReplyMessageCopy();
	DataMessage* peekReplyMessage();

	RequestStatus waitForResult(uint32 timeoutMS);
	DataMessage* waitForMessage(uint32 timeoutMS, bool takeMessage = false);

	uint64 getRequestDuration();
	uint32 getRequestDurationMS();
};

/////////////////////////////////////////////////////////////
// Request Client
/////////////////////////////////////////////////////////////

struct RequestConnection {
	uint64 lastConTime;
	uint64 lastFailTime;
	uint64 conID;
	uint64 location;
	uint64 lastStatusTime;
	uint32 shortReqQSize;
	uint32 longReqQSize;
	uint32 count;
	void clear() { lastConTime=lastFailTime=conID=location=lastStatusTime=0; shortReqQSize=longReqQSize=count=0; }
};

struct RequestGatewayConnection {
	uint32 id;
	uint64 lastConTime;
	uint64 lastFailTime;
	std::string addr;
	uint16 port;
	uint64 conID;
	uint64 location;
	uint8 encryption;
	void clear() { lastConTime=lastFailTime=conID=0; port=0; id=0; addr.clear(); conID=location=0; encryption = NOENC; }
};

class RequestClient : public Runnable, public NetworkReceiver {
public:
	friend THREAD_RET THREAD_FUNCTION_CALL RequestClientRun(THREAD_ARG arg);

	RequestClient();
	virtual ~RequestClient();

	bool isConnected();
	bool addGateway(uint32 id, std::string addr, uint16 port, uint8 encryption = NOENC);

	RequestReply* postRequest(DataMessage *msg);
	bool postRequest(DataMessage *msg, RequestCallbackFunction callback, uint32 timeoutMS);
	bool finishRequest(RequestReply* reply);

	bool receiveNetworkEvent(NetworkEvent* evt, NetworkChannel* channel, uint64 conid);
	bool receiveMessage(DataMessage* msg, NetworkChannel* channel, uint64 conid);
	bool receiveHTTPReply(HTTPReply* reply, HTTPRequest* req, NetworkChannel* channel, uint64 conid);

protected:
	NetworkManager* manager;
	NetworkChannel* channel;
	std::list<RequestGatewayConnection> connections;
	utils::Mutex mutex;
	utils::WaitQueuePointer<RequestReply*> outQ;
	std::map<uint64,RequestReply*> requestMap;
	uint32 threadID;
	uint64 lastRefID;
	uint64 sentCount;
	uint64 receivedCount;
	MovingAverage avgStats;

	bool sendRequest(RequestReply* reply);
	bool run();
};

THREAD_RET THREAD_FUNCTION_CALL RequestClientRun(THREAD_ARG arg);

class TestRequestClient : public RequestClient {
public:
	friend THREAD_RET THREAD_FUNCTION_CALL TestRequestClientRun(THREAD_ARG arg);
	TestRequestClient(bool isLong);
protected:
	bool testRun();
};

THREAD_RET THREAD_FUNCTION_CALL TestRequestClientRun(THREAD_ARG arg);

}

#endif // _REQUESTCLIENT_H_
