#ifndef DEVICE_H_
#define DEVICE_H_

#include "framework.h"
#include "VideoCapture.h"
#include "TIMImager.h"
#include "VideoDisplay.h"
#include "Timer.h"
#include "BMPRecord.h"
#include "FaceDetection.h"
#include "DrawRect.h"
#include "MyEnum.h"
#include "Resource.h"
#include <thread>

namespace thermoCamera {

	/**
	* @class Device
	* @brief Manage devices added to the application 
	* libirimager is used from url:
	* http://documentation.evocortex.com/libirimager2/html/index.html
	* @author Matej Roman (student of BUT)
	*/
	class Device {

		static const int sleep_thread_for_face_detection = 100; // ms

	public:

		Device() :
			_RecordFromCameraAllowed(false),
			_RecordOnlyAreFaceDetected(false),
			_AutoSize(false),
			_TempRelative(false),
			_isFaceDetection(false),
			_TemperatureOfRegion(0.f),
			_intervalOfRecord(2000),
			_isAlaramSavingOn{false,false},
			_numOfAlarmImages(1),
			_FaceDetectionStreaming(false),
			_RecordReadWriteStreaming(false),
			_drawingRectIsOn(false),
			_eAccess(AccessLevel::forbidden),
			_ePlayback(Playback::fromCamera),
			_device(NULL),
			_display(NULL),
			_sizeOfImage{0,0},
			_StreamingThread(NULL),
			_RecordingThread(NULL),
			_FaceDetectionThread(NULL),
			_RecordReadWriteThread(NULL)
		{}

		~Device()
		{
			if (_display) _display->DeleteObjects();
			if (_display) { delete _display; _display = NULL; }
			if (_device) { _device = NULL; }
		}

		void AddNewDevice(VideoCapture** device);

		void RemoveDevice();

		void ConfigureDisplay(HWND hWnd, int index,int left, int top, int width, int height);

		void ConfigureBuffer(int index, int width, int height);

		void AddRecord(HWND hWnd, int index, int left, int top, int width, int height);

		void DrawOutput(const HDC& hdc, const RECT& location, const HDC& buffer) const;

		void DrawTemperature(const HDC& hdc, COLORREF color) const;

		void DrawMinMaxBar(const HDC& hdc, RECT locationOfBar) const;

		void DrawRectOfFaces(HWND hWnd, HDC hdc) const;

		void DrawTemperatureOfFaces(HWND hWnd, HDC hdc) const;

		void TrackingObject(HWND hWnd, HDC hdc);

		// Rect with right = width and bottom = height
		void EraseOutput(RECT& location) const;

		void EraseAndUpdateWindow();

		void StartFaceDetection();

		void StopFaceDetection();

		void SetManualRangeTemperature();

		void ResetManualRangeTemperature();

		// index = 0 for camera, 1 for record
		void ChangePlayback(int index) { 		if (index == 0 || index == 1) _ePlayback = (Playback)index; }

		void SaveImageAfterAlarm();

		void SetTemperatureOfRegion();

		HDC GetHDCBackBuffer(int index);

		RECT GetRectangleVideo(int index);

		float GetTemperatureOfRegion() { return _TemperatureOfRegion; }

		int GetNumOfBuffer() { return _sizeOfVideoDisplay.size(); }

		// return = 0 for camera, 1 for record
		int GetPlaybackStatus() { return (int)_ePlayback; }

		int GetMethodForRecord() { return _record.GetMode(); }

		UINT GetTimeFromTimer() {  return _timer.getInterval(); }

		double GetTimeFromTracker(int index) { return _FaceDetector.GetTimerOfTracker(index); }

		float GetTemperatureFromTracker(int index) { return _FaceDetector.GetTemperatureOfTracker(index); }

		float GetCalibratedValue() { if (_display) return _display->GetCalibratedValue(); else return 0.f; }

		float GetAlarmValue() { return (_display) ? _display->GetAlarmThreshold() : 0.f; }

		int GetNumOfTrackers() { return _FaceDetector.getNumOfTrackers(); }

		bool IsSetRelativeTemp() { return _TempRelative; }

		const TCHAR* GetAlarmPathToSaving() { return _pathToAlarmImage; }

		const TCHAR*  GetPathRecord();

		void Play();

		void Stop();

		void Pause();

		bool isStreaming();

		// 0 for max, 1 for average
		void DrawRectangleOnScreen(HWND hWnd, HDC hdc);

		void UpdateDrawingRectangle() { _drawRegion.UpdateRectangle(); }

		void EnablePaintingRectangle() { _drawRegion.enablePainting(); }

		void DisablePaintingRectangle() { _drawRegion.disablePainting(); }

		void SetDrawingRectangle() { _drawingRectIsOn = true; _drawRegion.setActive(); }

		void SetLeftPointForRectangleDrawing(POINT point) { _drawRegion.setLeft(point); }

		void SetColorRectangle(COLORREF color) { _drawRegion.setColor(color); }

		void ClearRectangle() { _drawRegion.clearRect(); }

		void ResetDrawingRectangle() { _drawingRectIsOn = false; _drawRegion.clearActive(); }

		void SetCalibratedValue(float value) { if (_display) _display->SetCalibratedValue(value); }

		void SetAlarmThreshold(float alarm) { if (_display) _display->SetAlarmThreshold(alarm); }

		void SetPalleteImage(int index);

		void SetPathToClassifier(const TCHAR* path) { _FaceDetector.SetPathClasiffier(path); }

		void SetAutoSize(int left, int top);

		void SetTimerIntervalForRecord(unsigned int tim);

		void SetMethodForRecord(int index) { if(index == 0 || index == 1) _record.SetMetod(index); }

		// 0 absolute, 1 relative
		void SetTemperatureMethod(int index);

		void SetPathToRecord(const TCHAR* path);

		void SetPathToAlarmSaving(const TCHAR* path);

		void SetAlarmImageSaving() { _isAlaramSavingOn.first = true; }
		void ResetAlarmImageSaving() { _isAlaramSavingOn = { false,false }; _numOfAlarmImages = 1; }

		void SetRecordOnlyFaceDetected() { _RecordOnlyAreFaceDetected = true; }
		void ResetRecordOnlyFaceDetected() { _RecordOnlyAreFaceDetected = false; }

		void SetWidthVideo(int width);
		void SetHeightVideo(int height);

		void SetMinRangeTemperature(float min);
		void SetMaxRangeTemperature(float max);

		void SetAutoSize() { _AutoSize = true; }
		void ResetAutoSize() { _AutoSize = false; }

		void SetRecordFromCamera() { _RecordFromCameraAllowed = true; }
		void ResetRecordFromCamera() { _RecordFromCameraAllowed = false; }

		std::pair<float, float> GetMinMaxTemperature();

		bool isAutoSizeSet() { return _AutoSize; }

		bool isFaceDetectionOn() { return _isFaceDetection; }

		bool isAlarmSavingOn() { return _isAlaramSavingOn.first; }

		bool isAlarmPathSet() { return _isAlaramSavingOn.second; }

		bool isDrawingRectOn() { return _drawingRectIsOn; }

		bool isPaintingRectangleWithMouse() { return _drawRegion.isPainting(); }

		bool isSetManualTemperatureRange() { if (_display) { return _display->isSetManualRangeTemperature(); } else return false; }

	private:

		bool _RecordFromCameraAllowed;

		bool _AutoSize;

		bool _TempRelative;

		bool _isFaceDetection;

		bool _RecordOnlyAreFaceDetected;

		float _TemperatureOfRegion;

		int _intervalOfRecord;

		std::pair<bool,bool> _isAlaramSavingOn;

		size_t _numOfAlarmImages;

		bool _FaceDetectionStreaming;

		bool _RecordReadWriteStreaming;

		bool _drawingRectIsOn;

		TCHAR _pathToAlarmImage[MAX_PATH];

		thermoCamera::Timer _timer;

		thermoCamera::BMPRecord _record;

		thermoCamera::FaceDetector _FaceDetector;

		AccessLevel _eAccess;

		Playback _ePlayback;

		VideoCapture** _device;

		VideoDisplay* _display;

		drw::DrawRectangle _drawRegion;

		std::vector<RECT> _sizeOfVideoDisplay;

		SIZE _sizeOfImage;

		std::thread* _StreamingThread;

		std::thread* _RecordingThread;

		std::thread* _FaceDetectionThread;

		std::thread* _RecordReadWriteThread;

		void _setDisplay(int left, int right, int width, int height);

		bool _setSizeOfImage();

		void _SettingDisplayWithWindow(const HWND& hWnd, int index,const int& widthOut, const int& heightOut);

		bool _StartDevice();

		bool _isDisplaySet();

		bool _Play();

		bool _Stop();

		bool _Pause();

		bool _RunThreadPlay();

		bool _StopThreadPlay();

		bool _RunThreadFaceDetection();

		bool _StopThreadFaceDetection();

		bool _RunThreadRecord();

		bool _StopThreadRecord();

		void _StreamVideo();

		void _StreamFromRecordVideo();

		void _FaceDetection();

		void _RecordData();

		bool _isDevicePlay();

		bool _isDevicePause();

		bool _isDeviceStop();

		bool _InitDevice(VideoCapture& device) { return  (device.Initialize()) ? true : false; }

		void _RemoveDevice(); 

		bool _CheckInputParam(int& left, int& top, int& width, int& height);

		bool _isAlreadyInit() { return (_eAccess == AccessLevel::allowed) ? true : false; }

		bool _isAccessForbidden() { return (_eAccess == AccessLevel::forbidden) ? true : false; }

		bool _isPlaybackFromCamera() { return (_ePlayback == Playback::fromCamera) ? true : false; }

		bool _isRecordPlay() { 
			bool result =  (_record.getFlag() == 0) ? true : false; 
			return result;
		}

		bool _isRecordPause() { return (_record.getFlag() == 1) ? true : false; }

		bool _isRecordStop() { return (_record.getFlag() == 2) ? true : false; }

		bool _isFaceDetectionStreaming() { return _FaceDetectionStreaming; }

		void _setAccessAllowed() { _eAccess = AccessLevel::allowed; }

		void _setAccessForbidden() { _eAccess = AccessLevel::forbidden; }

		void _SetFlagPlay() { (*_device)->eFlag = thermoCamera::StateFlag::play; }

		void _AlreadyInit() { throw MyError::alreadyInit; }

		void _DeviceNotInit() { throw MyError::notInit; }

		void _BadInput() { throw MyError::badInput; }

		void _AccessDeniedEmpty() { throw thermoCamera::MyError::deviceIsEmpty; }

		void _VideoIsDeleted() { throw thermoCamera::MyError::videoIsDeleted; }

		void _AccessDenied() { throw thermoCamera::MyError::accessDenied; }

		void _FirstConfigureDevice() { throw thermoCamera::MyError::deviceIsNotConfig; }

		void _BadSizeOfInputImage() { throw thermoCamera::MyError::badSizeOfVideo; }

		void _CannotPlayVideo() { throw thermoCamera::MyError::cannotOpenFile; }

		void _CannotStopVideo() { throw thermoCamera::MyError::cannotOpenFile; }

		void _CannotDrawOutput() const { throw thermoCamera::MyError::cannotDrawOutput; }
	};

}

#endif
