// This file is part of VSTGUI. It is subject to the license terms 
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE

#include "../../lib/algorithm.h"
#include "../../lib/cfileselector.h"
#include "../../lib/cframe.h"
#include "../../lib/controls/coptionmenu.h"
#include "../../lib/controls/csegmentbutton.h"
#include "../../lib/controls/cstringlist.h"
#include "../../lib/controls/ctextedit.h"
#include "../../lib/crect.h"
#include "../../lib/cvstguitimer.h"
#include "../../lib/iviewlistener.h"
#include "../../uidescription/compresseduidescription.h"
#include "../../uidescription/cstream.h"
#include "../../uidescription/xmlparser.h"
#include "../../uidescription/detail/uiviewcreatorattributes.h"
#include "../../uidescription/editing/uieditcontroller.h"
#include "../../uidescription/editing/uieditmenucontroller.h"
#include "../../uidescription/icontroller.h"
#include "../../uidescription/uiattributes.h"
#include "../../uidescription/uicontentprovider.h"
#include "../../uidescription/uidescription.h"
#include "../include/helpers/menubuilder.h"
#include "../include/helpers/valuelistener.h"
#include "../include/helpers/windowcontroller.h"
#include "../include/ialertbox.h"
#include "../include/iappdelegate.h"
#include "../include/iapplication.h"
#include "../include/iasync.h"
#include "../include/iuidescwindow.h"
#include "application.h"
#include "shareduiresources.h"

//------------------------------------------------------------------------
namespace VSTGUI {
namespace Standalone {
namespace /* Anonymous */ {

using UIDesc::ModelBindingPtr;
using UIDesc::CustomizationPtr;

//------------------------------------------------------------------------
class WindowController : public IWindowController,
						 public ICommandHandler,
						 public IUIDescWindowController
{
public:
	bool init (const UIDesc::Config& config, WindowPtr& window);
	bool initStatic (const UIDesc::Config& config, WindowPtr& window);

	void onSizeChanged (const IWindow& window, const CPoint& newSize) override;
	void onPositionChanged (const IWindow& window, const CPoint& newPosition) override;
	void onShow (const IWindow& window) override;
	void onHide (const IWindow& window) override;
	void onClosed (const IWindow& window) override;
	void onActivated (const IWindow& window) override;
	void onDeactivated (const IWindow& window) override;
	CPoint constraintSize (const IWindow& window, const CPoint& newSize) override;
	bool canClose (const IWindow& window) override;
	void beforeShow (IWindow& window) override;
	PlatformFrameConfigPtr createPlatformFrameConfig (PlatformType platformType) override;
	void onSetContentView (IWindow& window, const SharedPointer<CFrame>& contentView) override;

	bool canHandleCommand (const Command& command) override;
	bool handleCommand (const Command& command) override;

	const IMenuBuilder* getWindowMenuBuilder (const IWindow& window) const override;

	ModelBindingPtr getModelBinding () const override;
	CustomizationPtr getCustomization () const override;

private:
	struct Impl;
	struct EditImpl;
	std::unique_ptr<Impl> impl;
};

//------------------------------------------------------------------------
class ValueWrapper : public ValueListenerAdapter,
                     public IControlListener,
                     public ViewListenerAdapter
{
public:
	using ControlList = std::vector<CControl*>;

	explicit ValueWrapper (const ValuePtr& value = nullptr) : value (value)
	{
		if (value)
			value->registerListener (this);
	}
	~ValueWrapper () noexcept override
	{
		if (value)
			value->unregisterListener (this);
		for (auto& control : controls)
			onRemoveControl (control);
	}

	const UTF8String& getID () const { return value->getID (); }

	void onBeginEdit (IValue&) override
	{
		for (auto& c : controls)
		{
			if (auto listener = c->getListener ())
				listener->controlBeginEdit (c);
		}
	}

	void onEndEdit (IValue&) override
	{
		for (auto& c : controls)
		{
			if (auto listener = c->getListener ())
				listener->controlEndEdit (c);
		}
	}

	void onPerformEdit (IValue&, IValue::Type newValue) override
	{
		auto newControlValue = static_cast<float> (newValue);
		for (auto& c : controls)
		{
			updateControl = c;
			c->setValueNormalized (newControlValue);
			c->valueChanged ();
			c->invalid ();
			updateControl = nullptr;
		}
	}

	void updateControlOnStateChange (CControl* control) const
	{
		control->setMouseEnabled (value->isActive ());

		auto listcontrol = dynamic_cast<CListControl*>(control);
		auto stepValue = dynamicPtrCast<const IStepValue> (value);
		if (listcontrol && stepValue)
		{
			control->setMin (0.f);
			control->setMax (static_cast<float> (stepValue->getSteps () - 1));
			return;
		}
		control->setMin (0.f);
		control->setMax (1.f);
		if (!stepValue)
			return;
		const auto& valueConverter = value->getConverter ();
		if (auto menu = dynamic_cast<COptionMenu*> (control))
		{
			menu->removeAllEntry ();
			for (IStepValue::StepType i = 0; i < stepValue->getSteps (); ++i)
			{
				auto title = valueConverter.valueAsString (stepValue->stepToValue (i));
				menu->addEntry (title);
			}
		}
		else if (auto segmentButton = dynamic_cast<CSegmentButton*> (control))
		{
			segmentButton->removeAllSegments ();
			for (IStepValue::StepType i = 0; i < stepValue->getSteps (); ++i)
			{
				auto title = valueConverter.valueAsString (stepValue->stepToValue (i));
				CSegmentButton::Segment segment;
				segment.name = title;
				segmentButton->addSegment (std::move (segment));
			}
		}
	}

	void onStateChange (IValue&) override
	{
		for (auto& c : controls)
		{
			updateControlOnStateChange (c);
			c->valueChanged ();
		}
	}

	void valueChanged (CControl* control) override
	{
		if (updateControl == control)
			return;

		auto newValue = static_cast<IValue::Type> (control->getValueNormalized ());
		value->performEdit (newValue);
	}

	void controlBeginEdit (CControl* control) override { value->beginEdit (); }
	void controlEndEdit (CControl* control) override { value->endEdit (); }

	void addControl (CControl* control)
	{
		if (auto paramDisplay = dynamic_cast<CParamDisplay*> (control))
		{
			paramDisplay->setValueToStringFunction2 (
			    [this] (float val, std::string& utf8String, CParamDisplay* display) {
				    utf8String = this->value->getConverter ().valueAsString (val);
				    return true;
			    });
			if (auto textEdit = dynamic_cast<CTextEdit*> (paramDisplay))
			{
				textEdit->setStringToValueFunction (
				    [&] (UTF8StringPtr txt, float& result, CTextEdit* textEdit) {
					    auto v = value->getConverter ().stringAsValue (txt);
					    if (v == IValue::InvalidValue)
						    v = value->getValue ();
					    result = static_cast<float> (v);
					    return true;
				    });
			}
		}
		else if (auto listControl = dynamic_cast<CListControl*> (control))
		{
			if (auto stringListDrawer =
			        dynamic_cast<StringListControlDrawer*> (listControl->getDrawer ()))
			{
				stringListDrawer->setStringProvider ([this] (int32_t row) {
					auto min = this->value->getConverter ().normalizedToPlain (0.);
					auto norm = this->value->getConverter ().plainToNormalized (row + min);
					auto string = this->value->getConverter ().valueAsString (norm);
					return shared (string.getPlatformString ());
				});
			}
		}
		updateControlOnStateChange (control);
		control->beginEdit ();
		control->setValueNormalized (static_cast<float> (value->getValue ()));
		control->valueChanged ();
		control->endEdit ();
		control->invalid ();
		control->registerControlListener (this);
		control->registerViewListener (this);
		controls.emplace_back (control);
	}

	void removeControl (CControl* control)
	{
		auto it = std::find (controls.begin (), controls.end (), control);
		if (it != controls.end ())
		{
			onRemoveControl (control);
			controls.erase (it);
		}
	}

	void viewWillDelete (CView* view) override { removeControl (dynamic_cast<CControl*> (view)); }

	const ControlList& getControls () const { return controls; }

protected:
	void onRemoveControl (CControl* control)
	{
		if (auto paramDisplay = dynamic_cast<CParamDisplay*> (control))
		{
			paramDisplay->setValueToStringFunction (nullptr);
			if (auto textEdit = dynamic_cast<CTextEdit*> (paramDisplay))
				textEdit->setStringToValueFunction (nullptr);
		}
		else if (auto listControl = dynamic_cast<CListControl*> (control))
		{
			if (auto stringListDrawer =
			        dynamic_cast<StringListControlDrawer*> (listControl->getDrawer ()))
			{
				stringListDrawer->setStringProvider (nullptr);
			}
		}
		control->unregisterViewListener (this);
		control->unregisterControlListener (this);
	}

	ValuePtr value;
	ControlList controls;
	CControl* updateControl {nullptr};
};

using ValueWrapperPtr = std::unique_ptr<ValueWrapper>;

//------------------------------------------------------------------------
struct WindowController::Impl : public IController, public ICommandHandler
{
	using ValueWrapperList = std::vector<ValueWrapperPtr>;

	Impl (WindowController& controller, const ModelBindingPtr& modelHandler,
	      const CustomizationPtr& customization)
	: controller (controller), modelBinding (modelHandler), customization (customization)
	{
		initModelValues (modelHandler);
	}

	~Impl () override
	{
		if (uiDesc && uiDesc->getSharedResources ())
		{
			uiDesc->setSharedResources (nullptr);
		}
		for (auto& vw : valueWrappers)
		{
			for (auto control : vw->getControls ())
			{
				if (control->getListener () == this)
					control->setListener (nullptr);
			}
		}
	}

	virtual bool init (WindowPtr& inWindow, const char* inFileName, const char* inTemplateName)
	{
		window = inWindow.get ();
		if (!initUIDesc (inFileName))
			return false;
		frame = makeOwned<CFrame> (CRect (), nullptr);
		frame->setTransparency (true);
		templateName = inTemplateName;

		showView ();

		window->setContentView (frame);
		return true;
	}

	bool initStatic (WindowPtr& inWindow, UTF8String inXml, const char* inTemplateName)
	{
		window = inWindow.get ();
		MemoryContentProvider xmlContentProvider (inXml, static_cast<uint32_t> (inXml.length ()));
		uiDesc = makeOwned<UIDescription> (&xmlContentProvider);
		if (!uiDesc->parse ())
			return false;
		if (customization)
			customization->onUIDescriptionParsed (uiDesc);

		frame = makeOwned<CFrame> (CRect (), nullptr);
		frame->setTransparency (true);
		templateName = inTemplateName;

		showView ();

		window->setContentView (frame);
		return true;
	}

	virtual CPoint constraintSize (const CPoint& newSize)
	{
		if (customization)
		{
			if (auto customController = dynamicPtrCast<IWindowController> (customization))
			{
				auto p = customController->constraintSize (*window, newSize);
				if (p != newSize)
					return p;
			}
		}
		CPoint p (newSize);
		if (minSize.x > 0 && minSize.y > 0)
		{
			p.x = std::max (p.x, minSize.x);
			p.y = std::max (p.y, minSize.y);
		}
		if (maxSize.x > 0 && maxSize.y > 0)
		{
			p.x = std::min (p.x, maxSize.x);
			p.y = std::min (p.y, maxSize.y);
		}
		return p;
	}

	virtual void beforeShow ()
	{
		if (customization)
		{
			if (auto customController = dynamicPtrCast<IWindowController> (customization))
				customController->beforeShow (*window);
		}
	}

	virtual bool canClose ()
	{
		if (customization)
		{
			if (auto customController = dynamicPtrCast<IWindowController> (customization))
				return customController->canClose (*window);
		}
		return true;
	}

	PlatformFrameConfigPtr createPlatformFrameConfig (PlatformType platformType)
	{
		if (customization)
		{
			if (auto customController = dynamicPtrCast<IWindowController> (customization))
				return customController->createPlatformFrameConfig (platformType);
		}
		return nullptr;
	}

	void onSetContentView (const SharedPointer<CFrame>& contentView)
	{
		if (customization)
		{
			if (auto customController = dynamicPtrCast<IWindowController> (customization))
				customController->onSetContentView (*window, contentView);
		}
	}

	virtual void onSizeChanged (const CPoint& newSize)
	{
		if (customization)
		{
			if (auto customController = dynamicPtrCast<IWindowController> (customization))
				customController->onSizeChanged (*window, newSize);
		}
	}

	void onPositionChanged (const CPoint& newPos)
	{
		if (customization)
		{
			if (auto customController = dynamicPtrCast<IWindowController> (customization))
				customController->onPositionChanged (*window, newPos);
		}
	}

	void onShow ()
	{
		if (customization)
		{
			if (auto customController = dynamicPtrCast<IWindowController> (customization))
				customController->onShow (*window);
		}
	}

	void onHide ()
	{
		if (customization)
		{
			if (auto customController = dynamicPtrCast<IWindowController> (customization))
				customController->onHide (*window);
		}
	}

	virtual void onClosed ()
	{
		if (customization)
		{
			if (auto customController = dynamicPtrCast<IWindowController> (customization))
				customController->onClosed (*window);
		}
	}

	void onActivated ()
	{
		if (customization)
		{
			if (auto customController = dynamicPtrCast<IWindowController> (customization))
				customController->onActivated (*window);
		}
	}

	void onDeactivated ()
	{
		if (customization)
		{
			if (auto customController = dynamicPtrCast<IWindowController> (customization))
				customController->onDeactivated (*window);
		}
	}

	bool initUIDesc (const char* fileName)
	{
		if (Detail::getApplicationPlatformAccess ()
		        ->getConfiguration ()
		        .useCompressedUIDescriptionFiles)
			uiDesc = makeOwned<CompressedUIDescription> (fileName);
		else
			uiDesc = makeOwned<UIDescription> (fileName);
		uiDesc->setSharedResources (Detail::getSharedUIDescription ());
		if (!uiDesc->parse ())
		{
			return false;
		}
		if (customization)
			customization->onUIDescriptionParsed (uiDesc);
		return true;
	}

	void updateMinMaxSizes ()
	{
		const UIAttributes* attr = uiDesc->getViewAttributes (templateName);
		if (!attr)
			return;
		CPoint p;
		if (attr->getPointAttribute (kTemplateAttributeMinSize, p))
			minSize = p;
		else
			minSize = {};
		if (attr->getPointAttribute (kTemplateAttributeMaxSize, p))
			maxSize = p;
		else
			maxSize = {};
	}

	void showView ()
	{
		frame->removeAll ();
		updateMinMaxSizes ();
		auto view = uiDesc->createView (templateName, this);
		if (!view)
		{
			return;
		}
		auto viewSize = view->getViewSize ().getSize ();
		frame->getTransform ().transform (viewSize);
		frame->setSize (viewSize.x, viewSize.y);
		frame->addView (view);

		auto focusDrawing = uiDesc->getFocusDrawingSettings ();
		frame->setFocusDrawingEnabled (focusDrawing.enabled);
		if (focusDrawing.enabled)
		{
			frame->setFocusWidth (focusDrawing.width);
			CColor focusColor;
			if (uiDesc->getColor (focusDrawing.colorName.data (), focusColor))
				frame->setFocusColor (focusColor);
		}

		window->setSize (view->getViewSize ().getSize ());
	}

	CView* currentCommandHandlerCandidate ()
	{
		if (auto focusView = frame->getFocusView ())
			return focusView;
		return frame->getView (0);
	}

	bool canHandleCommand (const Command& command) override
	{
		bool canHandle = false;
		if (modelBinding)
		{
			if (auto commandHandler = dynamicPtrCast<ICommandHandler> (modelBinding))
				canHandle = commandHandler->canHandleCommand (command);
		}
		if (!canHandle && customization)
		{
			if (auto commandHandler = dynamicPtrCast<ICommandHandler> (customization))
				canHandle = commandHandler->canHandleCommand (command);
		}
		if (!canHandle)
		{
			if (auto view = currentCommandHandlerCandidate ())
			{
				if (auto viewController = getViewController (view, true))
				{
					if (auto viewCommandHandler = dynamic_cast<ICommandHandler*> (viewController))
					{
						canHandle = viewCommandHandler->canHandleCommand (command);
					}
				}
			}
		}
		return canHandle;
	}

	bool handleCommand (const Command& command) override
	{
		bool handled = false;
		if (modelBinding)
		{
			if (auto commandHandler = dynamicPtrCast<ICommandHandler> (modelBinding))
				handled = commandHandler->handleCommand (command);
		}
		if (!handled && customization)
		{
			if (auto commandHandler = dynamicPtrCast<ICommandHandler> (customization))
				handled = commandHandler->handleCommand (command);
		}
		if (!handled)
		{
			if (auto view = currentCommandHandlerCandidate ())
			{
				if (auto viewController = getViewController (view, true))
				{
					if (auto viewCommandHandler = dynamic_cast<ICommandHandler*> (viewController))
					{
						handled = viewCommandHandler->handleCommand (command);
					}
				}
			}
		}
		return handled;
	}

	void initModelValues (const ModelBindingPtr& modelHandler)
	{
		if (!modelHandler)
			return;
		valueWrappers.reserve (modelHandler->getValues ().size ());
		for (auto& value : modelHandler->getValues ())
		{
			valueWrappers.emplace_back (std::make_unique<ValueWrapper> (value));
		}
	}

	// IController
	void valueChanged (CControl* control) override {}
	int32_t controlModifierClicked (CControl* control, CButtonState button) override { return 0; }
	void controlBeginEdit (CControl* control) override {}
	void controlEndEdit (CControl* control) override {}
	void controlTagWillChange (CControl* control) override
	{
		if (control->getTag () < 0)
			return;
		auto index = static_cast<ValueWrapperList::size_type> (control->getTag ());
		if (index < valueWrappers.size ())
			valueWrappers[index]->removeControl (control);
	}
	void controlTagDidChange (CControl* control) override
	{
		if (control->getTag () < 0)
			return;
		auto index = static_cast<ValueWrapperList::size_type> (control->getTag ());
		if (index < valueWrappers.size ())
			valueWrappers[index]->addControl (control);
	}

	int32_t getTagForName (UTF8StringPtr name, int32_t registeredTag) const override
	{
		if (auto index =
		        indexOfTest (valueWrappers.begin (), valueWrappers.end (),
		                     [&] (const ValueWrapperPtr& v) { return v->getID () == name; }))
			return *index;
		return registeredTag;
	}

	IControlListener* getControlListener (UTF8StringPtr controlTagName) override { return this; }
	CView* createView (const UIAttributes& attributes, const IUIDescription* description) override
	{
		return nullptr;
	}
	CView* verifyView (CView* view, const UIAttributes& attributes,
	                   const IUIDescription* description) override
	{
		auto* control = dynamic_cast<CControl*> (view);
		if (control)
		{
			if (control->getListener () == nullptr)
				control->setListener (this);
			auto index = static_cast<ValueWrapperList::size_type> (control->getTag ());
			if (index < valueWrappers.size ())
			{
				valueWrappers[index]->updateControlOnStateChange (control);
			}
		}
		return view;
	}
	IController* createSubController (UTF8StringPtr name,
	                                  const IUIDescription* description) override
	{
		if (customization)
			return customization->createController (name, this, description);
		return nullptr;
	}

	WindowController& controller;
	IWindow* window {nullptr};
	SharedPointer<VSTGUI::UIDescription> uiDesc;
	SharedPointer<CFrame> frame;
	UTF8String templateName;
	CPoint minSize;
	CPoint maxSize;
	ModelBindingPtr modelBinding;
	CustomizationPtr customization;
	ValueWrapperList valueWrappers;
};

#if VSTGUI_LIVE_EDITING
//------------------------------------------------------------------------
struct WindowController::EditImpl : WindowController::Impl
{
	EditImpl (WindowController& controller, const ModelBindingPtr& modelBinding,
	          const CustomizationPtr& customization)
	: Impl (controller, modelBinding, customization)
	{
		IApplication::instance ().registerCommand (Commands::Debug::ToggleInlineUIEditor, 'E');
		IApplication::instance ().registerCommand (Commands::Debug::RecreateView, 0);
		if (IApplication::instance ().getDelegate ().getSharedUIResourceFilename ())
			IApplication::instance ().registerCommand (Commands::Debug::ResaveSharedResources, 0);
	}

	bool init (WindowPtr& inWindow, const char* fileName, const char* templateName) override
	{
		this->filename = fileName;
		if (auto absPath = Detail::getEditFileMap ().get (fileName))
			fileName = *absPath;
		window = inWindow.get ();
		frame = makeOwned<CFrame> (CRect (), nullptr);
		frame->setTransparency (true);

		if (!initUIDesc (fileName))
		{
			auto attr = makeOwned<UIAttributes> ();
			attr->setAttribute (UIViewCreator::kAttrClass, "CViewContainer");
			attr->setAttribute (UIViewCreator::kAttrSize, "300, 300");
			attr->setAttribute (UIViewCreator::kAttrAutosize, "left right top bottom");
			uiDesc->addNewTemplate (templateName, attr);

			initAsNew ();
		}
		else
		{
			auto settings = uiDesc->getCustomAttributes ("UIDescFilePath", true);
			auto filePath = settings->getAttributeValue ("path");
			if (filePath)
			{
				if (auto file = fopen (filePath->data (), "rb"))
				{
					uiDesc->setFilePath (filePath->data ());
					fclose (file);
				}
			}
		}
		this->templateName = templateName;

		syncTags ();
		showView ();

		window->setContentView (frame);

		return true;
	}

	CPoint constraintSize (const CPoint& newSize) override
	{
		if (isEditing)
			return newSize;
		return Impl::constraintSize (newSize);
	}

	void onSizeChanged (const CPoint& newSize) override
	{
		if (isEditing)
			return;
		Impl::onSizeChanged (newSize);
	}

	void onClosed () override
	{
		enableEditing (false);
		Impl::onClosed ();
	}

	bool canClose () override
	{
		if (frame->getModalView ())
			return false;
		return Impl::canClose ();
	}

	void initAsNew ()
	{
		Detail::PreventPopupClose ppc (*window);

		if (Detail::initUIDescAsNew (*uiDesc, frame))
		{
			enableEditing (true, true);
			save (true);
			enableEditing (false);
		}
		else
		{
			Async::schedule (Async::mainQueue (), [this] () { window->close (); });
		}
	}

	void checkFileExists ()
	{
		Detail::PreventPopupClose ppc (*window);

		auto result = Detail::checkAndUpdateUIDescFilePath (*uiDesc, frame);
		if (result == Detail::UIDescCheckFilePathResult::Exists)
			return;
		if (result == Detail::UIDescCheckFilePathResult::NewPathSet)
		{
			save (true);
			return;
		}
		enableEditing (false);
	}

	void save (bool force = false)
	{
		if (!uiEditController)
			return;
		if (force || uiEditController->getUndoManager ()->isSavePosition () == false)
		{
			if (uiEditController->getUndoManager ()->isSavePosition () == false)
				Detail::saveSharedUIDescription ();
			int32_t flags = UIDescription::kWriteImagesIntoUIDescFile |
			                CompressedUIDescription::kForceWriteCompressedDesc;

			// filter out attributes we will always override with the values from the parameters
			auto filter = [] (CView* view, const std::string& name) -> bool {
				if (auto control = dynamic_cast<CControl*> (view))
				{
					if (control->getTag () >= 0)
					{
						if (name == UIViewCreator::kAttrMinValue)
							return false;
						if (name == UIViewCreator::kAttrMaxValue)
							return false;
						if (name == UIViewCreator::kAttrDefaultValue)
							return false;
						if (name == UIViewCreator::kAttrMouseEnabled)
							return false;
						if (name == UIViewCreator::kAttrTitle &&
							dynamic_cast<CTextLabel*> (control))
							return false;
					}
				}
				return true;
			};

			if (!uiDesc->save (uiDesc->getFilePath (), flags, filter))
			{
				AlertBoxConfig config;
				config.headline = "Saving the uidesc file failed.";
				IApplication::instance ().showAlertBox (config);
				uiDesc->setFilePath ("");
				checkFileExists ();
			}
			else
				Detail::getEditFileMap ().set (filename, uiDesc->getFilePath ());
		}
	}

	void syncTags ()
	{
		std::list<const std::string*> tagNames;
		uiDesc->collectControlTagNames (tagNames);
		int32_t index = 0;
		for (auto& v : valueWrappers)
		{
			auto it = std::find_if (tagNames.begin (), tagNames.end (),
			                        [&] (const std::string* name) { return v->getID () == *name; });
			auto create = it == tagNames.end ();
			uiDesc->changeControlTagString (v->getID (), std::to_string (index), create);
			++index;
		}
		// now remove all old tags
		for (auto& name : tagNames)
		{
			auto it = std::find_if (valueWrappers.begin (), valueWrappers.end (),
			                        [&] (const ValueWrapperList::value_type& value) {
				                        return value->getID () == *name;
			                        });
			if (it == valueWrappers.end ())
				uiDesc->removeTag (name->data ());
		}
	}

	void enableEditing (bool state, bool ignoreCheckFileExist = false)
	{
		if (isEditing == state && frame->getNbViews () != 0)
			return;
		isEditing = state;
		if (!state)
		{
			save ();
			uiEditController = nullptr;
		}

		frame->removeAll ();
		if (state)
		{
			uiDesc->setController (this);
			uiEditController = new UIEditController (uiDesc);
			auto view = uiEditController->createEditView ();
			auto viewSize = view->getViewSize ().getSize ();
			frame->getTransform ().transform (viewSize);
			frame->setSize (viewSize.x, viewSize.y);
			frame->addView (view);
			frame->enableTooltips (true);
			CColor focusColor = kBlueCColor;
			uiEditController->getEditorDescription ()->getColor ("focus", focusColor);
			frame->setFocusColor (focusColor);
			frame->setFocusDrawingEnabled (true);
			frame->setFocusWidth (1);
			window->setSize (view->getViewSize ().getSize ());
			if (auto menuController = uiEditController->getMenuController ())
			{
				if (auto menu = menuController->getFileMenu ())
				{
					menu->removeAllEntry ();
					menu->setMouseEnabled (false);
				}
			}
			if (!ignoreCheckFileExist)
				checkFileExists ();
		}
		else
		{
			showView ();
		}
	}

	bool canHandleCommand (const Command& command) override
	{
		using namespace Commands;
		if (command == Debug::ToggleInlineUIEditor || command == Debug::RecreateView)
			return frame->getModalView () == nullptr;
		if (command == Debug::ResaveSharedResources)
			return true;
		else if (uiEditController && uiEditController->getMenuController ()->canHandleCommand (
		                                 command.group, command.name))
			return true;
		return Impl::canHandleCommand (command);
	}

	bool handleCommand (const Command& command) override
	{
		using namespace Commands;
		if (command == Debug::ToggleInlineUIEditor)
		{
			enableEditing (!isEditing);
			return true;
		}
		else if (command == Debug::RecreateView)
		{
			showView ();
			return true;
		}
		else if (command == Debug::ResaveSharedResources)
		{
			Detail::saveSharedUIDescription ();
			return true;
		}
		else if (uiEditController && uiEditController->getMenuController ()->handleCommand (
		                                 command.group, command.name))
			return true;
		return Impl::handleCommand (command);
	}

	SharedPointer<UIEditController> uiEditController;
	bool isEditing {false};
	std::string filename;
};
#endif

//------------------------------------------------------------------------
bool WindowController::initStatic (const UIDesc::Config& config, WindowPtr& window)
{
	impl = std::unique_ptr<Impl> (new Impl (*this, config.modelBinding, config.customization));
	return impl->initStatic (window, config.uiDescFileName, config.viewName);
}

//------------------------------------------------------------------------
bool WindowController::init (const UIDesc::Config& config, WindowPtr& window)
{
#if VSTGUI_LIVE_EDITING
	impl = std::unique_ptr<Impl> (new EditImpl (*this, config.modelBinding, config.customization));
#else
	impl = std::unique_ptr<Impl> (new Impl (*this, config.modelBinding, config.customization));
#endif
	return impl->init (window, config.uiDescFileName, config.viewName);
}

//------------------------------------------------------------------------
CPoint WindowController::constraintSize (const IWindow& window, const CPoint& newSize)
{
	return impl ? impl->constraintSize (newSize) : newSize;
}

//------------------------------------------------------------------------
bool WindowController::canClose (const IWindow& window)
{
	return impl ? impl->canClose () : true;
}

//------------------------------------------------------------------------
void WindowController::onClosed (const IWindow& window)
{
	if (impl)
		impl->onClosed ();
	impl = nullptr;
}

//------------------------------------------------------------------------
void WindowController::beforeShow (IWindow& window)
{
	if (impl)
		impl->beforeShow ();
}

//------------------------------------------------------------------------
PlatformFrameConfigPtr WindowController::createPlatformFrameConfig (PlatformType platformType)
{
	if (impl)
		return impl->createPlatformFrameConfig (platformType);
	return nullptr;
}

//------------------------------------------------------------------------
void WindowController::onSetContentView (IWindow& window, const SharedPointer<CFrame>& contentView)
{
	if (impl)
		impl->onSetContentView (contentView);
}

//------------------------------------------------------------------------
void WindowController::onSizeChanged (const IWindow& window, const CPoint& newSize)
{
	if (impl)
		impl->onSizeChanged (newSize);
}

//------------------------------------------------------------------------
void WindowController::onPositionChanged (const IWindow& window, const CPoint& newPosition)
{
	if (impl)
		impl->onPositionChanged (newPosition);
}

//------------------------------------------------------------------------
void WindowController::onShow (const IWindow& window)
{
	if (impl)
		impl->onShow ();
}

//------------------------------------------------------------------------
void WindowController::onHide (const IWindow& window)
{
	if (impl)
		impl->onHide ();
}

//------------------------------------------------------------------------
void WindowController::onActivated (const IWindow& window)
{
	if (impl)
		impl->onActivated ();
}

//------------------------------------------------------------------------
void WindowController::onDeactivated (const IWindow& window)
{
	if (impl)
		impl->onDeactivated ();
}

//------------------------------------------------------------------------
bool WindowController::canHandleCommand (const Command& command)
{
	return impl->canHandleCommand (command);
}

//------------------------------------------------------------------------
bool WindowController::handleCommand (const Command& command)
{
	return impl->handleCommand (command);
}

//------------------------------------------------------------------------
const IMenuBuilder* WindowController::getWindowMenuBuilder (const IWindow& window) const
{
	if (auto menuBuilder = dynamicPtrCast<IMenuBuilder> (impl->customization))
		return menuBuilder.get ();
	return nullptr;
}

ModelBindingPtr WindowController::getModelBinding () const { return impl->modelBinding; }
CustomizationPtr WindowController::getCustomization () const { return impl->customization; }

//------------------------------------------------------------------------
} // Anonymous

//------------------------------------------------------------------------
namespace UIDesc {

static const UTF8StringView staticXmlIdentifier ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");

//------------------------------------------------------------------------
WindowPtr makeWindow (const Config& config)
{
	vstgui_assert (config.viewName.empty () == false);
	vstgui_assert (config.uiDescFileName.empty () == false);

	auto controller = std::make_shared<WindowController> ();

	if (UTF8StringView (config.uiDescFileName).startsWith (staticXmlIdentifier))
	{
		auto window = IApplication::instance ().createWindow (config.windowConfig, controller);
		if (!window)
			return nullptr;

		if (!controller->initStatic (config, window))
			return nullptr;

		return window;
	}

#if VSTGUI_LIVE_EDITING
	WindowConfiguration windowConfig = config.windowConfig;
	windowConfig.style.size ();
#else
	auto& windowConfig = config.windowConfig;
#endif

	auto window = IApplication::instance ().createWindow (windowConfig, controller);
	if (!window)
		return nullptr;

	if (!controller->init (config, window))
		return nullptr;

	return window;
}

//------------------------------------------------------------------------
} // UIDesc
} // Standalone
} // VSTGUI
