#pragma once
#include<iostream>
#include<sstream>
#include "../Toolbox/BaseCommunication.h"
#include "../Toolbox/BaseSensor.h"
#include "NetworkInformation.h"
#include "AngleRange.h"
#include "ContaminationThreshold.h"
#include "../Toolbox/ContaminationStatus.h"
#include "../Toolbox/SoftwareVersion.h"
#include "../Toolbox/BaseDataExtractor.h"
#include "RawDataError.h"
#include "SensorLeds.h"
#include "SensorLamps.h"
#include "../Toolbox/ConfigurationResult.h"
#include "../Toolbox/AvailableResolution.h"

using namespace std;

#ifdef RAWDATASENSOR_EXPORTS
#define RAWDATASENSOR __declspec(dllexport)
#else
#define RAWDATASENSOR __declspec(dllimport)
#endif


typedef int PROTOCOL;
#define TCP 1
#define UDP 0

// Command mode
#define READ 0
#define WRITE 1

typedef int SCAN_PACKET_TYPE;
#define DISTANCE 0
#define DISTANCE_INTENSITY 1

typedef int ANGULAR_RESOLUTION;
#define ZERO_TO_ZERO_POINT_TWO_AT_EIGHTY_HZ 0
#define ZERO_TO_ZERO_POINT_ONE_AT_FOURTY_HZ 1
#ifdef LEUZE
#define ZERO_TO_ZERO_POINT_ONE_AT_FIFTY_HZ 4
#endif

typedef int SCANNING_DIRECTION;
#define CLOCKWISE 0
#define COUNTER_CLOCKWISE 1

typedef int FILTER_STATUS;
#define FILTER_OFF 0
#define FILTER_ON 1

typedef int WINDOW_CALIBRATION;
#define PROCESSING 0
#define DONE 1
#define FAILED 3

/// <summary>
/// Definition of the Raw data sensor services.
/// This header will let the programmer to handle all the requests 
/// that have to be sent to the hardware through the appropriate channel
/// </summary>
class RAWDATASENSOR RawDataSensor : public BaseSensor, public BaseDataExtractor
{
public:
	/// <summary>
	/// Base constructor
	/// </summary>
	RawDataSensor();

	/// <summary>
	/// Destructor (disconects the data sensor)
	/// </summary>
	~RawDataSensor();

	/// <summary>
	/// Copy constructor: sets the basic communication object
	/// </summary>
	/// <param name="comm">The basic communication object</param>
	RawDataSensor(BaseCommunication* comm);
	
	/// <summary>
	/// Extracts the data from the datastream (Internal method, do not use as is)
	/// </summary>
	/// <param name="pDataToExtract">Datastream vector</param>
	/// <returns>The encaspulated raw data</returns>
	RawData ExtractData(vector<string>pDataToExtract);

	/// <summary>
	/// Check if data is a command acknowledgment
	/// </summary>
	/// <param name="pDataToVerify">The string to be checked</param>
	/// <returns>true if verified</returns>
	bool IsCommandAcknowledgment(string pDataToVerify);

	/// <summary>
	/// Check if data is a HEADER
	/// </summary>
	/// <param name="pDataToVerify">The string to be checked</param>
	/// <returns>true if verified</returns>
	bool IsHeader(string pDataToVerify);

	/// <summary>
	/// Check if the data is an end of packet group
	/// </summary>
	/// <param name="pDataToVerify">The string to be checked</param>
	/// <returns>true if verified</returns>
	bool IsEndOfPacket(string pDataToVerify);

	/// <summary>
	/// Check if the data is the first packet group
	/// </summary>
	/// <param name="pDataToVerify">The string to be checked</param>
	/// <returns>true if verified</returns>
	bool IsFirstPacket(string pDataToVerify);

	/// <summary>
	/// Returns a pointer on an array with a determined size
	/// </summary>
	/// <returns>A char array</returns>
	int GetPacketHeaderSizeToRead();

	/// <summary>
	/// Indicates how much byte must be read in order to obtain packet len
	/// </summary>
	/// <returns>An integer representing the amount of bytes to read</returns>
	int CountToReadForLength(char* pHeaderBuffer);

	/// <summary>
	/// Retrieve, in the pHeaderBuffer, the amount of data to read
	/// </summary>
	/// <param name="pHeaderBuffer"></param>
	/// <param name="pPacketType">RAW data (2) or Command data (4) ?</param>
	/// <returns>The amount of data to read</returns>
	int GetPacketLengthToRead(char* pLenHeaderBuffer, int pPacketType);

	/// <summary>
	/// Initializes the object to connect the sensor
	/// </summary>
	CommunicationStatus Initialize();

	/// <summary>
	/// Method that closes all communication
	/// stops all threads and cleanup memory
	/// This method must be called when finishing usage of sensor
	/// </summary>
	void Close();

	// Public exposed feature methods
	
	/// <summary>
	/// Wait for Raw Data availability
	/// </summary>
	/// <returns>A pointer to the dequeued raw data</returns>
	RawData RetrieveRawData();

	
	// Device utilities

	/// <summary>
	/// Resets the device
	/// </summary>
	void ResetDevice();

	/// <summary>
	/// Reboots the device
	/// </summary>
	void RebootDevice();

	// Command settings management
	// Data retrieve
	/// <summary>
	/// Gets whole data information
	/// </summary>
	/// <returns>An encapsulated way to group the network information</returns>
	NetworkInformation  GetNetworkInformation();

	/// <summary>
	/// Get the raw data exchange mode
	/// </summary>
	/// <returns>TCP or UDP</returns>
	PROTOCOL GetRawDataExchangeMode();

	/// <summary>
	/// Gets the data packed type
	/// </summary>
	/// <returns>DISTANCE or DISTANCE_INTENSITY</returns>
	SCAN_PACKET_TYPE GetDataPacketType();

	/// <summary>
	/// Gets the angular resolution
	/// </summary>
	/// <returns>An enum of angle resolution</returns>
	SensorResolutionFrequencyEnum GetAngularResolution();

	/// <summary>SensorResolutionFrequencyEnum
	/// Gets the scanning direction
	/// </summary>
	/// <returns>CLOCKWISE or COUNTER_CLOCKWISE</returns>
	SCANNING_DIRECTION GetScanningDirection();

	/// <summary>
	/// Gets the angle range
	/// </summary>
	/// <returns>An encapsulation of angle range</returns>
	AngleRange GetAngleRange();

	/// <summary>
	/// Gets the skip scan
	/// </summary>
	/// <returns>An integer representing the skip scan</returns>
	unsigned int GetSkipScan();

	/// <summary>
	/// Gets the immunity level
	/// </summary>
	/// <returns>An integer representing the immunity level</returns>
	unsigned int GetImmunityLevel();

	/// <summary>
	/// Gets the contamination threshold
	/// </summary>
	/// <returns>An encapsulation of the contamination threshold</returns>
	ContaminationThreshold GetContaminationThreshold();

	/// <summary>
	/// Gets the contamination status
	/// </summary>
	/// <returns>An ContaminationStatus representing the contamination status</returns>
	ContaminationStatus GetContaminationStatus();

	/// <summary>
	/// Gets the sensor version data
	/// </summary>
	/// <returns>An encapsulation representing the sensor version</returns>
	SoftwareVersion GetVersion();

	/// <summary>
	/// Gets the platform version
	/// </summary>
	/// <returns>a uint16_t containing the version number</returns>
	uint16_t GetPlatformVersion();

	/// <summary>
	/// Gets the temperature
	/// </summary>
	/// <returns>An integer representing the sensor's temperature</returns>
	int GetTemperature();

	/// <summary>
	/// Gets the error log
	/// </summary>
	/// <returns>An encapsulation representing the error log</returns>
	vector<RawDataError> GetErrorLog();

	/// <summary>
	/// Gets the sensor's leds status
	/// </summary>
	/// <returns>An encapsulation representing the sensor's leds status</returns>
	SensorLeds GetSensorLeds();

	/// <summary>
	/// Gets the sensor lamps status
	/// </summary>
	/// <returns>An encapsulation of the sensor's internal lamps status</returns>
	SensorLamps GetSensorLamps();	

	/// <summary>
	/// Gets the sensor runtime hours
	/// </summary>
	/// <returns>A long representing the sensor's runtime hours</returns>
	long GetRuntimeHours();

	/// <summary>
	/// Gets the sensor's name 
	/// </summary>
	/// <returns>A string representing the sensor's name</returns>
	string GetDeviceName();

	/// <summary>
	/// Gets the sensor's filter status
	/// </summary>
	/// <returns>0: OFF, 1: ON</returns>
	FILTER_STATUS GetFilterStatus();

	/// <summary>
	/// Gets the window calibration status
	/// </summary>
	/// <returns>PROCESSING, DONE or FAILED</returns>
	WINDOW_CALIBRATION GetWindowCalibrationStatus();

	/// <summary>
	/// Set the overall network information except IP v4 and Port
	/// </summary>
	/// <param name="pNetwork">An encapsulation of the network data</param>
	/// <returns>A configuration result encapsulation</returns>
	ConfigurationResult SetNetworkInformation(NetworkInformation pNetwork);

	/// <summary>
	/// Sets the RAW data exchange mode. Can be either TCP or UDP
	/// </summary>
	/// <param name="pProtocol">The exchange mode (TCP or UDP)</param>
	/// <returns>A configuration result encapsulation</returns>
	ConfigurationResult SetRawDataExchangeMode(PROTOCOL pProtocol);

	/// <summary>
	/// Sets the packet type returned in raw data. Can be either DISTANCE or DISTANCE_INTENSITY
	/// </summary>
	/// <param name="pPacketType">The packet type to be set</param>
	/// <returns>A configuration result encapsulation</returns>
	ConfigurationResult SetDataPacketType(SCAN_PACKET_TYPE pPacketType);

	/// <summary>
	/// Sets the angle resolution. Can be either 80Hz or 40Hz
	/// </summary>
	/// <param name="pResolution"></param>
	/// <returns>A configuration result encapsulation</returns>
	ConfigurationResult SetAngularResolution(SensorResolutionFrequencyEnum pResolution);

	/// <summary>
	/// Sets the scanning direction. Can be either CLOCKWISE or COUNTER_CLOCKWISE
	/// </summary>
	/// <param name="pScanDirection"></param>
	/// <returns>A configuration result encapsulation</returns>
	ConfigurationResult SetScanningDirection(SCANNING_DIRECTION pScanDirection);

	/// <summary>
	/// Sets the Angle range (start and stop)
	/// </summary>
	/// <param name="pAngleRange">The encapsulation of the angle range to be set</param>
	/// <returns>A configuration result encapsulation</returns>
	ConfigurationResult SetAngleRange(AngleRange pAngleRange);

	/// <summary>
	/// Sets the skip scan
	/// </summary>
	/// <param name="pSkipScan">The skip scan value to be set</param>
	/// <returns>A configuration result encapsulation</returns>
	ConfigurationResult SetSkipScan(int pSkipScan);

	/// <summary>
	/// Sets the sensor's immunity level
	/// </summary>
	/// <param name="pImmunityLevel">The immunity level to be set</param>
	/// <returns>A configuration result encapsulation</returns>
	ConfigurationResult SetImmunityLevel(int pImmunityLevel);

	/// <summary>
	/// Sets the contamination level threshold for warning and error
	/// </summary>
	/// <param name="pContaminationThreshold">The encapsulation of contamination leve threshold</param>
	/// <returns>A configuration result encapsulation</returns>
	ConfigurationResult SetContaminationThreshold(ContaminationThreshold pContaminationThreshold);

	/// <summary>
	/// Sets the sensors external leds status (ON or OFF)
	/// </summary>
	/// <param name="pLeds">The encapsulation of the leds to be set</param>
	/// <returns>A configuration result encapsulation</returns>
	ConfigurationResult SetSensorLeds(SensorLeds pLeds);

	/// <summary>
	/// Sets the sensor's name 
	/// </summary>
	/// <returns>A string representing the sensor's name</returns>
	ConfigurationResult SetDeviceName(string pName);

	/// <summary>
	/// Sets the sensor's filter status
	/// </summary>
	/// <param name="pFilterStatus">FILTER_ON or FILTER_OFF will enable / disable the filter</param>
	/// <returns></returns>
	ConfigurationResult SetFilterStatus(FILTER_STATUS pFilterStatus);


	/// <summary>
	/// Starts the window calibration
	/// </summary>
	/// <returns></returns>
	ConfigurationResult StartWindowCalibration();

	/// <summary>
	/// Request the sensor to start sending MDI data to host
	/// </summary>
	void SendMdi();

	/// <summary>
	/// Request the sensor to stop sending MDI data to host
	/// </summary>
	void StopMdi();
	friend class AgvSensor;
private:	
	// Variable used to set up the RetrieveRawData wait system. 
	// in case of StopMdi, the thread will remain blocked
	// This can cause unwanted behaviour when a BaseDataRecorder
	// has join the RawDataSensor class and attempts to replay recorded frames
	// So, this variable will be setup differently depending on the presence
	// or not of a BaseDataRecorder
	int _globalWaitTimeout = 0xFFFFFFFF; 

	/// <summary>
	/// Extracts a resuylt from a stream. It substrings the answer into readable parts
	/// </summary>
	/// <param name="readWriteResponseCommand">The command prefix</param>
	/// <param name="command">Command to be analysed and split</param>
	/// <param name="result">The data returned by stream</param>
	/// <returns></returns>
	string ExtractRawResult(string readWriteResponseCommand, string command, string result);

	/// <summary>
	/// Check for the intensity in the RAW data
	/// </summary>
	/// <param name="toVerify">String to be verified</param>
	/// <returns>A bool indicating if the stream is either DISTANCE or DISTANCE_INTENSITY</returns>
	bool CheckForDistIntensity(string toVerify);

	int _lastPacket = -1;
protected:
	/// <summary>
	/// This method analyses and decompose the reply to a command comming from the sensor media.
	/// It also checks if the reply matches the command that was sent in order to ensure coherence of the data
	/// It decompose the reply into a data vector representing the requested data.
	/// </summary>
	/// <param name="MODE">READ or WRITE mode</param>
	/// <param name="commandCode">The sensor GCID command to be analysed</param>
	/// <param name="result">A string representing the media stream returned from the sensor after command execution</param>
	/// <returns>A char array</returns>
	string ExtractCommandResult(int MODE, string commandCode, string result);

	
	
	string GetImmu = { 0x47 ,0x65 ,0x74 ,0x49, 0x6D, 0x6D, 0x75 };
	string SetImmu = { 0x53, 0x65, 0x74, 0x49, 0x6D, 0x6D, 0x75 };
};

