/*
 * Windows Dynamic Dialog Builder framework
 * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
 */

#pragma once

#include <algorithm>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include <cstdint>
#include "windowsh_wrapper.h"
#include "convert.h"

template<typename T> struct Point
{
	T x, y;

	Point() : x(), y() { }
	Point(T X, T Y) : x(X), y(Y) { }
};

template<typename T> struct Size
{
	T width, height;

	Size() : width(), height() { }
	Size(T Width, T Height) : width(Width), height(Height) { }
};

template<typename T> struct Rect
{
	Point<T> position;
	Size<T> size;

	Rect() : position(), size() { }
	Rect(const Point<T> &Position, const Size<T> &Sz) : position(Position), size(Sz) { }
	Rect(T X, T Y, T Width, T Height) : position(X, Y), size(Width, Height) { }
};

class RelativePosition
{
public:
	Point<short> relativePosition;
	enum class BaseType
	{
		ToParent,
		ToSibling
	} type;
	enum class PositionType
	{
		FromTop,
		FromBottom,
		FromLeft,
		FromRight,
		FromTopLeft,
		FromBottomLeft,
		FromTopRight,
		FromBottomRight
	} positionType;

	RelativePosition(const Point<short> &RelPosition, BaseType Type, PositionType PosType) : relativePosition(RelPosition), type(Type), positionType(PosType) { }
	virtual ~RelativePosition() { }
	virtual RelativePosition *Clone() const = 0;
	Point<short> CalculatePosition(const Rect<short> &child, const Rect<short> &other);
};

class RelativePositionToParent : public RelativePosition
{
public:
	RelativePositionToParent(const Point<short> &RelPosition, PositionType PosType) : RelativePosition(RelPosition, BaseType::ToParent, PosType) { }
	RelativePositionToParent *Clone() const override { return new RelativePositionToParent(this->relativePosition, this->positionType); }
};

class RelativePositionToSibling : public RelativePosition
{
public:
	short siblingsBack;

	RelativePositionToSibling(const Point<short> &RelPosition, PositionType PosType, short SiblingsBack = 1) : RelativePosition(RelPosition, BaseType::ToSibling, PosType), siblingsBack(SiblingsBack) { }
	RelativePositionToSibling *Clone() const override { return new RelativePositionToSibling(this->relativePosition, this->positionType, this->siblingsBack); }
};

enum class DialogControlType
{
	None,
	Group,
	EditBox,
	Label,
	CheckBox,
	Button,
	ListBox,
	ComboBox
};

class DialogTemplate;

class DialogBuilder
{
protected:
	friend class DialogTemplate;
	std::wstring title, fontName;
	std::uint32_t style, exstyle;
	std::uint16_t fontSizeInPts;
	Size<short> size;
	bool resetControls;
public:
	DialogBuilder() : title(L""), fontName(L""), style(0), exstyle(0), fontSizeInPts(0), size(), resetControls(false) { }
	DialogBuilder &WithTitle(const std::wstring &Title) { this->title = Title; return *this; }
	DialogBuilder &WithFont(const std::wstring &FontName, std::uint16_t FontSizeInPts) { this->fontName = FontName; this->fontSizeInPts = FontSizeInPts; return *this; }
	DialogBuilder &WithSize(short Width, short Height) { this->size.width = Width; this->size.height = Height; return *this; }
	DialogBuilder &WithSize(const Size<short> &Sz) { this->size = Sz; return *this; }
	DialogBuilder &ResetControls(bool Reset = true) { this->resetControls = Reset; return *this; }
	DialogBuilder &IsOverlapped() { this->style &= ~(WS_OVERLAPPED | WS_POPUP | WS_CHILD); this->style |= WS_OVERLAPPED; return *this; }
	DialogBuilder &IsPopup() { this->style &= ~(WS_OVERLAPPED | WS_POPUP | WS_CHILD); this->style |= WS_POPUP; return *this; }
	DialogBuilder &IsChild() { this->style &= ~(WS_OVERLAPPED | WS_POPUP | WS_CHILD); this->style |= WS_CHILD; return *this; }
	DialogBuilder &WithBorder(bool Border = true) { if (Border) this->style |= WS_BORDER; else this->style &= ~WS_BORDER; return *this; }
	DialogBuilder &WithDialogFrame(bool DialogFrame = true) { if (DialogFrame) this->style |= WS_DLGFRAME; else this->style &= ~WS_DLGFRAME; return *this; }
	DialogBuilder &WithVerticalScrollbar(bool VerticalScrollbar = true) { if (VerticalScrollbar) this->style |= WS_VSCROLL; else this->style &= ~WS_VSCROLL; return *this; }
	DialogBuilder &WithHorizontalScrollbar(bool HorizontalScrollbar = true) { if (HorizontalScrollbar) this->style |= WS_HSCROLL; else this->style &= ~WS_HSCROLL; return *this; }
	DialogBuilder &WithSystemMenu(bool SystemMenu = true) { if (SystemMenu) this->style |= WS_SYSMENU; else this->style &= ~WS_SYSMENU; return *this; }
	DialogBuilder &WithSizingBorder(bool SizingBorder = true) { if (SizingBorder) this->style |= WS_THICKFRAME; else this->style &= ~WS_THICKFRAME; return *this; }
	DialogBuilder &WithMinimizeBox(bool MinimizeBox = true) { if (MinimizeBox) this->style |= WS_MINIMIZEBOX; else this->style &= ~WS_MINIMIZEBOX; return *this; }
	DialogBuilder &WithMaximizeBox(bool MaximizeBox = true) { if (MaximizeBox) this->style |= WS_MAXIMIZEBOX; else this->style &= ~WS_MAXIMIZEBOX; return *this; }
	DialogBuilder &WithDialogModalFrame(bool DialogModalFrame = true) { if (DialogModalFrame) this->exstyle |= WS_EX_DLGMODALFRAME; else this->exstyle &= ~WS_EX_DLGMODALFRAME; return *this; }
#if WINVER >= 0x0400
	DialogBuilder &WithRaisedEdge(bool RaisedEdge = true) { if (RaisedEdge) this->exstyle |= WS_EX_WINDOWEDGE; else this->exstyle &= ~WS_EX_WINDOWEDGE; return *this; }
	DialogBuilder &WithSunkenEdge(bool SunkenEdge = true) { if (SunkenEdge) this->exstyle |= WS_EX_CLIENTEDGE; else this->exstyle &= ~WS_EX_CLIENTEDGE; return *this; }
	DialogBuilder &WithContextHelp(bool ContextHelp = true) { if (ContextHelp) this->exstyle |= WS_EX_CONTEXTHELP; else this->exstyle &= ~WS_EX_CONTEXTHELP; return *this; }
	DialogBuilder &IsControlWindow(bool ControlWindow = true) { if (ControlWindow) this->style |= DS_CONTROL; else this->style &= ~DS_CONTROL; return *this; }
	DialogBuilder &IsCentered(bool Centered = true) { if (Centered) this->style |= DS_CENTER; else this->style &= ~DS_CENTER; return *this; }
#endif
};

template<class T> class DialogControlBuilder
{
protected:
	friend class DialogTemplate;
	DialogControlType controlType;
	std::uint32_t style, exstyle;
	Rect<short> rect;
	short id;
	int index;
	std::unique_ptr<RelativePosition> relativePosition;

	T &me() { return dynamic_cast<T &>(*this); }
public:
	DialogControlBuilder(DialogControlType Type = DialogControlType::None) : controlType(Type), style(0), exstyle(0), rect(), id(-1), index(-1), relativePosition() { }
	virtual ~DialogControlBuilder() { }
	T &WithPosition(short X, short Y) { this->rect.position.x = X; this->rect.position.y = Y; return this->me(); }
	T &WithPosition(const Point<short> &Position) { this->rect.position = Position; return this->me(); }
	T &WithRelativePositionToParent(RelativePosition::PositionType PosType, const Point<short> &RelativePosition)
	{
		this->relativePosition.reset(new RelativePositionToParent(RelativePosition, PosType));
		return this->me();
	}
	T &WithRelativePositionToSibling(RelativePosition::PositionType PosType, const Point<short> &RelativePosition, short SiblingsBack = 1)
	{
		this->relativePosition.reset(new RelativePositionToSibling(RelativePosition, PosType, SiblingsBack));
		return this->me();
	}
	T &WithSize(short Width, short Height) { this->rect.size.width = Width; this->rect.size.height = Height; return this->me(); }
	T &WithSize(const Size<short> &Sz) { this->rect.size = Sz; return this->me(); }
	T &WithID(short ID) { this->id = ID; return this->me(); }
	T &AtIndex(int Index) { this->index = Index; return this->me(); }
	T &IsDisabled(bool Disabled = true) { if (Disabled) this->style |= WS_DISABLED; else this->style &= ~WS_DISABLED; return this->me(); }
	T &WithBorder(bool ThinBorder = true) { if (ThinBorder) this->style |= WS_BORDER; else this->style &= ~WS_BORDER; return this->me(); }
	T &WithVerticalScrollbar(bool VerticalScrollbar = true) { if (VerticalScrollbar) this->style |= WS_VSCROLL; else this->style &= ~WS_VSCROLL; return this->me(); }
	T &WithHorizontalScrollbar(bool HorizontalScrollbar = true) { if (HorizontalScrollbar) this->style |= WS_HSCROLL; else this->style &= ~WS_HSCROLL; return this->me(); }
	T &WithTabStop(bool TabStop = true) { if (TabStop) this->style |= WS_TABSTOP; else this->style &= ~WS_TABSTOP; return this->me(); }
#if WINVER >= 0x0400
	T &WithRaisedEdge(bool RaisedEdge = true) { if (RaisedEdge) this->exstyle |= WS_EX_WINDOWEDGE; else this->exstyle &= ~WS_EX_WINDOWEDGE; return this->me(); }
	T &WithSunkenEdge(bool SunkenEdge = true) { if (SunkenEdge) this->exstyle |= WS_EX_CLIENTEDGE; else this->exstyle &= ~WS_EX_CLIENTEDGE; return this->me(); }
#endif
};

class DialogGroupBuilder : public DialogControlBuilder<DialogGroupBuilder>
{
protected:
	friend class DialogTemplate;
	std::wstring groupName;
public:
	DialogGroupBuilder(const std::wstring &newGroupName) : DialogControlBuilder(DialogControlType::Group), groupName(newGroupName) { }
};

template<class T> class DialogInGroupBuilder : public DialogControlBuilder<T>
{
protected:
	friend class DialogTemplate;
	std::wstring groupName;
public:
	DialogInGroupBuilder(DialogControlType Type) : DialogControlBuilder<T>(Type), groupName(L"") { }
	T &InGroup(const std::wstring &GroupName) { this->groupName = GroupName; return this->me(); }
};

class DialogEditBoxBuilder : public DialogInGroupBuilder<DialogEditBoxBuilder>
{
protected:
	friend class DialogTemplate;
public:
	DialogEditBoxBuilder() : DialogInGroupBuilder(DialogControlType::EditBox) { }
	DialogEditBoxBuilder &IsLeftJustified() { this->style &= ~(ES_LEFT | ES_CENTER | ES_RIGHT); this->style |= ES_LEFT; return this->me(); }
	DialogEditBoxBuilder &IsCenterJustified() { this->style &= ~(ES_LEFT | ES_CENTER | ES_RIGHT); this->style |= ES_CENTER; return this->me(); }
	DialogEditBoxBuilder &IsRightJustified() { this->style &= ~(ES_LEFT | ES_CENTER | ES_RIGHT); this->style |= ES_RIGHT; return this->me(); }
	DialogEditBoxBuilder &IsMultiline(bool Multiline = true) { if (Multiline) this->style |= ES_MULTILINE; else this->style &= ~ES_MULTILINE; return this->me(); }
	DialogEditBoxBuilder &WithAllUppercase(bool AllUppercase = true) { if (AllUppercase) this->style |= ES_UPPERCASE; else this->style &= ~ES_UPPERCASE; return this->me(); }
	DialogEditBoxBuilder &WithAllLowercase(bool AllLowercase = true) { if (AllLowercase) this->style |= ES_LOWERCASE; else this->style &= ~ES_LOWERCASE; return this->me(); }
	DialogEditBoxBuilder &IsPasswordInput(bool PasswordInput = true) { if (PasswordInput) this->style |= ES_PASSWORD; else this->style &= ~ES_PASSWORD; return this->me(); }
	DialogEditBoxBuilder &WithAutoVScroll(bool AutoVScroll = true) { if (AutoVScroll) this->style |= ES_AUTOVSCROLL; else this->style &= ~ES_AUTOVSCROLL; return this->me(); }
	DialogEditBoxBuilder &WithAutoHScroll(bool AutoHScroll = true) { if (AutoHScroll) this->style |= ES_AUTOHSCROLL; else this->style &= ~ES_AUTOHSCROLL; return this->me(); }
	DialogEditBoxBuilder &WithDontHideSelection(bool DontHideSelection = true) { if (DontHideSelection) this->style |= ES_NOHIDESEL; else this->style &= ~ES_NOHIDESEL; return this->me(); }
	DialogEditBoxBuilder &IsReadOnly(bool ReadOnly = true) { if (ReadOnly) this->style |= ES_READONLY; else this->style &= ~ES_READONLY; return this->me(); }
	DialogEditBoxBuilder &WithWantReturn(bool WantReturn = true) { if (WantReturn) this->style |= ES_WANTRETURN; else this->style &= ~ES_WANTRETURN; return this->me(); }
};

template<class T> class DialogControlWithLabelBuilder : public DialogInGroupBuilder<T>
{
protected:
	friend class DialogTemplate;
	std::wstring label;
public:
	DialogControlWithLabelBuilder(DialogControlType Type, const std::wstring &Label) : DialogInGroupBuilder<T>(Type), label(Label) { }
};

class DialogLabelBuilder : public DialogControlWithLabelBuilder<DialogLabelBuilder>
{
protected:
	friend class DialogTemplate;
public:
	DialogLabelBuilder(const std::wstring &Label) : DialogControlWithLabelBuilder(DialogControlType::Label, Label) { }
	DialogLabelBuilder &IsLeftJustified(bool WithWordWrap = false)
	{
		this->style &= ~(SS_LEFT | SS_CENTER | SS_RIGHT | SS_LEFTNOWORDWRAP);
		if (WithWordWrap)
			this->style |= SS_LEFTNOWORDWRAP;
		else
			this->style |= SS_LEFT;
		return this->me();
	}
	DialogLabelBuilder &IsCenterJustified() { this->style &= ~(SS_LEFT | SS_CENTER | SS_RIGHT | SS_LEFTNOWORDWRAP); this->style |= SS_CENTER; return this->me(); }
	DialogLabelBuilder &IsRightJustified() { this->style &= ~(SS_LEFT | SS_CENTER | SS_RIGHT | SS_LEFTNOWORDWRAP); this->style |= SS_RIGHT; return this->me(); }
	DialogLabelBuilder &WithAmpsNotTranslated(bool AmpsNotTranslated = true) { if (AmpsNotTranslated) this->style |= SS_NOPREFIX; else this->style &= ~SS_NOPREFIX; return this->me(); }
#if WINVER >= 0x0400
	DialogLabelBuilder &WithNotify(bool Notify = true) { if (Notify) this->style |= SS_NOTIFY; else this->style &= ~SS_NOTIFY; return this->me(); }
#endif
};

template<class T> class DialogButtonBaseBuilder : public DialogControlWithLabelBuilder<T>
{
protected:
	friend class DialogTemplate;
public:
	DialogButtonBaseBuilder(DialogControlType Type, const std::wstring &Label) : DialogControlWithLabelBuilder<T>(Type, Label) { }
#if WINVER >= 0x0400
	T &IsLeftJustified(bool LeftJustified = true)
	{
		if (LeftJustified)
		{
			this->style |= BS_LEFT;
			this->style &= ~BS_RIGHT;
		}
		else
			this->style &= ~BS_LEFT;
		return this->me();
	}
	T &IsRightJustified(bool RightJustified = true)
	{
		if (RightJustified)
		{
			this->style |= BS_RIGHT;
			this->style &= ~BS_LEFT;
		}
		else
			this->style &= ~BS_RIGHT;
		return this->me();
	}
	T &IsCenterJustified(bool CenterJustified = true) { if (CenterJustified) this->style |= BS_CENTER; else this->style &= ~BS_CENTER; return this->me(); }
	T &WithVerticalTop(bool VerticalTop = true)
	{
		if (VerticalTop)
		{
			this->style |= BS_TOP;
			this->style &= ~BS_BOTTOM;
		}
		else
			this->style &= ~BS_TOP;
		return this->me();
	}
	T &WithVerticalBottom(bool VerticalBottom = true)
	{
		if (VerticalBottom)
		{
			this->style |= BS_BOTTOM;
			this->style &= ~BS_TOP;
		}
		else
			this->style &= ~BS_BOTTOM;
		return this->me();
	}
	T &WithVerticalCenter(bool VerticalCenter = true) { if (VerticalCenter) this->style |= BS_VCENTER; else this->style &= ~BS_VCENTER; return this->me(); }
	T &IsMultiline(bool Multiline = true) { if (Multiline) this->style |= BS_MULTILINE; else this->style &= ~BS_MULTILINE; return this->me(); }
	T &WithNotify(bool Notify = true) { if (Notify) this->style |= BS_NOTIFY; else this->style &= ~BS_NOTIFY; return this->me(); }
	T &IsFlat(bool Flat = true) { if (Flat) this->style |= BS_FLAT; else this->style &= ~BS_FLAT; return this->me(); }
#endif
};

class DialogButtonBuilder : public DialogButtonBaseBuilder<DialogButtonBuilder>
{
protected:
	friend class DialogTemplate;
public:
	DialogButtonBuilder(const std::wstring &Label) : DialogButtonBaseBuilder(DialogControlType::Button, Label) { }
	DialogButtonBuilder &IsDefault(bool Default = true) { if (Default) this->style |= BS_DEFPUSHBUTTON; else this->style &= ~BS_DEFPUSHBUTTON; return this->me(); }
};

class DialogCheckBoxBuilder : public DialogButtonBaseBuilder<DialogCheckBoxBuilder>
{
protected:
	friend class DialogTemplate;
public:
	DialogCheckBoxBuilder(const std::wstring &Label) : DialogButtonBaseBuilder(DialogControlType::CheckBox, Label) { this->style |= BS_AUTOCHECKBOX; }
	DialogCheckBoxBuilder &WithTextOnLeft(bool TextOnLeft = true) { if (TextOnLeft) this->style |= BS_LEFTTEXT; else this->style &= ~BS_LEFTTEXT; return this->me(); }
	DialogCheckBoxBuilder &LikePushButton(bool PushButton = true) { if (PushButton) this->style |= BS_PUSHLIKE; else this->style &= ~BS_PUSHLIKE; return this->me(); }
};

class DialogListBoxBuilder : public DialogInGroupBuilder<DialogListBoxBuilder>
{
protected:
	friend class DialogTemplate;
public:
	DialogListBoxBuilder() : DialogInGroupBuilder(DialogControlType::ListBox) { }
	DialogListBoxBuilder &WithNotify(bool Notify = true) { if (Notify) this->style |= LBS_NOTIFY; else this->style &= ~LBS_NOTIFY; return this->me(); }
	DialogListBoxBuilder &WithSort(bool Sort = true) { if (Sort) this->style |= LBS_SORT; else this->style &= ~LBS_SORT; return this->me(); }
	DialogListBoxBuilder &WithMultipleSelect(bool MultipleSelect = true) { if (MultipleSelect) this->style |= LBS_MULTIPLESEL; else this->style &= ~LBS_MULTIPLESEL; return this->me(); }
	DialogListBoxBuilder &WithExactHeight(bool ExactHeight = true) { if (ExactHeight) this->style |= LBS_NOINTEGRALHEIGHT; else this->style &= ~LBS_NOINTEGRALHEIGHT; return this->me(); }
	DialogListBoxBuilder &WithMultipleColumns(bool MultipleColumns = true) { if (MultipleColumns) this->style |= LBS_MULTICOLUMN; else this->style &= ~LBS_MULTICOLUMN; return this->me(); }
	DialogListBoxBuilder &WithExtendedSelect(bool ExtendedSelect = true) { if (ExtendedSelect) this->style |= LBS_EXTENDEDSEL; else this->style &= ~LBS_EXTENDEDSEL; return this->me(); }
	DialogListBoxBuilder &WithDisabledNoScroll(bool DisabledNoScroll = true) { if (DisabledNoScroll) this->style |= LBS_DISABLENOSCROLL; else this->style &= ~LBS_DISABLENOSCROLL; return this->me(); }
#if WINVER >= 0x0400
	DialogListBoxBuilder &WithNoSelect(bool NoSelect = true) { if (NoSelect) this->style |= LBS_NOSEL; else this->style &= ~LBS_NOSEL; return this->me(); }
#endif
};

class DialogComboBoxBuilder : public DialogInGroupBuilder<DialogComboBoxBuilder>
{
protected:
	friend class DialogTemplate;
public:
	DialogComboBoxBuilder() : DialogInGroupBuilder(DialogControlType::ComboBox) { }
	DialogComboBoxBuilder &IsSimple() { this->style &= ~(CBS_SIMPLE | CBS_DROPDOWN | CBS_DROPDOWNLIST); this->style |= CBS_SIMPLE; return this->me(); }
	DialogComboBoxBuilder &IsDropDown() { this->style &= ~(CBS_SIMPLE | CBS_DROPDOWN | CBS_DROPDOWNLIST); this->style |= CBS_DROPDOWN; return this->me(); }
	DialogComboBoxBuilder &IsDropDownList() { this->style &= ~(CBS_SIMPLE | CBS_DROPDOWN | CBS_DROPDOWNLIST); this->style |= CBS_DROPDOWNLIST; return this->me(); }
	DialogComboBoxBuilder &WithAutoHScroll(bool AutoHScroll = true) { if (AutoHScroll) this->style |= CBS_AUTOHSCROLL; else this->style &= ~CBS_AUTOHSCROLL; return this->me(); }
	DialogComboBoxBuilder &WithSort(bool Sort = true) { if (Sort) this->style |= CBS_SORT; else this->style &= ~CBS_SORT; return this->me(); }
	DialogComboBoxBuilder &WithExactHeight(bool ExactHeight = true) { if (ExactHeight) this->style |= CBS_NOINTEGRALHEIGHT; else this->style &= ~CBS_NOINTEGRALHEIGHT; return this->me(); }
	DialogComboBoxBuilder &WithDisabledNoScroll(bool DisabledNoScroll = true) { if (DisabledNoScroll) this->style |= CBS_DISABLENOSCROLL; else this->style &= ~CBS_DISABLENOSCROLL; return this->me(); }
#if WINVER >= 0x0400
	DialogComboBoxBuilder &WithAllLowercase(bool AllLowercase = true) { if (AllLowercase) this->style |= CBS_LOWERCASE; else this->style &= ~CBS_LOWERCASE; return this->me(); }
	DialogComboBoxBuilder &WithAllUppercase(bool AllUppercase = true) { if (AllUppercase) this->style |= CBS_UPPERCASE; else this->style &= ~CBS_UPPERCASE; return this->me(); }
#endif
};

class DialogTemplate
{
	class DialogControl;

	typedef std::vector<std::unique_ptr<DialogControl>> Controls;

	class DialogControl
	{
	protected:
		DialogControlType controlType;
		std::uint32_t style, exstyle;
		Rect<short> rect;
		short id;
		std::unique_ptr<RelativePosition> relativePosition;

		friend class DialogTemplate;
		DialogControl() : controlType(DialogControlType::None), style(0), exstyle(0), rect(), id(-1), relativePosition() { }
		DialogControl(const DialogControl &control) : controlType(control.controlType), style(control.style), exstyle(control.exstyle), rect(control.rect), id(control.id),
			relativePosition(control.relativePosition ? control.relativePosition->Clone() : nullptr) { }
		DialogControl &operator=(const DialogControl &control)
		{
			this->controlType = control.controlType;
			this->style = control.style;
			this->exstyle = control.exstyle;
			this->rect = control.rect;
			this->id = control.id;
			if (control.relativePosition)
				this->relativePosition.reset(control.relativePosition->Clone());
			else
				this->relativePosition.reset();

			return *this;
		}
	public:
		virtual ~DialogControl() { }
		template<typename Control, typename Builder> static std::unique_ptr<Control> CreateControl(const DialogControlBuilder<Builder> &builder)
		{
			auto control = std::unique_ptr<Control>(new Control());

			control->controlType = builder.controlType;
			control->style = builder.style;
			control->exstyle = builder.exstyle;
			control->rect = builder.rect;
			control->id = builder.id;
			if (builder.relativePosition)
				control->relativePosition.reset(builder.relativePosition->Clone());

			return control;
		}
		virtual std::uint16_t GetControlCount() const { return 1; }
		virtual short GetControlHeight() const { return this->rect.size.height; }
		virtual std::vector<std::uint8_t> GenerateControlTemplate() const = 0;
		virtual DialogControl *Clone() const = 0;
	};

	class DialogGroup : public DialogControl
	{
	protected:
		DialogTemplate::Controls controls;
		std::wstring groupName;

		friend class DialogTemplate;
		friend class DialogControl;
		DialogGroup() : DialogControl(), controls(), groupName(L"") { }
		DialogGroup(const DialogGroup &control) : DialogControl(control), controls(), groupName(control.groupName)
		{
			std::for_each(control.controls.begin(), control.controls.end(), [&](const std::unique_ptr<DialogControl> &ctl) { this->controls.push_back(std::unique_ptr<DialogControl>(ctl->Clone())); });
		}
		DialogGroup &operator=(const DialogGroup &control)
		{
			DialogControl::operator=(control);

			this->controls.clear();
			std::for_each(control.controls.begin(), control.controls.end(), [&](const std::unique_ptr<DialogControl> &ctl) { this->controls.push_back(std::unique_ptr<DialogControl>(ctl->Clone())); });

			this->groupName = control.groupName;

			return *this;
		}
	public:
		static std::unique_ptr<DialogGroup> CreateControl(const DialogControlBuilder<DialogGroupBuilder> &builder)
		{
			auto control = DialogControl::CreateControl<DialogGroup>(builder);

			control->groupName = dynamic_cast<const DialogGroupBuilder &>(builder).groupName;

			return control;
		}
		void CalculatePositions(bool doRightAndBottom = false);
		void CalculateSize();
		std::uint16_t GetControlCount() const override;
		std::vector<std::uint8_t> GenerateControlTemplate() const override;
		DialogGroup *Clone() const override { return new DialogGroup(*this); }
	};

	class DialogControlWithoutLabel : public DialogControl
	{
	protected:
		std::uint16_t type;

		friend class DialogTemplate;
		friend class DialogControl;
		DialogControlWithoutLabel() : DialogControl(), type(0) { }
		DialogControlWithoutLabel(const DialogControlWithoutLabel &control) : DialogControl(control), type(control.type) { }
		DialogControlWithoutLabel &operator=(const DialogControlWithoutLabel &control)
		{
			DialogControl::operator=(control);

			this->type = control.type;

			return *this;
		}
	public:
		template<typename Control, typename Builder> static std::unique_ptr<Control> CreateControl(const DialogControlBuilder<Builder> &builder, std::uint16_t Type)
		{
			auto control = DialogControl::CreateControl<Control>(builder);

			control->type = Type;

			return control;
		}
		virtual std::vector<std::uint8_t> GenerateControlTemplate() const override;
		virtual DialogControlWithoutLabel *Clone() const override{ return new DialogControlWithoutLabel(*this); }
	};

	class DialogControlWithLabel : public DialogControl
	{
	protected:
		std::uint16_t type;
		std::wstring label;

		friend class DialogTemplate;
		friend class DialogControl;
		DialogControlWithLabel() : DialogControl(), type(0), label(L"") { }
		DialogControlWithLabel(const DialogControlWithLabel &control) : DialogControl(control), type(control.type), label(control.label) { }
		DialogControlWithLabel &operator=(const DialogControlWithLabel &control)
		{
			DialogControl::operator=(control);

			this->type = control.type;
			this->label = control.label;

			return *this;
		}
	public:
		template<typename Control, typename Builder> static std::unique_ptr<Control> CreateControl(const DialogControlBuilder<Builder> &builder, std::uint16_t Type)
		{
			auto control = DialogControl::CreateControl<Control>(builder);

			control->type = Type;
			control->label = dynamic_cast<const DialogControlWithLabelBuilder<Builder> &>(builder).label;

			return control;
		}
		virtual std::vector<std::uint8_t> GenerateControlTemplate() const override;
		virtual DialogControlWithLabel *Clone() const override { return new DialogControlWithLabel(*this); }
	};

	class DialogEditBox : public DialogControlWithoutLabel
	{
	protected:
		friend class DialogTemplate;
		friend class DialogControl;
		DialogEditBox() : DialogControlWithoutLabel() { }
	public:
		static std::unique_ptr<DialogEditBox> CreateControl(const DialogControlBuilder<DialogEditBoxBuilder> &builder)
		{
			return DialogControlWithoutLabel::CreateControl<DialogEditBox>(builder, 0x0081);
		}
	};

	class DialogLabel : public DialogControlWithLabel
	{
	protected:
		friend class DialogTemplate;
		friend class DialogControl;
		DialogLabel() : DialogControlWithLabel() { }
	public:
		static std::unique_ptr<DialogLabel> CreateControl(const DialogControlBuilder<DialogLabelBuilder> &builder)
		{
			return DialogControlWithLabel::CreateControl<DialogLabel>(builder, 0x0082);
		}
	};

	class DialogButton : public DialogControlWithLabel
	{
	protected:
		friend class DialogTemplate;
		friend class DialogControl;
		DialogButton() : DialogControlWithLabel() { }
	public:
		template<typename Builder> static std::unique_ptr<DialogButton> CreateControl(const DialogControlBuilder<Builder> &builder)
		{
			return DialogControlWithLabel::CreateControl<DialogButton>(builder, 0x0080);
		}
	};

	class DialogListBox : public DialogControlWithoutLabel
	{
	protected:
		friend class DialogTemplate;
		friend class DialogControl;
		DialogListBox() : DialogControlWithoutLabel() { }
	public:
		static std::unique_ptr<DialogListBox> CreateControl(const DialogControlBuilder<DialogListBoxBuilder> &builder)
		{
			return DialogControlWithoutLabel::CreateControl<DialogListBox>(builder, 0x0083);
		}
	};

	class DialogComboBox : public DialogControlWithoutLabel
	{
	protected:
		friend class DialogTemplate;
		friend class DialogControl;
		DialogComboBox() : DialogControlWithoutLabel() { }
	public:
		static std::unique_ptr<DialogComboBox> CreateControl(const DialogControlBuilder<DialogComboBoxBuilder> &builder)
		{
			return DialogControlWithoutLabel::CreateControl<DialogComboBox>(builder, 0x0085);
		}
		short GetControlHeight() const override { return (this->style & CBS_SIMPLE && !(this->style & CBS_DROPDOWNLIST)) ? this->rect.size.height : 14; }
	};

	std::wstring title;
	std::uint32_t style, exstyle;
	std::wstring fontName;
	std::uint16_t fontSizeInPts;
	Size<short> size;
	DialogTemplate::Controls controls;
	std::vector<std::uint8_t> templateData;

	template<typename Builder> void AddControlToGroup(std::unique_ptr<DialogControl> &&control, const DialogControlBuilder<Builder> &builder)
	{
		const auto &groupBuilder = dynamic_cast<const DialogInGroupBuilder<Builder> &>(builder);
		if (groupBuilder.groupName.empty())
		{
			if (builder.index == -1)
				this->controls.push_back(std::move(control));
			else
				this->controls.insert(this->controls.begin() + builder.index, std::move(control));
		}
		else
		{
			for (auto curr = this->controls.begin(), end = this->controls.end(); curr != end; ++curr)
			{
				if ((*curr)->controlType != DialogControlType::Group)
					continue;
				DialogGroup *group = dynamic_cast<DialogGroup *>(curr->get());
				if (group->groupName == groupBuilder.groupName)
				{
					if (builder.index == -1)
						group->controls.push_back(std::move(control));
					else
						group->controls.insert(group->controls.begin() + builder.index, std::move(control));
					return;
				}
			}
			throw std::runtime_error("Group " + ConvertFuncs::WStringToString(groupBuilder.groupName) + " was not found.");
		}
	}
	std::uint16_t GetTotalControlCount() const;
	bool CalculateControlPosition(short index, bool doRightAndBottom = false);
	void CalculateSize();
public:
	DialogTemplate() : title(L""), style(0), exstyle(0), fontName(L""), fontSizeInPts(0), size(), controls(), templateData() { }
	DialogTemplate(const DialogBuilder &builder) : title(builder.title), style(builder.style), exstyle(builder.exstyle), fontName(builder.fontName), fontSizeInPts(builder.fontSizeInPts),
		size(builder.size), controls(), templateData() { }
	DialogTemplate(const DialogTemplate &dlg) : title(dlg.title), style(dlg.style), exstyle(dlg.style), fontName(dlg.fontName), fontSizeInPts(dlg.fontSizeInPts), size(dlg.size),
		controls(), templateData()
	{
		std::for_each(dlg.controls.begin(), dlg.controls.end(), [&](const std::unique_ptr<DialogControl> &control) { this->controls.push_back(std::unique_ptr<DialogControl>(control->Clone())); });
	}
	DialogTemplate &operator=(const DialogBuilder &builder)
	{
		this->title = builder.title;
		this->style = builder.style;
		this->exstyle = builder.exstyle;
		this->fontName = builder.fontName;
		this->fontSizeInPts = builder.fontSizeInPts;
		this->size = builder.size;
		if (builder.resetControls)
			this->controls.clear();

		return *this;
	}
	DialogTemplate &operator=(const DialogTemplate &dlg)
	{
		this->title = dlg.title;
		this->style = dlg.style;
		this->exstyle = dlg.exstyle;
		this->fontName = dlg.fontName;
		this->fontSizeInPts = dlg.fontSizeInPts;
		this->size = dlg.size;
		this->controls.clear();
		std::for_each(dlg.controls.begin(), dlg.controls.end(), [&](const std::unique_ptr<DialogControl> &control) { this->controls.push_back(std::unique_ptr<DialogControl>(control->Clone())); });

		return *this;
	}
	void AddGroupControl(const DialogControlBuilder<DialogGroupBuilder> &builder);
	void AddEditBoxControl(const DialogControlBuilder<DialogEditBoxBuilder> &builder);
	void AddLabelControl(const DialogControlBuilder<DialogLabelBuilder> &builder);
	void AddCheckBoxControl(const DialogControlBuilder<DialogCheckBoxBuilder> &builder);
	void AddButtonControl(const DialogControlBuilder<DialogButtonBuilder> &builder);
	void AddListBoxControl(const DialogControlBuilder<DialogListBoxBuilder> &builder);
	void AddComboBoxControl(const DialogControlBuilder<DialogComboBoxBuilder> &builder);
	void AutoSize();
	const DLGTEMPLATE *GenerateTemplate();
};