// 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 "uiviewcreatecontroller.h"

#if VSTGUI_LIVE_EDITING

#include "uieditcontroller.h"
#include "uibasedatasource.h"
#include "../cstream.h"
#include "../../lib/cdropsource.h"
#include "../../lib/dragging.h"
#include "../../lib/controls/coptionmenu.h"
#include "../../lib/controls/csearchtextedit.h"
#include "../detail/uiviewcreatorattributes.h"

namespace VSTGUI {

//----------------------------------------------------------------------------------------------------
class UIViewCreatorDataSource : public UIBaseDataSource
{
public:
	UIViewCreatorDataSource (const IViewFactory* factory, UIDescription* description);

	CMouseEventResult dbOnMouseDown (const CPoint& where, const CButtonState& buttons, int32_t row, int32_t column, CDataBrowser* browser) override;
	CMouseEventResult dbOnMouseMoved (const CPoint& where, const CButtonState& buttons, int32_t row, int32_t column, CDataBrowser* browser) override;

	void getNames (std::list<const std::string*>& names) override;
	bool addItem (UTF8StringPtr name) override { return false; }
	bool removeItem (UTF8StringPtr name) override { return false; }
	bool performNameChange (UTF8StringPtr oldName, UTF8StringPtr newName) override { return false; }
	UTF8StringPtr getDefaultsName () override { return "UIViewCreatorDataSource"; }

	void addViewToCurrentEditView (int32_t row);
protected:
	SharedPointer<UISelection> createSelection (int32_t row);
	IViewFactoryEditingSupport::ViewAndDisplayNameList viewAndDisplayNameList;
	const IViewFactory* factory;
	DragStartMouseObserver dragStartMouseObserver;
};

//----------------------------------------------------------------------------------------------------
UIViewCreatorController::UIViewCreatorController (IController* baseController, UIDescription* description)
: DelegationController (baseController)
, description (description)
{
}

//----------------------------------------------------------------------------------------------------
UIViewCreatorController::~UIViewCreatorController ()
{
	if (dataSource)
		dataSource->forget ();
}

//----------------------------------------------------------------------------------------------------
CView* UIViewCreatorController::createView (const UIAttributes& attributes, const IUIDescription* _description)
{
	const std::string* name = attributes.getAttributeValue (IUIDescription::kCustomViewName);
	if (name)
	{
		if (*name == "ViewDataBrowser")
		{
			vstgui_assert (dataBrowser == nullptr);
			dataSource = new UIViewCreatorDataSource (description->getViewFactory (), description);
			UIEditController::setupDataSource (dataSource);
			dataBrowser = new CDataBrowser (CRect (0, 0, 0, 0), dataSource, CDataBrowser::kDrawRowLines|CScrollView::kHorizontalScrollbar | CScrollView::kVerticalScrollbar);
			return dataBrowser;
		}
	}
	return DelegationController::createView (attributes, _description);
}

//----------------------------------------------------------------------------------------------------
CView* UIViewCreatorController::verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* desc)
{
	auto searchField = dynamic_cast<CSearchTextEdit*>(view);
	if (searchField && searchField->getTag () == kSearchFieldTag)
	{
		dataSource->setSearchFieldControl (searchField);
	}
	return DelegationController::verifyView (view, attributes, desc);
}

//----------------------------------------------------------------------------------------------------
IControlListener* UIViewCreatorController::getControlListener (UTF8StringPtr name)
{
	if (std::strcmp (name, "viewcreator.search") == 0)
		return dataSource;
	return this;
}

//----------------------------------------------------------------------------------------------------
void UIViewCreatorController::valueChanged (CControl* control)
{
}

//----------------------------------------------------------------------------------------------------
void UIViewCreatorController::appendContextMenuItems (COptionMenu& contextMenu, const CPoint& where)
{
	auto cell = dataBrowser->getCellAt (where);
	if (!cell.isValid ())
		return;
	const auto& viewName = dataSource->getStringList ()->at (static_cast<uint32_t> (cell.row));
	UTF8String menuEntryName = "Insert '" + viewName + "'";
	auto item = new CCommandMenuItem (menuEntryName);
	item->setActions ([&, cell] (CCommandMenuItem* item) {
		dataSource->addViewToCurrentEditView (cell.row);
	});
	contextMenu.addEntry (item);
}

//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
UIViewCreatorDataSource::UIViewCreatorDataSource (const IViewFactory* factory,
												  UIDescription* description)
: UIBaseDataSource (description, nullptr, nullptr), factory (factory)
{
}

//----------------------------------------------------------------------------------------------------
void UIViewCreatorDataSource::getNames (std::list<const std::string*>& names)
{
	if (const auto* vfEditingSupport = dynamic_cast<const IViewFactoryEditingSupport*> (factory))
	{
		viewAndDisplayNameList = vfEditingSupport->collectRegisteredViewAndDisplayNames ();
		for (const auto& e : viewAndDisplayNameList)
		{
			names.emplace_back (&e.second);
		}
		names.sort ([] (const auto& lhs, const auto& rhs) { return *lhs < *rhs; });
	}
}

//----------------------------------------------------------------------------------------------------
void UIViewCreatorDataSource::addViewToCurrentEditView (int32_t row)
{
	UIViewCreatorController* controller = dynamic_cast<UIViewCreatorController*> (getViewController (dataBrowser, true));
	if (controller)
	{
		if (UIEditController* editController = dynamic_cast<UIEditController*> (controller->getBaseController ()))
		{
			SharedPointer<UISelection> selection = createSelection (row);
			editController->addSelectionToCurrentView (selection);
		}
	}
}

//----------------------------------------------------------------------------------------------------
SharedPointer<UISelection> createSelectionFromViewName (const std::string& viewName,
														const IViewFactory* factory,
														const UIDescription* description,
														const UIAttributes* optionalAttributes)
{
	SharedPointer<UISelection> selection;
	UIAttributes viewAttr;
	viewAttr.setAttribute (UIViewCreator::kAttrClass, viewName);
	if (optionalAttributes)
	{
		for (auto& a : *optionalAttributes)
			viewAttr.setAttribute (a.first, a.second);
	}
	CView* view = factory->createView (viewAttr, description);
	if (view)
	{
		if (view->getViewSize ().isEmpty ())
		{
			CRect size (CPoint (0, 0), CPoint (20, 20));
			view->setViewSize (size);
			view->setMouseableArea (size);
		}
		selection = makeOwned<UISelection> ();
		selection->add (view);
		view->forget ();
	}
	return selection;
}

//----------------------------------------------------------------------------------------------------
SharedPointer<UISelection> UIViewCreatorDataSource::createSelection (int32_t row)
{
	SharedPointer<UISelection> selection;
	auto viewDisplayName = getStringList ()->at (static_cast<uint32_t> (row)).getString ();
	auto it = std::find_if (viewAndDisplayNameList.begin (), viewAndDisplayNameList.end (),
	                        [&] (const auto& entry) { return entry.second == viewDisplayName; });
	if (it == viewAndDisplayNameList.end ())
		return nullptr;
	return createSelectionFromViewName (*it->first, factory, description, nullptr);
}

//----------------------------------------------------------------------------------------------------
CMouseEventResult UIViewCreatorDataSource::dbOnMouseDown (const CPoint& where, const CButtonState& buttons, int32_t row, int32_t column, CDataBrowser* browser)
{
	if (buttons.isLeftButton ())
	{
		dragStartMouseObserver.init (where);
		if (!buttons.isDoubleClick ())
			return kMouseEventHandled;
		addViewToCurrentEditView (row);
	}
	return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
}

//----------------------------------------------------------------------------------------------------
CMouseEventResult UIViewCreatorDataSource::dbOnMouseMoved (const CPoint& where, const CButtonState& buttons, int32_t row, int32_t column, CDataBrowser* browser)
{
	if (buttons.isLeftButton () && row != -1 && column != -1)
	{
		if (dragStartMouseObserver.shouldStartDrag (where))
		{
			auto selRow = dataBrowser->getSelection ().front ();
			SharedPointer<UISelection> selection = createSelection (selRow);
			CMemoryStream stream (1024, 1024, false);
			if (selection->store (stream, description))
			{
				stream.end ();
				auto dropSource = CDropSource::create (stream.getBuffer (),
				                                       static_cast<uint32_t> (stream.tell ()),
				                                       CDropSource::kText);
				auto bitmap = createBitmapFromSelection (selection, dataBrowser->getFrame ());
				browser->doDrag (DragDescription (dropSource, CPoint (), bitmap));
			}
		}
		return kMouseEventHandled;
	}
	return kMouseEventNotHandled;
}

} // VSTGUI

#endif // VSTGUI_LIVE_EDITING
