/***********************************************************************
THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY
DEVELOPER: Zihan Chen(vczh)
***********************************************************************/
#include "GacUI.h"
#include "VlppGlrParser.h"
#include "VlppWorkflowLibrary.h"
#include "VlppReflection.h"
#include "VlppOS.h"
#include "Vlpp.h"
#include "VlppRegex.h"
#include "GacUIReflection.h"
#include "VlppWorkflowCompiler.h"
#include "VlppWorkflowRuntime.h"

/***********************************************************************
.\GUIUNITTESTPROTOCOL_SHARED.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/

#ifndef VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_SHARED
#define VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_SHARED


namespace vl::presentation::unittest
{
	struct WindowStyleConfig
	{
		WString						title;
		bool						enabled = true;
		bool						topMost = false;
		bool						showInTaskBar = true;

		bool						customFrameMode = false;
		bool						maximizedBox = true;
		bool						minimizedBox = true;
		bool						border = true;
		bool						sizeBox = true;
		bool						iconVisible = true;
		bool						titleBar = true;
		bool						activated = false;

		auto operator<=>(const WindowStyleConfig&) const = default;
	};

	enum class UnitTestRemoteChannel
	{
		None,
		Sync,
		Async,
	};

	struct UnitTestScreenConfig
	{
		using FontConfig = vl::presentation::remoteprotocol::FontConfig;
		using ScreenConfig = vl::presentation::remoteprotocol::ScreenConfig;

		WString						executablePath;
		NativeMargin				customFramePadding;
		FontConfig					fontConfig;
		ScreenConfig				screenConfig;
		bool						useDomDiff = false;
		UnitTestRemoteChannel		useChannel = UnitTestRemoteChannel::None;

		void						FastInitialize(vint width, vint height, vint taskBarHeight = 0);
	};

	class UnitTestRemoteProtocolBase : public Object, protected virtual IGuiRemoteProtocol
	{
	protected:
		IGuiRemoteProtocolEvents*	events = nullptr;
		UnitTestScreenConfig		globalConfig;

	public:
		UnitTestRemoteProtocolBase(const UnitTestScreenConfig& _globalConfig)
			: globalConfig(_globalConfig)
		{
		}

		IGuiRemoteProtocol* GetProtocol()
		{
			return this;
		}

		IGuiRemoteProtocolEvents* GetEvents() const
		{
			return events;
		}

		const UnitTestScreenConfig& GetGlobalConfig() const
		{
			return globalConfig;
		}

	protected:

#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE)					void Impl_ ## NAME()									{ CHECK_FAIL(L"Not Implemented!"); }
#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE)						void Impl_ ## NAME(vint id)								{ CHECK_FAIL(L"Not Implemented!"); }
#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE)						void Impl_ ## NAME(const REQUEST& arguments)			{ CHECK_FAIL(L"Not Implemented!"); }
#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE)						void Impl_ ## NAME(vint id, const REQUEST& arguments)	{ CHECK_FAIL(L"Not Implemented!"); }
#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...)	MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE)
		GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER)
#undef MESSAGE_HANDLER
#undef MESSAGE_REQ_RES
#undef MESSAGE_REQ_NORES
#undef MESSAGE_NOREQ_RES
#undef MESSAGE_NOREQ_NORES

/***********************************************************************
IGuiRemoteProtocol
***********************************************************************/

		void Initialize(IGuiRemoteProtocolEvents* _events) override
		{
			events = _events;
		}

		WString GetExecutablePath() override
		{
			return globalConfig.executablePath;
		}
	};
}

#endif

/***********************************************************************
.\GUIUNITTESTPROTOCOL_IO.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/

#ifndef VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_IO
#define VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_IO


namespace vl::presentation::unittest
{
	class UnitTestRemoteProtocol_IO : public virtual UnitTestRemoteProtocolBase
	{
		using GlobalShortcutKey = remoteprotocol::GlobalShortcutKey;
		using GlobalShortcutKeyList = collections::List<GlobalShortcutKey>;
	public:
		bool						capturing = false;
		GlobalShortcutKeyList		globalShortcutKeys;

		UnitTestRemoteProtocol_IO(const UnitTestScreenConfig& _globalConfig)
			: UnitTestRemoteProtocolBase(_globalConfig)
		{
		}

	protected:

/***********************************************************************
IGuiRemoteProtocolMessages (IO)
***********************************************************************/

		void Impl_IOUpdateGlobalShortcutKey(const Ptr<GlobalShortcutKeyList>& arguments)
		{
			if (arguments)
			{
				CopyFrom(globalShortcutKeys, *arguments.Obj());
			}
			else
			{
				globalShortcutKeys.Clear();
			}
		}

		void Impl_IORequireCapture()
		{
			capturing = true;
		}

		void Impl_IOReleaseCapture()
		{
			capturing = false;
		}

		void Impl_IOIsKeyPressing(vint id, const VKEY& arguments)
		{
			CHECK_FAIL(L"Not Implemented!");
		}

		void Impl_IOIsKeyToggled(vint id, const VKEY& arguments)
		{
			CHECK_FAIL(L"Not Implemented!");
		}
	};
}

#endif

/***********************************************************************
.\GUIUNITTESTPROTOCOL_IOCOMMANDS.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/

#ifndef VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_IOCOMMANDS
#define VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_IOCOMMANDS


namespace vl::presentation::unittest
{

/***********************************************************************
UnitTestRemoteProtocol
***********************************************************************/
	
	class UnitTestRemoteProtocol_IOCommands : public virtual UnitTestRemoteProtocolBase
	{
	protected:
		Nullable<NativePoint>				mousePosition;
		collections::SortedList<VKEY>		pressingKeys;
		bool								leftPressing = false;
		bool								middlePressing = false;
		bool								rightPressing = false;
		bool								capslockToggled = false;

		IGuiRemoteProtocolEvents& UseEvents()
		{
			return *this->GetEvents();
		}

		bool IsPressing(VKEY key)
		{
			return pressingKeys.Contains(key);
		}

		NativeWindowMouseInfo MakeMouseInfo()
		{
			NativeWindowMouseInfo info;
			info.ctrl = IsPressing(VKEY::KEY_CONTROL) || IsPressing(VKEY::KEY_LCONTROL) || IsPressing(VKEY::KEY_RCONTROL);
			info.shift = IsPressing(VKEY::KEY_SHIFT) || IsPressing(VKEY::KEY_LSHIFT) || IsPressing(VKEY::KEY_RSHIFT);
			info.left = leftPressing;
			info.middle = middlePressing;
			info.right = rightPressing;
			info.x = mousePosition.Value().x.value;
			info.y = mousePosition.Value().y.value;
			info.wheel = 0;
			info.nonClient = false;
			return info;
		}

		NativeWindowKeyInfo MakeKeyInfo(VKEY key, bool autoRepeatKeyDown = false)
		{
			NativeWindowKeyInfo info;
			info.code = key;
			info.ctrl = IsPressing(VKEY::KEY_CONTROL) || IsPressing(VKEY::KEY_LCONTROL) || IsPressing(VKEY::KEY_RCONTROL);
			info.shift = IsPressing(VKEY::KEY_SHIFT) || IsPressing(VKEY::KEY_LSHIFT) || IsPressing(VKEY::KEY_RSHIFT);
			info.alt = IsPressing(VKEY::KEY_MENU) || IsPressing(VKEY::KEY_LMENU) || IsPressing(VKEY::KEY_RMENU);
			info.capslock = capslockToggled;
			info.autoRepeatKeyDown = autoRepeatKeyDown;
			return info;
		}

		NativeWindowCharInfo MakeCharInfo(wchar_t ch)
		{
			NativeWindowCharInfo info;
			info.code = ch;
			info.ctrl = IsPressing(VKEY::KEY_CONTROL) || IsPressing(VKEY::KEY_LCONTROL) || IsPressing(VKEY::KEY_RCONTROL);
			info.shift = IsPressing(VKEY::KEY_SHIFT) || IsPressing(VKEY::KEY_LSHIFT) || IsPressing(VKEY::KEY_RSHIFT);
			info.alt = IsPressing(VKEY::KEY_MENU) || IsPressing(VKEY::KEY_LMENU) || IsPressing(VKEY::KEY_RMENU);
			info.capslock = capslockToggled;
			return info;
		}

	public:

		UnitTestRemoteProtocol_IOCommands(const UnitTestScreenConfig& _globalConfig)
			: UnitTestRemoteProtocolBase(_globalConfig)
		{
		}

/***********************************************************************
Helper Functions
***********************************************************************/

		NativePoint LocationOf(compositions::GuiGraphicsComposition* composition, double ratioX = 0.5, double ratioY = 0.5, vint offsetX = 0, vint offsetY = 0)
		{
			INativeWindow* nativeWindow = composition->GetRelatedControlHost()->GetNativeWindow();
			Rect bounds = composition->GetGlobalBounds();
			NativeRect nativeBounds = { nativeWindow->Convert(bounds.LeftTop()),nativeWindow->Convert(bounds.GetSize()) };
			vint x = nativeBounds.x1.value + (vint)(nativeBounds.Width().value * ratioX) + offsetX;
			vint y = nativeBounds.y1.value + (vint)(nativeBounds.Height().value * ratioY) + offsetY;
			NativePoint windowLocation = nativeWindow->GetBounds().LeftTop();
			return { windowLocation.x.value + x,windowLocation.y.value + y };
		}

		NativePoint LocationOf(controls::GuiControl* control, double ratioX = 0.5, double ratioY = 0.5, vint offsetX = 0, vint offsetY = 0)
		{
			return LocationOf(control->GetBoundsComposition(), ratioX, ratioY, offsetX, offsetY);
		}

#define CLASS_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_IOCommands::"

/***********************************************************************
Keys
***********************************************************************/

		void _KeyDown(VKEY key)
		{
#define ERROR_MESSAGE_PREFIX CLASS_PREFIX L"_KeyDown(...)#"
			CHECK_ERROR(!pressingKeys.Contains(key), ERROR_MESSAGE_PREFIX L"The key is already being pressed.");
			pressingKeys.Add(key);
			if (key == VKEY::KEY_CAPITAL)
			{
				capslockToggled = !capslockToggled;
			}
			UseEvents().OnIOKeyDown(MakeKeyInfo(key, false));
#undef ERROR_MESSAGE_PREFIX
		}

		void _KeyDownRepeat(VKEY key)
		{
#define ERROR_MESSAGE_PREFIX CLASS_PREFIX L"_KeyDownRepeat(...)#"
			CHECK_ERROR(pressingKeys.Contains(key), ERROR_MESSAGE_PREFIX L"The key is not being pressed.");
			UseEvents().OnIOKeyDown(MakeKeyInfo(key, true));
#undef ERROR_MESSAGE_PREFIX
		}

		void _KeyUp(VKEY key)
		{
#define ERROR_MESSAGE_PREFIX CLASS_PREFIX L"_KeyUp(...)#"
			CHECK_ERROR(pressingKeys.Contains(key), ERROR_MESSAGE_PREFIX L"The key is not being pressed.");
			pressingKeys.Remove(key);
			UseEvents().OnIOKeyUp(MakeKeyInfo(key, false));
#undef ERROR_MESSAGE_PREFIX
		}

		void KeyPress(VKEY key)
		{
			_KeyDown(key);
			_KeyUp(key);
		}

		void KeyPress(VKEY key, bool ctrl, bool shift, bool alt)
		{
			if (ctrl) _KeyDown(VKEY::KEY_CONTROL);
			if (shift) _KeyDown(VKEY::KEY_SHIFT);
			if (alt) _KeyDown(VKEY::KEY_MENU);
			KeyPress(key);
			if (alt) _KeyUp(VKEY::KEY_MENU);
			if (shift) _KeyUp(VKEY::KEY_SHIFT);
			if (ctrl) _KeyUp(VKEY::KEY_CONTROL);
		}

		// Emits plain IOChar events without synthesizing key presses; expand in future iterations when modifiers are required.
		void TypeString(const WString& text)
		{
			if (text.Length() == 0) return;
			for (vint i = 0; i < text.Length(); i++)
			{
				UseEvents().OnIOChar(MakeCharInfo(text[i]));
			}
		}

/***********************************************************************
Mouse Move Events
***********************************************************************/

		void MouseMove(NativePoint position)
		{
			if (!mousePosition)
			{
				UseEvents().OnIOMouseEntered();
				goto DO_MOUSE_MOVE;
			}

			if (mousePosition.Value() == position) return;
		DO_MOUSE_MOVE:

			mousePosition = position;
			UseEvents().OnIOMouseMoving(MakeMouseInfo());
		}

		NativePoint GetMousePosition()
		{
#define ERROR_MESSAGE_PREFIX CLASS_PREFIX L"GetMousePosition()#"
			CHECK_ERROR(mousePosition, CLASS_PREFIX L"The mouse position is not set.");
			return mousePosition.Value();
#undef ERROR_MESSAGE_PREFIX
		}

/***********************************************************************
Mouse Wheel Events
***********************************************************************/

		void _Wheel(vint up, Nullable<NativePoint> position = {})
		{
			if (position) MouseMove(position.Value());
			auto info = MakeMouseInfo();
			info.wheel = up;
			UseEvents().OnIOVWheel(info);
		}

		void _Wheel(vint up, Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)
		{
			if (ctrl) _KeyDown(VKEY::KEY_CONTROL);
			if (shift) _KeyDown(VKEY::KEY_SHIFT);
			if (alt) _KeyDown(VKEY::KEY_MENU);
			_Wheel(up, position);
			if (alt) _KeyUp(VKEY::KEY_MENU);
			if (shift) _KeyUp(VKEY::KEY_SHIFT);
			if (ctrl) _KeyUp(VKEY::KEY_CONTROL);
		}

		void _HWheel(vint right, Nullable<NativePoint> position = {})
		{
			if (position) MouseMove(position.Value());
			auto info = MakeMouseInfo();
			info.wheel = right;
			UseEvents().OnIOHWheel(info);
		}

		void _HWheel(vint right, Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)
		{
			if (ctrl) _KeyDown(VKEY::KEY_CONTROL);
			if (shift) _KeyDown(VKEY::KEY_SHIFT);
			if (alt) _KeyDown(VKEY::KEY_MENU);
			_HWheel(right, position);
			if (alt) _KeyUp(VKEY::KEY_MENU);
			if (shift) _KeyUp(VKEY::KEY_SHIFT);
			if (ctrl) _KeyUp(VKEY::KEY_CONTROL);
		}

		void WheelDown(vint jumps = 1, Nullable<NativePoint> position = {})
		{
			_Wheel(-jumps * 120, position);
		}

		void WheelDown(vint jumps, Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)
		{
			_Wheel(-jumps * 120, position, ctrl, shift, alt);
		}

		void WheelUp(vint jumps = 1, Nullable<NativePoint> position = {})
		{
			_Wheel(jumps * 120, position);
		}

		void WheelUp(vint jumps, Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)
		{
			_Wheel(jumps * 120, position, ctrl, shift, alt);
		}

		void HWheelLeft(vint jumps = 1, Nullable<NativePoint> position = {})
		{
			_HWheel(-jumps * 120, position);
		}

		void HWheelLeft(vint jumps, Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)
		{
			_HWheel(-jumps * 120, position, ctrl, shift, alt);
		}

		void HWheelRight(vint jumps = 1, Nullable<NativePoint> position = {})
		{
			_HWheel(jumps * 120, position);
		}

		void HWheelRight(vint jumps, Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)
		{
			_HWheel(jumps * 120, position, ctrl, shift, alt);
		}

/***********************************************************************
Mouse Click Events
***********************************************************************/

#define DEFINE_MOUSE_ACTIONS(PREFIX, LOWER, UPPER)\
		void _ ## PREFIX ## Down(Nullable<NativePoint> position = {})\
		{\
			if (position) MouseMove(position.Value());\
			CHECK_ERROR(!LOWER ## Pressing, CLASS_PREFIX L"_" L ## #PREFIX L"Down(...)#" L"The button should not be being pressed.");\
			LOWER ## Pressing = true;\
			UseEvents().OnIOButtonDown({ remoteprotocol::IOMouseButton::UPPER,MakeMouseInfo() });\
		}\
		void _ ## PREFIX ## Up(Nullable<NativePoint> position = {})\
		{\
			if (position) MouseMove(position.Value());\
			CHECK_ERROR(LOWER ## Pressing, CLASS_PREFIX L"_" L ## #PREFIX L"Up(...)#" L"The button should be being pressed.");\
			LOWER ## Pressing = false;\
			UseEvents().OnIOButtonUp({ remoteprotocol::IOMouseButton::UPPER,MakeMouseInfo() });\
		}\
		void _ ## PREFIX ## DBClick(Nullable<NativePoint> position = {})\
		{\
			if (position) MouseMove(position.Value());\
			CHECK_ERROR(!LOWER ## Pressing, CLASS_PREFIX L"_" L ## #PREFIX L"DBClick(...)#" L"The button should not be being pressed.");\
			LOWER ## Pressing = true;\
			UseEvents().OnIOButtonDoubleClick({ remoteprotocol::IOMouseButton::UPPER,MakeMouseInfo() });\
		}\
		void PREFIX ## Click(Nullable<NativePoint> position = {})\
		{\
			_ ## PREFIX ## Down(position);\
			_ ## PREFIX ## Up(position);\
		}\
		void PREFIX ## Click(Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)\
		{\
			if (ctrl) _KeyDown(VKEY::KEY_CONTROL);\
			if (shift) _KeyDown(VKEY::KEY_SHIFT);\
			if (alt) _KeyDown(VKEY::KEY_MENU);\
			PREFIX ## Click(position);\
			if (alt) _KeyUp(VKEY::KEY_MENU);\
			if (shift) _KeyUp(VKEY::KEY_SHIFT);\
			if (ctrl) _KeyUp(VKEY::KEY_CONTROL);\
		}\
		void PREFIX ## DBClick(Nullable<NativePoint> position = {})\
		{\
			_ ## PREFIX ## Down(position);\
			_ ## PREFIX ## Up(position);\
			_ ## PREFIX ## DBClick(position);\
			_ ## PREFIX ## Up(position);\
		}\
		void PREFIX ## DBClick(Nullable<NativePoint> position, bool ctrl, bool shift, bool alt)\
		{\
			if (ctrl) _KeyDown(VKEY::KEY_CONTROL);\
			if (shift) _KeyDown(VKEY::KEY_SHIFT);\
			if (alt) _KeyDown(VKEY::KEY_MENU);\
			PREFIX ## DBClick(position);\
			if (alt) _KeyUp(VKEY::KEY_MENU);\
			if (shift) _KeyUp(VKEY::KEY_SHIFT);\
			if (ctrl) _KeyUp(VKEY::KEY_CONTROL);\
		}\

		DEFINE_MOUSE_ACTIONS(L, left, Left);
		DEFINE_MOUSE_ACTIONS(M, middle, Middle);
		DEFINE_MOUSE_ACTIONS(R, right, Right);

#undef DEFINE_MOUSE_ACTIONS

#undef CLASS_PREFIX
	};
}

#endif

/***********************************************************************
.\GUIUNITTESTPROTOCOL_MAINWINDOW.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/

#ifndef VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_MAINWINDOW
#define VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_MAINWINDOW


namespace vl::presentation::unittest
{
	class UnitTestRemoteProtocol_MainWindow : public virtual UnitTestRemoteProtocolBase
	{
		using WindowSizingConfig = remoteprotocol::WindowSizingConfig;
		using WindowShowing = remoteprotocol::WindowShowing;
	public:
		WindowSizingConfig			sizingConfig;
		WindowStyleConfig			styleConfig;
		NativeRect					lastRestoredSize;
	
		UnitTestRemoteProtocol_MainWindow(const UnitTestScreenConfig& _globalConfig)
			: UnitTestRemoteProtocolBase(_globalConfig)
		{
			sizingConfig.bounds = { 0,0,0,0 };
			sizingConfig.clientBounds = { 0,0,0,0 };
			sizingConfig.customFramePadding = this->GetGlobalConfig().customFramePadding;
			sizingConfig.sizeState = INativeWindow::Restored;
		}

	protected:

/***********************************************************************
IGuiRemoteProtocolMessages (Controller)
***********************************************************************/
	
		void Impl_ControllerGetFontConfig(vint id)
		{
			this->GetEvents()->RespondControllerGetFontConfig(id, this->GetGlobalConfig().fontConfig);
		}
	
		void Impl_ControllerGetScreenConfig(vint id)
		{
			this->GetEvents()->RespondControllerGetScreenConfig(id, this->GetGlobalConfig().screenConfig);
		}

/***********************************************************************
IGuiRemoteProtocolMessages (Window)
***********************************************************************/
	
		void Impl_WindowGetBounds(vint id)
		{
			this->GetEvents()->RespondWindowGetBounds(id, sizingConfig);
		}
	
		void Impl_WindowNotifySetTitle(const ::vl::WString& arguments)
		{
			styleConfig.title = arguments;
		}
	
		void Impl_WindowNotifySetEnabled(const bool& arguments)
		{
			styleConfig.enabled = arguments;
		}
	
		void Impl_WindowNotifySetTopMost(const bool& arguments)
		{
			styleConfig.topMost = arguments;
		}
	
		void Impl_WindowNotifySetShowInTaskBar(const bool& arguments)
		{
			styleConfig.showInTaskBar = arguments;
		}
	
		void OnBoundsUpdated()
		{
			sizingConfig.clientBounds = sizingConfig.bounds;
			if (sizingConfig.sizeState == INativeWindow::Restored)
			{
				lastRestoredSize = sizingConfig.bounds;
			}
			this->GetEvents()->OnWindowBoundsUpdated(sizingConfig);
		}
	
		void Impl_WindowNotifySetBounds(const NativeRect& arguments)
		{
			sizingConfig.bounds = arguments;
			OnBoundsUpdated();
		}
	
		void Impl_WindowNotifySetClientSize(const NativeSize& arguments)
		{
			sizingConfig.bounds = { sizingConfig.bounds.LeftTop(), arguments };
			OnBoundsUpdated();
		}
	
		void Impl_WindowNotifySetCustomFrameMode(const bool& arguments)	{ styleConfig.customFrameMode = arguments;	this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
		void Impl_WindowNotifySetMaximizedBox(const bool& arguments)	{ styleConfig.maximizedBox = arguments;		this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
		void Impl_WindowNotifySetMinimizedBox(const bool& arguments)	{ styleConfig.minimizedBox = arguments;		this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
		void Impl_WindowNotifySetBorder(const bool& arguments)			{ styleConfig.border = arguments;			this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
		void Impl_WindowNotifySetSizeBox(const bool& arguments)			{ styleConfig.sizeBox = arguments;			this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
		void Impl_WindowNotifySetIconVisible(const bool& arguments)		{ styleConfig.iconVisible = arguments;		this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
		void Impl_WindowNotifySetTitleBar(const bool& arguments)		{ styleConfig.titleBar = arguments;			this->GetEvents()->OnWindowBoundsUpdated(sizingConfig); }
		void Impl_WindowNotifyActivate()								{ styleConfig.activated = true; }
		void Impl_WindowNotifyMinSize(const NativeSize& arguments)		{}
		void Impl_WindowNotifySetCaret(const NativePoint& arguments)	{}
	
		void Impl_WindowNotifyShow(const WindowShowing& arguments)
		{
			styleConfig.activated = arguments.activate;
			if (sizingConfig.sizeState != arguments.sizeState)
			{
				sizingConfig.sizeState = arguments.sizeState;
				switch (arguments.sizeState)
				{
				case INativeWindow::Maximized:
					sizingConfig.bounds = this->GetGlobalConfig().screenConfig.clientBounds;
					OnBoundsUpdated();
					break;
				case INativeWindow::Minimized:
					sizingConfig.bounds = NativeRect(
						{
							this->GetGlobalConfig().screenConfig.bounds.x2,
							this->GetGlobalConfig().screenConfig.bounds.y2
						},
						{ 1,1 }
					);
					OnBoundsUpdated();
					break;
				case INativeWindow::Restored:
					if (sizingConfig.bounds != lastRestoredSize)
					{
						sizingConfig.bounds = lastRestoredSize;
						OnBoundsUpdated();
					}
					else
					{
						this->GetEvents()->OnWindowBoundsUpdated(sizingConfig);
					}
					break;
				}
			}
		}
	};
}

#endif

/***********************************************************************
.\GUIUNITTESTPROTOCOL_RENDERING.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/

#ifndef VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_RENDERING
#define VCZH_PRESENTATION_GUIUNITTESTPROTOCOL_RENDERING


namespace vl::presentation::unittest
{

/***********************************************************************
DocumentParagraphState
***********************************************************************/

	struct DocumentParagraphCharLayout
	{
		double									x;               // X position of character start
		double									width;           // Character width
		vint									lineIndex;       // Which line this character belongs to
		vint									height;          // Character height (font size or inline object height)
		vint									length;          // Character length in text (1 for normal characters, more for inline objects)
		vint									baseline;        // Distance from top to baseline (for alignment)
		bool									isInlineObject;  // True if this position is part of an inline object
	};

	struct DocumentParagraphLineInfo
	{
		vint									startPos;        // Text position of line start (inclusive)
		vint									endPos;          // Text position of line end (exclusive)
		vint									y;               // Y coordinate of line top
		vint									height;          // Line height (computed from baseline alignment)
		vint									baseline;        // Line baseline position from line top
		vint									width;           // Line width (for alignment calculation)
	};

	struct DocumentParagraphState
	{
		WString											text;
		bool											wrapLine = false;
		vint											maxWidth = -1;
		remoteprotocol::ElementHorizontalAlignment		alignment = remoteprotocol::ElementHorizontalAlignment::Left;

		elements::DocumentTextRunPropertyMap			textRuns;
		elements::DocumentInlineObjectRunPropertyMap	inlineObjectRuns;
		elements::DocumentRunPropertyMap				mergedRuns;

		collections::Dictionary<vint, DocumentParagraphCharLayout>	caretLayouts;
		collections::SortedList<vint>								caretLayoutKeys;
		collections::List<DocumentParagraphLineInfo>	lines;
		Size											cachedSize;
		collections::Dictionary<vint, Rect>				cachedInlineObjectBounds;
	};

/***********************************************************************
UnitTestRemoteProtocol
***********************************************************************/

	using ElementDescVariant = remoteprotocol::UnitTest_ElementDescVariant;
	using UnitTestRenderingDom = remoteprotocol::RenderingDom;

	struct UnitTestLoggedFrame
	{
		vint													frameId;
		collections::List<WString>								renderingCommandsLog;
		Nullable<remoteprotocol::RenderingDom_DiffsInOrder>		renderingDiffs;
		Ptr<UnitTestRenderingDom>								renderingDom;
	};

	using UnitTestLoggedFrameList = collections::List<Ptr<UnitTestLoggedFrame>>;
	
	class UnitTestRemoteProtocol_Rendering : public virtual UnitTestRemoteProtocolBase
	{
		using IdSet = collections::SortedList<vint>;
		using Base64ToImageMetadataMap = collections::Dictionary<WString, remoteprotocol::ImageMetadata>;
		using ElementDescMap = collections::Dictionary<vint, ElementDescVariant>;
		using ImageMetadataMap = collections::Dictionary<vint, remoteprotocol::ImageMetadata>;

	protected:
		remoteprotocol::RenderingDomBuilder				renderingDomBuilder;
		remoteprotocol::UnitTest_RenderingTrace			loggedTrace;
		UnitTestLoggedFrameList							loggedFrames;
		bool											lastRenderingCommandsOpening = false;

		Ptr<remoteprotocol::RenderingDom>				receivedDom;
		remoteprotocol::DomIndex						receivedDomIndex;
		bool											receivedDomDiffMessage = false;
		bool											receivedElementMessage = false;
		vint											rendererIdleCount = 0;

		ElementDescMap									lastElementDescs;
		IdSet											removedElementIds;
		IdSet											removedImageIds;
		Ptr<Base64ToImageMetadataMap>					cachedImageMetadatas;

		remoteprotocol::ElementMeasurings				measuringForNextRendering;
		regex::Regex									regexCrLf{ L"/n|/r(/n)?" };

		collections::Dictionary<vint, Ptr<DocumentParagraphState>> paragraphStates;

		void ResetCreatedObjects()
		{
			loggedTrace.createdElements = Ptr(new collections::Dictionary<vint, remoteprotocol::RendererType>);
			loggedTrace.imageCreations = Ptr(new remoteprotocol::ArrayMap<vint, remoteprotocol::ImageCreation, &remoteprotocol::ImageCreation::id>);
			loggedTrace.imageMetadatas = Ptr(new remoteprotocol::ArrayMap<vint, remoteprotocol::ImageMetadata, &remoteprotocol::ImageMetadata::id>);
			lastElementDescs.Clear();
		}
	public:

		UnitTestRemoteProtocol_Rendering(const UnitTestScreenConfig& _globalConfig)
			: UnitTestRemoteProtocolBase(_globalConfig)
		{
			ResetCreatedObjects();
			loggedTrace.frames = Ptr(new collections::List<remoteprotocol::UnitTest_RenderingFrame>);
		}

	protected:

/***********************************************************************
IGuiRemoteProtocolMessages (Rendering)
***********************************************************************/

		Ptr<UnitTestLoggedFrame>			GetLastRenderingFrame();
		Ptr<UnitTestLoggedFrame>			TryGetLastRenderingFrameAndReset();
		void								Impl_RendererBeginRendering(const remoteprotocol::ElementBeginRendering& arguments);
		void								Impl_RendererEndRendering(vint id);
		void								Impl_RendererIdle();

/***********************************************************************
IGuiRemoteProtocolMessages (Rendering - Element)
***********************************************************************/

		void								Impl_RendererBeginBoundary(const remoteprotocol::ElementBoundary& arguments);
		void								Impl_RendererEndBoundary();
		void								EnsureRenderedElement(vint id, Rect bounds);
		void								Impl_RendererRenderElement(const remoteprotocol::ElementRendering& arguments);

/***********************************************************************
IGuiRemoteProtocolMessages (Rendering - Dom)
***********************************************************************/

		void								CalculateSolidLabelSizesIfNecessary(Ptr<remoteprotocol::RenderingDom> dom);
		void								Impl_RendererRenderDom(const Ptr<remoteprotocol::RenderingDom>& arguments);
		void								Impl_RendererRenderDomDiff(const remoteprotocol::RenderingDom_DiffsInOrder& arguments);

/***********************************************************************
IGuiRemoteProtocolMessages (Elements)
***********************************************************************/

		void								Impl_RendererCreated(const Ptr<collections::List<remoteprotocol::RendererCreation>>& arguments);
		void								Impl_RendererDestroyed(const Ptr<collections::List<vint>>& arguments);

		template<remoteprotocol::RendererType RendererType, typename TElementDesc>
		void RequestRendererUpdateElement(const TElementDesc& arguments, const wchar_t* emWrongId, const wchar_t* emWrongType)
		{
			vint index = loggedTrace.createdElements->Keys().IndexOf(arguments.id);
			CHECK_ERROR(index != -1, emWrongId);
			CHECK_ERROR(loggedTrace.createdElements->Values()[index] == RendererType, emWrongType);
			lastElementDescs.Set(arguments.id, arguments);
		}

		void								Impl_RendererUpdateElement_SolidBorder(const remoteprotocol::ElementDesc_SolidBorder& arguments);
		void								Impl_RendererUpdateElement_SinkBorder(const remoteprotocol::ElementDesc_SinkBorder& arguments);
		void								Impl_RendererUpdateElement_SinkSplitter(const remoteprotocol::ElementDesc_SinkSplitter& arguments);
		void								Impl_RendererUpdateElement_SolidBackground(const remoteprotocol::ElementDesc_SolidBackground& arguments);
		void								Impl_RendererUpdateElement_GradientBackground(const remoteprotocol::ElementDesc_GradientBackground& arguments);
		void								Impl_RendererUpdateElement_InnerShadow(const remoteprotocol::ElementDesc_InnerShadow& arguments);
		void								Impl_RendererUpdateElement_Polygon(const remoteprotocol::ElementDesc_Polygon& arguments);

/***********************************************************************
IGuiRemoteProtocolMessages (Elements - SolidLabel)
***********************************************************************/

		void								CalculateSolidLabelSizeIfNecessary(vint width, vint height, const remoteprotocol::ElementDesc_SolidLabel& arguments);
		void								Impl_RendererUpdateElement_SolidLabel(const remoteprotocol::ElementDesc_SolidLabel& arguments);

/***********************************************************************
IGuiRemoteProtocolMessages (Elements - Image)
***********************************************************************/

		WString								GetBinaryKeyFromBinary(stream::IStream& binary);
		WString								GetBinaryKeyFromImage(Ptr<INativeImage> image);
		remoteprotocol::ImageMetadata		MakeImageMetadata(const remoteprotocol::ImageCreation& arguments);
		void								Impl_ImageCreated(vint id, const remoteprotocol::ImageCreation& arguments);
		void								Impl_ImageDestroyed(const vint& arguments);
		void								Impl_RendererUpdateElement_ImageFrame(const remoteprotocol::ElementDesc_ImageFrame& arguments);

/***********************************************************************
IGuiRemoteProtocolMessages (Elements - Document)
***********************************************************************/

		void								Impl_RendererUpdateElement_DocumentParagraph(vint id, const remoteprotocol::ElementDesc_DocumentParagraph& arguments);
		void								Impl_DocumentParagraph_GetCaret(vint id, const remoteprotocol::GetCaretRequest& arguments);
		void								Impl_DocumentParagraph_GetCaretBounds(vint id, const remoteprotocol::GetCaretBoundsRequest& arguments);
		void								Impl_DocumentParagraph_GetInlineObjectFromPoint(vint id, const remoteprotocol::GetInlineObjectFromPointRequest & arguments);
		void								Impl_DocumentParagraph_GetNearestCaretFromTextPos(vint id, const remoteprotocol::GetNearestCaretFromTextPosRequest& arguments);
		void								Impl_DocumentParagraph_IsValidCaret(vint id, const remoteprotocol::IsValidCaretRequest& arguments);
		void								Impl_DocumentParagraph_OpenCaret(const remoteprotocol::OpenCaretRequest& arguments);
		void								Impl_DocumentParagraph_CloseCaret(const vint& arguments);
	};
}

#endif


/***********************************************************************
.\GUIUNITTESTPROTOCOL.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/

#ifndef VCZH_PRESENTATION_GUIUNITTESTPROTOCOL
#define VCZH_PRESENTATION_GUIUNITTESTPROTOCOL


namespace vl::presentation::unittest
{

/***********************************************************************
UnitTestFrameworkConfig
***********************************************************************/

	struct UnitTestFrameworkConfig
	{
		filesystem::FilePath					snapshotFolder;
		filesystem::FilePath					resourceFolder;
	};

	extern const UnitTestFrameworkConfig&		GetUnitTestFrameworkConfig();

/***********************************************************************
UnitTestRemoteProtocol
***********************************************************************/

	class UnitTestRemoteProtocolFeatures :
		public UnitTestRemoteProtocol_MainWindow,
		public UnitTestRemoteProtocol_IO,
		public UnitTestRemoteProtocol_Rendering,
		public UnitTestRemoteProtocol_IOCommands
	{
	protected:
		bool								stopped = false;
		bool								everRendered = false;
		Ptr<UnitTestLoggedFrame>			candidateFrame;
		vint								idlingCounter = 0;

		bool LogRenderingResult()
		{
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocolFeatures::LogRenderingResult()#"
			if (auto lastFrame = this->TryGetLastRenderingFrameAndReset())
			{
				candidateFrame = lastFrame;
				everRendered = true;
			}
			else if (everRendered)
			{
				if (candidateFrame)
				{
					idlingCounter = 0;
					auto descs = Ptr(new collections::Dictionary<vint, ElementDescVariant>);
					CopyFrom(*descs.Obj(), this->lastElementDescs);
					this->loggedTrace.frames->Add({
						candidateFrame->frameId,
						{},
						this->sizingConfig,
						descs,
						candidateFrame->renderingDom
						});
					candidateFrame = {};
					return true;
				}
				else
				{
					idlingCounter++;
					if (idlingCounter == 100)
					{
						CHECK_ERROR(
							idlingCounter < 100,
							ERROR_MESSAGE_PREFIX L"The last frame didn't trigger UI updating. The action registered by OnNextIdleFrame should always make any element or layout to change."
							);
					}
				}
			}
			return false;
#undef ERROR_MESSAGE_PREFIX
		}

	public:
		UnitTestRemoteProtocolFeatures(const UnitTestScreenConfig& _globalConfig)
			: UnitTestRemoteProtocol_MainWindow(_globalConfig)
			, UnitTestRemoteProtocol_IO(_globalConfig)
			, UnitTestRemoteProtocol_Rendering(_globalConfig)
			, UnitTestRemoteProtocol_IOCommands(_globalConfig)
		{
		}

		const auto& GetLoggedTrace()
		{
			return this->loggedTrace;
		}

		const auto& GetLoggedFrames()
		{
			return this->loggedFrames;
		}

/***********************************************************************
IGuiRemoteProtocolMessages (Initialization)
***********************************************************************/

	protected:

		void Impl_ControllerConnectionEstablished()
		{
			ResetCreatedObjects();
		}

		void Impl_ControllerConnectionStopped()
		{
			stopped = true;
		}
	};

	class UnitTestRemoteProtocol 
		: public UnitTestRemoteProtocolFeatures
		, protected virtual IGuiRemoteEventProcessor
	{
		using EventPair = collections::Pair<Nullable<WString>, Func<void()>>;
	protected:
		const UnitTestFrameworkConfig&		frameworkConfig;
		WString								appName;
		collections::List<EventPair>		processRemoteEvents;
		vint								lastFrameIndex = -1;
		vint								nextEventIndex = 0;
		bool								frameExecuting = false;

	public:

		UnitTestRemoteProtocol(const WString& _appName, UnitTestScreenConfig _globalConfig)
			: UnitTestRemoteProtocolBase(_globalConfig)
			, UnitTestRemoteProtocolFeatures(_globalConfig)
			, frameworkConfig(GetUnitTestFrameworkConfig())
			, appName(_appName)
		{
		}

		template<typename TCallback>
		void OnNextIdleFrame(TCallback&& callback)
		{
			processRemoteEvents.Add({ Nullable<WString>{},std::forward<TCallback&&>(callback) });
		}

		template<typename TCallback>
		void OnNextIdleFrame(const WString& name, TCallback&& callback)
		{
			processRemoteEvents.Add({ name,std::forward<TCallback&&>(callback) });
		}

/***********************************************************************
IGuiRemoteProtocolMessages
***********************************************************************/

#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE)					void Request ## NAME() override { UnitTestRemoteProtocolFeatures::Impl_ ## NAME(); }
#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE)						void Request ## NAME(vint id) override { UnitTestRemoteProtocolFeatures::Impl_ ## NAME(id); }
#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE)						void Request ## NAME(const REQUEST& arguments) override { UnitTestRemoteProtocolFeatures::Impl_ ## NAME(arguments); }
#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE)						void Request ## NAME(vint id, const REQUEST& arguments) override { UnitTestRemoteProtocolFeatures::Impl_ ## NAME(id, arguments); }
#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...)	MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE)
		GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER)
#undef MESSAGE_HANDLER
#undef MESSAGE_REQ_RES
#undef MESSAGE_REQ_NORES
#undef MESSAGE_NOREQ_RES
#undef MESSAGE_NOREQ_NORES

/***********************************************************************
IGuiRemoteProtocol
***********************************************************************/

		void Submit(bool& disconnected) override
		{
			// TODO: Failure injection to disconnected
		}

		void ProcessRemoteEvents() override
		{
#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol::ProcessRemoteEvents()#"
			if (!stopped)
			{
				if (LogRenderingResult())
				{
					auto [name, func] = processRemoteEvents[nextEventIndex];
					CHECK_ERROR(!frameExecuting, ERROR_MESSAGE_PREFIX L"The action registered by OnNextIdleFrame should not call any blocking function, consider using InvokeInMainThread.");
					vl::unittest::UnitTest::PrintMessage(L"Execute idle frame[" + (name ? name.Value() : itow(nextEventIndex)) + L"]", vl::unittest::UnitTest::MessageKind::Info);
					CHECK_ERROR(lastFrameIndex != loggedTrace.frames->Count() - 1, ERROR_MESSAGE_PREFIX L"No rendering occured after the last idle frame.");
					lastFrameIndex = loggedTrace.frames->Count() - 1;

					if (name)
					{
						auto&& lastFrame = (*loggedTrace.frames.Obj())[loggedTrace.frames->Count() - 1];
						lastFrame.frameName = name;
					}
					if (nextEventIndex == 0)
					{
						CHECK_ERROR(rendererIdleCount <= 1, ERROR_MESSAGE_PREFIX L"Expected at most one RendererIdle before the first frame boundary.");
					}
					else
					{
						CHECK_ERROR(rendererIdleCount == 1, ERROR_MESSAGE_PREFIX L"Expected exactly one RendererIdle between two frame boundaries.");
					}
					rendererIdleCount = 0;
					// Note: do not reset rendererIdleCount when LogRenderingResult() is false;
					// it must accumulate across non-boundary ticks to detect duplicate/out-of-order RendererIdle.
					frameExecuting = true;
					func();
					frameExecuting = false;
					nextEventIndex++;
				}
			}
#undef ERROR_MESSAGE_PREFIX
		}

		IGuiRemoteEventProcessor* GetRemoteEventProcessor() override
		{
			return this;
		}
	};
}

#endif


/***********************************************************************
.\GUIUNITTESTUTILITIES.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Unit Test Snapsnot and other Utilities
***********************************************************************/

#ifndef VCZH_PRESENTATION_GUIUNITTESTUTILITIES
#define VCZH_PRESENTATION_GUIUNITTESTUTILITIES


namespace vl::presentation::unittest
{
	class IUnitTestContext : public virtual Interface
	{
	public:
	};

	using UnitTestMainFunc = vl::Func<void(UnitTestRemoteProtocol*, IUnitTestContext*)>;
	using UnitTestLinkFunc = vl::Func<void(UnitTestRemoteProtocol*, IUnitTestContext*, const UnitTestMainFunc&)>;
}

extern void										GacUIUnitTest_Initialize(const vl::presentation::unittest::UnitTestFrameworkConfig* config);
extern void										GacUIUnitTest_Finalize();
extern void										GacUIUnitTest_SetGuiMainProxy(const vl::presentation::unittest::UnitTestMainFunc& proxy);
extern void										GacUIUnitTest_LinkGuiMainProxy(const vl::presentation::unittest::UnitTestLinkFunc& proxy);
extern void										GacUIUnitTest_Start(const vl::WString& appName, vl::Nullable<vl::presentation::unittest::UnitTestScreenConfig> config = {});
extern void										GacUIUnitTest_StartAsync(const vl::WString& appName, vl::Nullable<vl::presentation::unittest::UnitTestScreenConfig> config = {});
extern void										GacUIUnitTest_Start_WithResourceAsText(const vl::WString& appName, vl::Nullable<vl::presentation::unittest::UnitTestScreenConfig> config, const vl::WString& resourceText);
extern vl::Ptr<vl::presentation::GuiResource>	GacUIUnitTest_CompileAndLoad(const vl::WString& xmlResource);

#ifdef VCZH_DESCRIPTABLEOBJECT_WITH_METADATA

struct GacUIUnitTest_Installer
{
	vl::Func<void()> initialize;
	vl::Func<void()> finalize;
	vl::Func<void(vl::presentation::controls::GuiWindow*)> installWindow;
	vl::Nullable<vl::presentation::unittest::UnitTestScreenConfig> config;
};

template<typename TTheme>
void GacUIUnitTest_StartFast_WithResourceAsText(
	const vl::WString& appName,
	const vl::WString& windowTypeFullName,
	const vl::WString& resourceText,
	GacUIUnitTest_Installer installer = {}
)
{
	GacUIUnitTest_LinkGuiMainProxy([=](
		vl::presentation::unittest::UnitTestRemoteProtocol* protocol,
		vl::presentation::unittest::IUnitTestContext* context,
		const vl::presentation::unittest::UnitTestMainFunc& previousMainProxy
		)
	{
		{
			vl::presentation::remoteprotocol::ControllerGlobalConfig globalConfig;
#if defined VCZH_WCHAR_UTF16
			globalConfig.documentCaretFromEncoding = vl::presentation::remoteprotocol::CharacterEncoding::UTF16;
#elif defined VCZH_WCHAR_UTF32
			globalConfig.documentCaretFromEncoding = vl::presentation::remoteprotocol::CharacterEncoding::UTF32;
#endif
			protocol->GetEvents()->OnControllerConnect(globalConfig);
		}
		auto theme = vl::Ptr(new TTheme);
		vl::presentation::theme::RegisterTheme(theme);
		{
			if(installer.initialize)
			{
				installer.initialize();
			}
			auto windowValue = vl::reflection::description::Value::Create(windowTypeFullName);
			TEST_ASSERT(windowValue.GetRawPtr());

			auto window = vl::Ptr(windowValue.GetRawPtr()->SafeAggregationCast<vl::presentation::controls::GuiWindow>());
			TEST_ASSERT(window);

			if (installer.installWindow)
			{
				installer.installWindow(window.Obj());
			}
			window->MoveToScreenCenter();
			previousMainProxy(protocol, context);
			vl::presentation::controls::GetApplication()->Run(window.Obj());
			if (installer.finalize)
			{
				installer.finalize();
			}
		}
		vl::presentation::theme::UnregisterTheme(theme->Name);
	});
	GacUIUnitTest_Start_WithResourceAsText(appName, installer.config, resourceText);
}

#endif

#endif
