#pragma once
#include <string>
#include <thread>
#include <iostream>
#include <future>
#include "BaseSettings.h"
#include "RawData.h"
#include "BaseDataExtractor.h"
#include "CommunicationStructures.h"

//#define MONITORING_ENABLED

using namespace std;

#ifdef STARFLEETTOOLBOX_EXPORTS
#define BASECOMM __declspec(dllexport)
#else
#define BASECOMM __declspec(dllimport)
#endif

#define MONITORING_TIMESLEEP 250		// Sets the loop period of the monitoring system
#define RECONNECT_TIMESLEEP 250			// Sets the reconnect timeout period in the monitoring system


#define MONITORING_MODE unsigned int
#define MONITORING_OFF 0				// Indicates that monitoring is OFF
#define MONITORING_ON 1					// Indicates that monitoring is ON
#define AUTO_RECONNECT 2				// Indicates that monitoring is ON and auto reconnect mode is on

typedef void  (*MarshallingConnectionCheckerCallback)(CommunicationStatus*);
typedef std::function<void(CommunicationStatus*)> ConnectionCheckerCallback;

/// <summary>
/// Basic abstract communication class. Must be implemented by subclasses in charge of communication
/// </summary>
class BASECOMM BaseCommunication abstract
{
public:	
	/// <summary>
	/// Base constructor
	/// </summary>
	BaseCommunication();

	/// <summary>
	/// Base constructor
	/// </summary>
	BaseCommunication(MONITORING_MODE monitor);

	/// <summary>
	/// Destructor
	/// </summary>
	~BaseCommunication();

	/// <summary>
	/// Sets the communication settings
	/// </summary>
	/// <param name="settings">The communication settings to be set</param>
	void SetSettings(BaseSettings* settings);

	/// <summary>
	/// Gets the communication settings
	/// </summary>
	/// <returns>An encaspulation of communication settings</returns>
	BaseSettings* GetSettings();


	/// <summary>
	/// Sets the data extractor
	/// </summary>
	/// <param name="pDataExtractor">The data extractor to be set</param>
	void SetDataExtractor(BaseDataExtractor* pDataExtractor);

	/// <summary>
	/// Gets the data extractir
	/// </summary>
	/// <returns>A pointer to the data extractor</returns>
	BaseDataExtractor* GetDataExtractor();

	/// <summary>
	/// Pure virtual method that will help the caller to proceed to the connection
	/// to media
	/// </summary>
	virtual CommunicationStatus Connect() = 0;

	/// <summary>
	/// Pure virtual method that will help the caller to proceed to media disconection
	/// </summary>
	virtual void Disconnect() = 0;

	/// <summary>
	/// Returns the actual connection state.
	/// </summary>
	/// <returns>An enumeration with connection status</returns>
	virtual CommunicationStatus GetConnectionStatus() = 0;

	/// <summary>
	/// Indicates rather or not connect or disconnect method has been called
	/// </summary>
	/// <returns>true if 'connect' has been called</returns>
	bool IsConnected();


	/// <summary>
	/// Pure virtual method that will help the caller to send command
	/// on the media
	/// </summary>
	/// <param name="commandToSend">A string representing the command to be sent</param>
	/// <returns></returns>
	virtual string SendCommand(string commandToSend) = 0;

	/// <summary>
	/// Pure virtual method that will help the caller to wait for RAW data comming
	/// from the media
	/// </summary>
	/// <param name="waitTimeout">The amount of miliseconds the process has to wait befor being released</param>
	/// <param name="semStatus">The status of the semaphore (updated by reference)</param>
	/// <returns>A pointer to RAW Data</returns>
	virtual RawData WaitForRawData(int waitTimeout, unsigned long* semStatus) = 0;	

	/// <summary>
	/// Starts the monitoring process
	/// </summary>
	void StartMonitoring();
	
	/// <summary>
	/// Stops the monitoring process
	/// </summary>
	void StopMonitoring();

	void SetCommunicationCallback(ConnectionCheckerCallback);
	void SetCommunicationCallback(void(* ConnectionCheckerCallbackMethod)(CommunicationStatus*));

protected:
	std::atomic<bool> _connected;
	std::atomic<bool> _reconnecting;
	bool _connectHasBeenCalled = false;

	/// <summary>
	/// Returns the monitoring mode
	/// </summary>
	/// <returns>MONITORING_OFF, MONITORING_ON or AUTO_RECONNECT</returns>
	MONITORING_MODE GetMonitoringMode();
	
	virtual CommunicationStatus Reconnect() = 0;

	void *_mutexMonitoring;										/// The mutex for the monitoring flag
	void* _mutexConnectState;									/// The mutex for the connection state value
	volatile int _connStatus = 0;								/// The real connection status given bythe monitoring process
private:
	std::atomic<bool> _monitoringExit;							/// The flag to exit the monitoring thread
	thread _monitoringWorker;									/// The monitoring thread handle	
	MONITORING_MODE _monitoringMode = MONITORING_OFF;			/// Default monitoring mode	
	ConnectionCheckerCallback _monitorinCallBack = NULL;		/// the monitoring callback that has to be called by MonitoringBusiness
	void AsyncWrapper(ConnectionCheckerCallback, CommunicationStatus*); // async wrapper for callback
	void MonitoringBusiness();									/// The monitoring thread business code
	
	BaseSettings* _settings;
	BaseDataExtractor* _dataExtractor;
};

