#ifndef __COLOURPICKER_H__
#define __COLOURPICKER_H__

#include <wx/wx.h>
#include <wx/spinctrl.h>
#include <wx/tokenzr.h>
#include <wx/xrc/xmlres.h>
#include <float.h>
#include <vector>

#include "kernel.h"
#include "spinsliderd.h"

namespace VRUT
{
	class NumberInput;

	/**
	* wxDialog for customizing colour
	*
	* Notices:
	* - CIELab contain color that are not visible by human eye and therefore they can't be represented by RGB
	* - conversion from RGB (through CIE XYZ) to temperature is very approximative solution therefore there are discontinueties in conversions (inconsistencies)
	*/
	class ColourDialog : public wxDialog
	{

	public:

		typedef enum _DialogMode
		{
			EE_COLOUR_DIALOG_ALPHA = 0,
			EE_COLOUR_DIALOG_INTENSITY
		} DialogMode;

	private:

		/* LERP(a,b,c) = linear interpolation macro, is 'a' when c == 0.0 and 'b' when c == 1.0 */
		#define LERP(a,b,c) (((b) - (a)) * (c) + (a))

		#define CIE_LAB_EPSILON             (216.0 / 24389.0)		// = 0.008856
		#define CIE_LAB_K                   (24389.0 / 27.0)		// = ~903.3

		#define CIE_STANDARD_ILLUMINANT_C   0.3101, 0.3162			/* For NTSC television */
		#define CIE_STANDARD_ILLUMINANT_D65 0.3127, 0.3291			/* For EBU and SMPTE */
		#define CIE_STANDARD_ILLUMINANT_E   0.33333333, 0.33333333	/* CIE equal-energy illuminant */

		#define GAMMA_REC709   0									/* Rec. 709 */

		/**
		* struct and define for colour profile and converting from rgb to xyz, xyz to rgb, temperature to xyz and xyz to rgb
		*/
		struct ColourSystem {
			wxString name;								/* Colour system name */

			double xRed, yRed;							/* Red chromaticities x, y */
			double xGreen, yGreen;						/* Green chromaticities x, y */
			double xBlue, yBlue;						/* Blue chromaticities x, y */
			double xWhite, yWhite;						/* White point chromaticities x, y */
			double gamma;								/* Gamma correction for colour system */
		};

		typedef struct UVT {
			double  u;
			double  v;
			double  t;
		} UVT;

		/* reciprocal temperature (K) */
		double rtt[31];
		UVT uvt[31];

		NumberInput *redSpinSlider, *greenSpinSlider, *blueSpinSlider;
		NumberInput *hueSpinSlider, *saturationSpinSlider, *valueSpinSlider;
		NumberInput *lSpinSlider, *aSpinSlider, *bSpinSlider;
		//SpinSliderDouble *alphaSpinSlider;
		NumberInput *alphaSpinSlider;
		NumberInput *temperatureSpinSlider;
		wxComboBox *colourProfile;

		wxWindow *actColourPreview, *origColourPreview;
		wxButton *resetButton;

		double r, g, b, alpha; // current values
		double r0, g0, b0, alpha0; // original values

		DialogMode mode;

		/**
		*combo box and help list for change colour profile 
		*/
		wxArrayString             typeStringList;
		std::vector<unsigned int> typeValuesList;
		std::vector<ColourSystem> colourSystemList;
		size_t type;

		void ComboBoxSelected(wxCommandEvent &evt);

		/**
		* function for converting from temperature to XYZ
		* http://www.fourmilab.ch/documents/specrend/specrend.c
		*/
		double blackBodyTemperature;
		double BlackBodySpectrum(double wavelength);

		void SpectrumToXYZ(double *x, double *y, double *z);
		void NormRGB(double *r, double *g, double *b);
		bool ConstrainRGB(double *r, double *g, double *b);
		void TemperatureToRGB();

		/**
		* function for converting from XYZ to RGB and RGB to xyz
		* http://www.brucelindbloom.com/Math.html
		* http://www.brucelindbloom.com/Eqn_XYZ_to_RGB.html
		* http://www.brucelindbloom.com/Eqn_RGB_to_XYZ.html
		* http://www.fourmilab.ch/documents/specrend/specrend.c
		*
		*/
		void XYZtoRGB(struct ColourSystem *cs, double xc, double yc, double zc, double *red, double *green, double *blue);
		void RGBtoXYZ(struct ColourSystem *cs, double red, double green, double blue, double *xc, double *yc, double *zc);

		void HSVtoRGB(double hue, double saturation, double value, double &red, double &green, double &blue);
		void RGBtoHSV(double red, double green, double blue, double &hue, double &saturation, double &value);

		/**
		* sets correct value to Lab spin_slider
		* http://stackoverflow.com/questions/9372626/converting-lab-values-to-rgb-values-in-opencv
		* http://www.brucelindbloom.com/Eqn_Lab_to_XYZ.html
		* http://www.easyrgb.com/index.php?X=CALC#Result
		*/
		void RGBToCIELab(double rL, double gL, double bL, double& L, double& a, double& b);
		void CIELabToRGB(double Lt, double at, double bt, double& red, double& green, double& blue);
		void SetLab();

		/**
		* sets correct value to Temperature spin_slider
		* http://www.brucelindbloom.com/Eqn_XYZ_to_T.html
		*/
		bool XYZtoCorrelatedColorTemperature(double xt, double yt, double zt, double *colorTemperature);
		void SetTemperature();	

		/**
		* Handler converting values from spin_slider values
		*/
		void OnSlider(wxCommandEvent &evt);

		/**
		* handling OK and CANCEL button
		*/
		void OnClick(wxCommandEvent &evt);

		/**
		* sends event on change values
		*/
		void SliderUpdated();

		/**
		* sets correct values to HSV spin_sliders
		*/
		void SetHSV();

		/**
		* updates GUI components to reflect color value
		*/
		void UpdateGUI();

	public:

		ColourDialog(wxWindow *parent, wxWindowID id, const wxString &title, double red, double green, double blue, double a, DialogMode dialogMode = EE_COLOUR_DIALOG_ALPHA);

		~ColourDialog()
		{
			KERNEL->WinPosSizeToVRUTConfig(this, wxT("/ColourDialog/"), false);
		}

		void ResetValue(double red, double green, double blue, double a);

		friend class ColourPicker;
	};


	/**
	* wxWidget for customizing colour
	*/
	class ColourPicker : public wxButton
	{
		wxString strValue;

		double r, g, b, a; // current values

		static ColourDialog *dialog; // Only one dialog can exit at one time
		ColourDialog::DialogMode dialogMode;

		/**
		* opens colour dialog
		*/
		void OnClick(wxMouseEvent &evt);

		/**
		* Handler setting values from colour dialog on change
		*/
		void OnDialog(wxCommandEvent &evt);

		/**
		* Generated event when colour is changed
		*/
		void SendEvent();

		void LocalInit(const wxString &value) 
		{
			r = 0.0;
			g = 0.0;
			b = 0.0;
			a = 0.0;
			strValue = wxT("");

			wxString newValue = value;

			if (newValue.IsEmpty()) //  == wxT(""))
			{
				newValue = wxT("0.0, 0.0, 0.01, 0.0");//workaround to fix problem with Enable (it changes colour to default, but remembers original background and prevent refresh)
			}

			dialogMode = ColourDialog::DialogMode::EE_COLOUR_DIALOG_ALPHA;

			this->SetValue(newValue);
			this->Connect(wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxMouseEventHandler(ColourPicker::OnClick), NULL, this);
		}

	public:

		void SetValue(const wxString &value);

		wxString GetValue()
		{
			return strValue;
		}

		void SetColourDialogMode(ColourDialog::DialogMode dMode)
		{
			dialogMode = dMode;
		}

		ColourPicker() : wxButton()
		{
			LocalInit(wxT(""));
		}

		ColourPicker(wxWindow *parent, wxWindowID id, const wxString &label, const wxString &value) : wxButton(parent, id, label)
		{
			LocalInit(value);
		}

		virtual ~ColourPicker()
		{
			// Clean up color dialog if it belongs to this object.
			if (dialog && dialog->GetParent() == this)
			{
				SAFE_DELETE(dialog);
			}
		}

		bool Create(wxWindow *parent, wxWindowID id, const wxString & label = wxEmptyString, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = 0, const wxValidator & validator = wxDefaultValidator, const wxString & name = wxButtonNameStr) 	
		{
			if ( !wxButton::Create(parent, id, label, pos, size, style, validator, name) )
				return false;

			LocalInit(wxT(""));

			return true;
		}

		DECLARE_DYNAMIC_CLASS(ColourPicker)
	};

	class ColourPickerXmlHandler : public wxXmlResourceHandler
	{
		public:
			// Constructor.
			ColourPickerXmlHandler()
			{
				XRC_ADD_STYLE(wxBU_LEFT);
				XRC_ADD_STYLE(wxBU_RIGHT);
				XRC_ADD_STYLE(wxBU_TOP);
				XRC_ADD_STYLE(wxBU_BOTTOM);
				XRC_ADD_STYLE(wxBU_EXACTFIT);

				AddWindowStyles();
			}

			// Creates the control and returns a pointer to it.
			virtual wxObject *DoCreateResource()
			{
				XRC_MAKE_INSTANCE(control, ColourPicker)

				control->Create(m_parentAsWindow,
					GetID(),
					GetText(wxT("label")),
					GetPosition(), GetSize(),
					GetStyle(),
					wxDefaultValidator,
					GetName()
				);

				if (GetBool(wxT("default"), 0))
					control->SetDefault();

				if ( GetParamNode("bitmap") )
				{
					control->SetBitmap(GetBitmap("bitmap", wxART_BUTTON), GetDirection("bitmapposition"));
				}

				SetupWindow(control);

				return control;
			}

			// Returns true if we know how to create a control for the given node.
			virtual bool CanHandle(wxXmlNode *node)
			{
				// this function tells XRC system that this handler can parse
				// the <object class="MyControl"> tags
				return IsOfClass(node, wxT("ColourPicker"));
			}

		// Register with wxWidgets' dynamic class subsystem.
		DECLARE_DYNAMIC_CLASS(ColourPickerXmlHandler)
	};

}

#endif
