// 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 "uiviewfactory.h"
#include "uiattributes.h"
#include "../lib/cview.h"
#include "../lib/cstring.h"
#include "detail/uiviewcreatorattributes.h"
#include "../lib/platform/std_unorderedmap.h"

namespace VSTGUI {

/** @class IViewCreator

	You can register your own custom views with the UIViewFactory by inheriting from this interface and register it
	with UIViewFactory::registerViewCreator().
	
	Example for an imaginary view class called MyView which directly inherites from CView:
	@code
	class MyViewCreator : public ViewCreatorAdapter
	{
	public:
		// register this class with the view factory
		MyViewCreator () { UIViewFactory::registerViewCreator (*this); }
		
		// return an unique name here
		IdStringPtr getViewName () const { return "MyView"; }

		// return the name here from where your custom view inherites.
		// Your view automatically supports the attributes from it.
		IdStringPtr getBaseViewName () const { return "CView"; }

		// create your view here.
		// Note you don't need to apply attributes here as the apply method will be called with this new view
		CView* create (const UIAttributes& attributes, const IUIDescription* description) const { return new MyView (); }

		// apply custom attributes to your view		
		bool apply (CView* view, const UIAttributes& attributes, const IUIDescription* description) const
		{
			MyView* myView = dynamic_cast<MyView*> (view);
			if (myView == 0)
				return false;
			int32_t value;
			if (attributes.getIntegerAttribute ("my-custom-attribute", value))
				myView->setCustomAttribute (value);
			return true;
		}

		// add your custom attributes to the list		
		bool getAttributeNames (StringList& attributeNames) const
		{
			attributeNames.emplace_back ("my-custom-attribute");
			return true;
		}
		
		// return the type of your custom attributes
		AttrType getAttributeType (const std::string& attributeName) const
		{
			if (attributeName == "my-custom-attribute")
				return kIntegerType;
			return kUnknownType;
		}
		
		// return the string value of the custom attributes of the view
		bool getAttributeValue (CView* view, const string& attributeName, string& stringValue, const IUIDescription* desc) const
		{
			MyView* myView = dynamic_cast<MyView*> (view);
			if (myView == 0)
				return false;
			if (attributeName == "my-custom-attribute")
			{
				stringValue = UIAttributes::integerToString (myView->getCustomAttribute ());
				return true;
			}
			return false;
		}
	};
	// create a static instance so that it registers itself with the view factory
	MyViewCreator __gMyViewCreator;
	
	@endcode
*/

using ViewCreatorRegistryMap = std::unordered_map<std::string, const IViewCreator*>;

//-----------------------------------------------------------------------------
class ViewCreatorRegistry : private ViewCreatorRegistryMap
{
public:
	using const_iterator = ViewCreatorRegistryMap::const_iterator;

	const_iterator begin () { return ViewCreatorRegistryMap::begin (); }
	const_iterator end () { return ViewCreatorRegistryMap::end (); }

	const_iterator find (IdStringPtr name)
	{
		if (name)
			return ViewCreatorRegistryMap::find (name);
		return end ();
	}

	void add (IdStringPtr name, const IViewCreator* viewCreator)
	{
#if DEBUG
		if (find (viewCreator->getViewName ()) != end ())
		{
			DebugPrint ("ViewCreateFunction for '%s' already registered\n", viewCreator->getViewName ());
		}
#endif
		insert (std::make_pair (viewCreator->getViewName (), viewCreator));
		
	}

	void remove (const IViewCreator* viewCreator)
	{
		auto it = find (viewCreator->getViewName ());
		if (it == end ())
			return;
		erase (it);
	}
};

//-----------------------------------------------------------------------------
static ViewCreatorRegistry& getCreatorRegistry ()
{
	static ViewCreatorRegistry creatorRegistry;
	return creatorRegistry;
}

//-----------------------------------------------------------------------------
static CViewAttributeID kViewNameAttribute = 'cvcr';

//-----------------------------------------------------------------------------
UIViewFactory::UIViewFactory ()
{
}

//------------------------------------------------------------------------
bool UIViewFactory::viewIsTypeOf (CView* view, const std::string& typeName) const
{
	auto viewTypeName = getViewName (view);
	if (typeName == viewTypeName)
		return true;
	auto& registry = getCreatorRegistry ();
	auto iter = registry.find (viewTypeName);
	while (iter != registry.end ())
	{
		auto baseViewTypeName = iter->second->getBaseViewName ();
		if (baseViewTypeName == nullptr)
			return false;
		if (typeName == baseViewTypeName)
			return true;
		iter = registry.find (baseViewTypeName);
	}
	return false;
}

//-----------------------------------------------------------------------------
CView* UIViewFactory::createViewByName (const std::string* className, const UIAttributes& attributes, const IUIDescription* description) const
{
	auto& registry = getCreatorRegistry ();
	auto iter = registry.find (className->c_str ());
	if (iter != registry.end ())
	{
		CView* view = (*iter).second->create (attributes, description);
		if (view)
		{
			IdStringPtr viewName = (*iter).second->getViewName ();
			view->setAttribute (kViewNameAttribute, viewName);
			UIAttributes evaluatedAttributes;
			evaluateAttributesAndRemember (view, attributes, evaluatedAttributes, description);
			while (iter != registry.end () && (*iter).second->apply (view, evaluatedAttributes, description))
			{
				if ((*iter).second->getBaseViewName () == nullptr)
					break;
				iter = registry.find ((*iter).second->getBaseViewName ());
			}
			return view;
		}
	}
	else
	{
	#if DEBUG
		DebugPrint ("UIViewFactory::createView(..): Could not find view of class: %s\n", className->c_str ());
	#endif
	}
	return nullptr;
}

//-----------------------------------------------------------------------------
CView* UIViewFactory::createView (const UIAttributes& attributes, const IUIDescription* description) const
{
	const std::string* className = attributes.getAttributeValue (UIViewCreator::kAttrClass);
	if (className)
		return createViewByName (className, attributes, description);
	std::string viewContainerName ("CViewContainer");
	return createViewByName (&viewContainerName, attributes, description);
}

//-----------------------------------------------------------------------------
bool UIViewFactory::applyAttributeValues (CView* view, const UIAttributes& attributes, const IUIDescription* desc) const
{
	bool result = false;
	auto& registry = getCreatorRegistry ();
	auto iter = registry.find (getViewName (view));

	UIAttributes evaluatedAttributes;
	evaluateAttributesAndRemember (view, attributes, evaluatedAttributes, desc);
	
	while (iter != registry.end () && (result = (*iter).second->apply (view, evaluatedAttributes, desc)) && (*iter).second->getBaseViewName ())
	{
		iter = registry.find ((*iter).second->getBaseViewName ());
	}
	return result;
}

//-----------------------------------------------------------------------------
bool UIViewFactory::applyCustomViewAttributeValues (CView* customView, IdStringPtr baseViewName, const UIAttributes& attributes, const IUIDescription* desc) const
{
	bool result = false;
	auto& registry = getCreatorRegistry ();
	auto iter = registry.find (baseViewName);
	if (iter != registry.end ())
	{
		IdStringPtr viewName = (*iter).second->getViewName ();
		customView->setAttribute (kViewNameAttribute, viewName);
	}
	UIAttributes evaluatedAttributes;
	evaluateAttributesAndRemember (customView, attributes, evaluatedAttributes, desc);
	while (iter != registry.end () && (result = (*iter).second->apply (customView, evaluatedAttributes, desc)) && (*iter).second->getBaseViewName ())
	{
		iter = registry.find ((*iter).second->getBaseViewName ());
	}
	return result;
}

//-----------------------------------------------------------------------------
IdStringPtr IViewFactory::getViewName (CView* view)
{
	IdStringPtr viewName = nullptr;
	uint32_t size = sizeof (IdStringPtr);
	view->getAttribute (kViewNameAttribute, size, &viewName, size);
	return viewName;
}

//-----------------------------------------------------------------------------
void UIViewFactory::evaluateAttributesAndRemember (CView* view, const UIAttributes& attributes, UIAttributes& evaluatedAttributes, const IUIDescription* description) const
{
	std::string evaluatedValue;
	for (const auto& attr : attributes)
	{
		const std::string& value = attr.second;
		if (description && description->getVariable (value.c_str (), evaluatedValue))
		{
		#if VSTGUI_LIVE_EDITING
			rememberAttribute (view, attr.first.c_str (), value.c_str ());
		#endif
			evaluatedAttributes.setAttribute (attr.first, evaluatedValue);
		}
		else
		{
		#if VSTGUI_LIVE_EDITING
			auto type = getAttributeType (view, attr.first);
			switch (type)
			{
				case IViewCreator::kColorType:
				case IViewCreator::kTagType:
				case IViewCreator::kFontType:
				case IViewCreator::kGradientType:
					rememberAttribute (view, attr.first.c_str (), value.c_str ());
					break;
				default:
					break;
			}
		#endif
			evaluatedAttributes.setAttribute (attr.first, value);
		}
	}
}

//-----------------------------------------------------------------------------
bool UIViewFactory::getAttributeValue (CView* view, const std::string& attributeName, std::string& stringValue, const IUIDescription* desc) const
{
#if VSTGUI_LIVE_EDITING
	bool result = getRememberedAttribute (view, attributeName.c_str (), stringValue);
	if (result == false)
	{
#else
	bool result = false;
#endif
		auto& registry = getCreatorRegistry ();
		auto iter = registry.find (getViewName (view));
		while (iter != registry.end () && !(result = (*iter).second->getAttributeValue (view, attributeName, stringValue, desc)) && (*iter).second->getBaseViewName ())
		{
			iter = registry.find ((*iter).second->getBaseViewName ());
		}
#if VSTGUI_LIVE_EDITING
	}
#endif
	return result;
}

#if VSTGUI_LIVE_EDITING
//-----------------------------------------------------------------------------
bool UIViewFactory::getAttributeNamesForView (CView* view, StringList& attributeNames) const
{
	bool result = false;
	auto& registry = getCreatorRegistry ();
	auto iter = registry.find (getViewName (view));
	while (iter != registry.end () &&
		   (result = (*iter).second->getAttributeNames (attributeNames)) &&
		   (*iter).second->getBaseViewName ())
	{
		iter = registry.find ((*iter).second->getBaseViewName ());
	}
	return result;
}

//-----------------------------------------------------------------------------
IViewCreator::AttrType UIViewFactory::getAttributeType (CView* view, const std::string& attributeName) const
{
	auto& registry = getCreatorRegistry ();
	auto type = IViewCreator::kUnknownType;
	auto iter = registry.find (getViewName (view));
	while (iter != registry.end () && (type = (*iter).second->getAttributeType (attributeName)) == IViewCreator::kUnknownType && (*iter).second->getBaseViewName ())
	{
		iter = registry.find ((*iter).second->getBaseViewName ());
	}
	return type;
}

//-----------------------------------------------------------------------------
bool UIViewFactory::getPossibleAttributeListValues (CView* view, const std::string& attributeName, StringPtrList& values) const
{
	auto& registry = getCreatorRegistry ();
	auto iter = registry.find (getViewName (view));
	while (iter != registry.end () && (*iter).second->getPossibleListValues (attributeName, values) == false && (*iter).second->getBaseViewName ())
	{
		iter = registry.find ((*iter).second->getBaseViewName ());
	}
	return !values.empty ();
}

//-----------------------------------------------------------------------------
bool UIViewFactory::getAttributeValueRange (CView* view, const std::string& attributeName, double& minValue, double& maxValue) const
{
	minValue = maxValue = -1.;
	auto& registry = getCreatorRegistry ();
	auto iter = registry.find (getViewName (view));
	while (iter != registry.end () && (*iter).second->getAttributeValueRange (attributeName, minValue, maxValue) == false && (*iter).second->getBaseViewName ())
	{
		iter = registry.find ((*iter).second->getBaseViewName ());
	}
	return minValue != maxValue && minValue != -1.;
}

//-----------------------------------------------------------------------------
bool UIViewFactory::getAttributesForView (CView* view, const IUIDescription* desc, UIAttributes& attr) const
{
	bool result = false;
	StringList attrNames;
	if (getAttributeNamesForView (view, attrNames))
	{
		auto it = attrNames.begin ();
		while (it != attrNames.end ())
		{
			std::string value;
			if (getAttributeValue (view, *it, value, desc))
				attr.setAttribute (*it, value);
			it++;
		}
		attr.setAttribute (UIViewCreator::kAttrClass, getViewName (view));
		result = true;
	}
	return result;
}

//-----------------------------------------------------------------------------
void UIViewFactory::collectRegisteredViewNames (StringPtrList& viewNames, IdStringPtr _baseClassNameFilter) const
{
	UTF8StringView baseClassNameFilter (_baseClassNameFilter);
	auto& registry = getCreatorRegistry ();
	auto iter = registry.begin ();
	while (iter != registry.end ())
	{
		if (baseClassNameFilter)
		{
			bool found = false;
			auto iter2 = iter;
			while (iter2 != registry.end () && (*iter2).second->getBaseViewName ())
			{
				if (baseClassNameFilter == (*iter2).second->getViewName () || baseClassNameFilter == (*iter2).second->getBaseViewName ())
				{
					found = true;
					break;
				}
				iter2 = registry.find ((*iter2).second->getBaseViewName ());
			}
			if (!found)
			{
				iter++;
				continue;
			}
		}
		viewNames.emplace_back (&(*iter).first);
		iter++;
	}
	viewNames.sort ([] (const std::string* lhs, const std::string* rhs) { return *lhs < *rhs; });
}

//-----------------------------------------------------------------------------
auto UIViewFactory::collectRegisteredViewAndDisplayNames (IdStringPtr baseClassNameFilter) const -> ViewAndDisplayNameList
{
	ViewAndDisplayNameList list;
	auto& registry = getCreatorRegistry ();
	auto iter = registry.begin ();
	while (iter != registry.end ())
	{
		if (baseClassNameFilter)
		{
			UTF8StringView baseClassNameStr (baseClassNameFilter);
			bool found = false;
			auto iter2 = iter;
			while (iter2 != registry.end () && (*iter2).second->getBaseViewName ())
			{
				if (baseClassNameStr == (*iter2).second->getViewName () || baseClassNameStr == (*iter2).second->getBaseViewName ())
				{
					found = true;
					break;
				}
				iter2 = registry.find ((*iter2).second->getBaseViewName ());
			}
			if (!found)
			{
				iter++;
				continue;
			}
		}
		list.emplace_back (&(*iter).first, (*iter).second->getDisplayName ());
		iter++;
	}
	return list;
}

//------------------------------------------------------------------------
UTF8StringPtr UIViewFactory::getViewDisplayName (CView* view) const
{
	if (auto viewName = getViewName (view))
	{
		UTF8StringView viewNameStr (viewName);
		auto& registry = getCreatorRegistry ();
		for (auto& entry : registry)
		{
			if (viewNameStr == entry.second->getViewName ())
				return entry.second->getDisplayName ();
		}
	}
	return nullptr;
}

//-----------------------------------------------------------------------------
size_t UIViewFactory::createHash (const std::string& str)
{
	static std::hash<std::string> hashFunc;
	return hashFunc (str);
}

//-----------------------------------------------------------------------------
void UIViewFactory::rememberAttribute (CView* view, IdStringPtr attrName, const std::string& value) const
{
#if ENABLE_UNIT_TESTS
	if (disableRememberAttributes)
		return;
#endif
	auto hash = createHash (attrName);
	view->setAttribute (hash, static_cast<uint32_t> (value.size () + 1), value.c_str ());
}

//-----------------------------------------------------------------------------
bool UIViewFactory::getRememberedAttribute (CView* view, IdStringPtr attrName, std::string& value) const
{
	bool result = false;
	size_t hash = createHash (attrName);
	uint32_t attrSize = 0;
	if (view->getAttributeSize (hash, attrSize))
	{
		char* temp = new char[attrSize];
		if (view->getAttribute (hash, attrSize, temp, attrSize))
		{
			value = temp;
			result = true;
		}
		delete [] temp;
	}
	return result;
}

#endif

//-----------------------------------------------------------------------------
void UIViewFactory::registerViewCreator (const IViewCreator& viewCreator)
{
	auto& registry = getCreatorRegistry ();
	registry.add (viewCreator.getViewName (), &viewCreator);
}

//-----------------------------------------------------------------------------
void UIViewFactory::unregisterViewCreator (const IViewCreator& viewCreator)
{
	auto& registry = getCreatorRegistry ();
	registry.remove (&viewCreator);
}

} // VSTGUI
