/* greebo: This gets called by the SelectObserver if the user drags a box and holds down
 * any of the selection modifiers. Possible selection candidates are determined and selected/deselected
 */
void RadiantSelectionSystem::SelectArea(const View& view,
                                        const Vector2& device_point,
                                        const Vector2& device_delta,
                                        SelectionSystem::EModifier modifier, bool face)
{
    // If we are in replace mode, deselect all the components or previous selections
    if (modifier == SelectionSystem::eReplace) {
        if (face) {
            setSelectedAllComponents(false);
        }
        else {
            deselectAll();
        }
    }

    {
        // Construct the selection test according to the area the user covered with his drag
        View scissored(view);
        ConstructSelectionTest(scissored, Rectangle::ConstructFromArea(device_point, device_delta));

        SelectionVolume volume(scissored);
        // The posssible candidates go here
        SelectionPool pool;

        SelectablesList candidates;

        if (face)
        {
            ComponentSelector selectionTester(pool, volume, eFace);
            GlobalSceneGraph().foreachVisibleNodeInVolume(scissored, selectionTester);

            // Load them all into the vector
            for (SelectionPool::iterator i = pool.begin(); i != pool.end(); ++i)
            {
                candidates.push_back(i->second);
            }
        }
        else {
            testSelectScene(candidates, volume, scissored, Mode(), ComponentMode());
        }

        // Cycle through the selection pool and toggle the candidates, but only if we are in toggle mode
        for (SelectablesList::iterator i = candidates.begin(); i != candidates.end(); i++) {
            (*i)->setSelected(!(modifier == SelectionSystem::eToggle && (*i)->isSelected()));
        }
    }
}
/* greebo: This is called by the ManipulateObserver class on the mouseDown event. It checks, if a manipulator
 * can be selected where the mouse is pointing to.
 */
bool RadiantSelectionSystem::SelectManipulator(const View& view, const Vector2& device_point, const Vector2& device_epsilon)
{
    if (!nothingSelected() || (ManipulatorMode() == eDrag && Mode() == eComponent))
    {
        // Unselect any currently selected manipulators to be sure
        _manipulator->setSelected(false);

        // Test, if the current manipulator can be selected
        if (!nothingSelected() || (ManipulatorMode() == eDrag && Mode() == eComponent))
        {
            View scissored(view);
            ConstructSelectionTest(scissored, Rectangle::ConstructFromPoint(device_point, device_epsilon));

            // The manipulator class checks on its own, if any of its components can be selected
            _manipulator->testSelect(scissored, GetPivot2World());
        }

        // Save the pivot2world matrix
        startMove();

        // This is true, if a manipulator could be selected
        _pivotMoving = _manipulator->isSelected();

        // is a manipulator selected / the pivot moving?
        if (_pivotMoving) {
            Pivot2World pivot;
            pivot.update(GetPivot2World(), view.GetModelview(), view.GetProjection(), view.GetViewport());

            _manip2pivotStart = _pivot2worldStart.getFullInverse().getMultipliedBy(pivot._worldSpace);

            Matrix4 device2manip;
            ConstructDevice2Manip(device2manip, _pivot2worldStart, view.GetModelview(), view.GetProjection(), view.GetViewport());
            _manipulator->getActiveComponent()->Construct(device2manip, device_point[0], device_point[1]);

            _deviceStart = Vector2(device_point[0], device_point[1]);

            _undoBegun = false;
        }

        SceneChangeNotify();
    }

    return _pivotMoving;
}
/* greebo: This is called by the ManipulateObserver class on the mouseDown event. It checks, if a manipulator
 * can be selected where the mouse is pointing to.
 */
bool RadiantSelectionSystem::SelectManipulator(const View& view, const float device_point[2], const float device_epsilon[2]) {
	if (!nothingSelected() || (ManipulatorMode() == eDrag && Mode() == eComponent)) {
#if defined (DEBUG_SELECTION)
		g_render_clipped.destroy();
#endif

		// Unselect any currently selected manipulators to be sure
		_manipulator->setSelected(false);

		// Test, if the current manipulator can be selected
		if (!nothingSelected() || (ManipulatorMode() == eDrag && Mode() == eComponent)) {
			View scissored(view);
			ConstructSelectionTest(scissored, SelectionBoxForPoint(device_point, device_epsilon));

			// The manipulator class checks on its own, if any of its components can be selected
			_manipulator->testSelect(scissored, GetPivot2World());
		}

		// Save the pivot2world matrix
		startMove();

		// This is true, if a manipulator could be selected
		_pivotMoving = _manipulator->isSelected();

		// is a manipulator selected / the pivot moving?
		if (_pivotMoving) {
			Pivot2World pivot;
			pivot.update(GetPivot2World(), view.GetModelview(), view.GetProjection(), view.GetViewport());

			_manip2pivotStart = matrix4_multiplied_by_matrix4(matrix4_full_inverse(_pivot2worldStart), pivot.m_worldSpace);

			Matrix4 device2manip;
			ConstructDevice2Manip(device2manip, _pivot2worldStart, view.GetModelview(), view.GetProjection(), view.GetViewport());
			_manipulator->GetManipulatable()->Construct(device2manip, device_point[0], device_point[1]);

			_undoBegun = false;
		}

		SceneChangeNotify();
	}

	return _pivotMoving;
}
/* greebo: This gets called by the SelectObserver if the user drags a box and holds down
 * any of the selection modifiers. Possible selection candidates are determined and selected/deselected
 */
void RadiantSelectionSystem::SelectArea(const View& view,
										const float device_point[2],
										const float device_delta[2],
										SelectionSystem::EModifier modifier, bool face)
{
	// If we are in replace mode, deselect all the components or previous selections
	if (modifier == SelectionSystem::eReplace) {
		if (face) {
			setSelectedAllComponents(false);
		}
		else {
			deselectAll();
		}
	}

#if defined (DEBUG_SELECTION)
	g_render_clipped.destroy();
#endif

	{
		// Construct the selection test according to the area the user covered with his drag
		View scissored(view);
		ConstructSelectionTest(scissored, SelectionBoxForArea(device_point, device_delta));

		SelectionVolume volume(scissored);
		// The posssible candidates go here
		SelectionPool pool;
		if (face) {
			Scene_TestSelect_Component(pool, volume, scissored, eFace);
		}
		else {
			Scene_TestSelect(pool, volume, scissored, Mode(), ComponentMode());
		}

		// Cycle through the selection pool and toggle the candidates, but only if we are in toggle mode
		for (SelectionPool::iterator i = pool.begin(); i != pool.end(); ++i) {
			(*i).second->setSelected(!(modifier == SelectionSystem::eToggle && (*i).second->isSelected()));
		}
	}
}
/* greebo: This gets called by SelectObserver if the user just clicks into the scene (without dragging)
 * It checks for any possible targets (in the "line of click") and takes the actions according
 * to the modifiers that are held down (Alt-Shift, etc.)
 */
void RadiantSelectionSystem::SelectPoint(const View& view,
                                         const Vector2& device_point,
                                         const Vector2& device_epsilon,
                                         SelectionSystem::EModifier modifier,
                                         bool face)
{
    ASSERT_MESSAGE(fabs(device_point[0]) <= 1.0f && fabs(device_point[1]) <= 1.0f, "point-selection error");
    // If the user is holding the replace modifiers (default: Alt-Shift), deselect the current selection
    if (modifier == SelectionSystem::eReplace) {
        if (face) {
            setSelectedAllComponents(false);
        }
        else {
            deselectAll();
        }
    }

    {
        View scissored(view);
        // Construct a selection test according to a small box with 2*epsilon edge length
        ConstructSelectionTest(scissored, Rectangle::ConstructFromPoint(device_point, device_epsilon));

        // Create a new SelectionPool instance and fill it with possible candidates
        SelectionVolume volume(scissored);
        // The possible candidates are stored in the SelectablesSet
        SelectablesList candidates;

        if (face)
        {
            SelectionPool selector;

            ComponentSelector selectionTester(selector, volume, eFace);
            GlobalSceneGraph().foreachVisibleNodeInVolume(scissored, selectionTester);

            // Load them all into the vector
            for (SelectionPool::iterator i = selector.begin(); i != selector.end(); ++i)
            {
                candidates.push_back(i->second);
            }
        }
        else {
            testSelectScene(candidates, volume, scissored, Mode(), ComponentMode());
        }

        // Was the selection test successful (have we found anything to select)?
        if (candidates.size() > 0) {
            // Yes, now determine how we should interpret the click
            switch (modifier) {
                // If we are in toggle mode (Shift-Left-Click by default), just toggle the
                // selection of the "topmost" item
                case SelectionSystem::eToggle: {
                    Selectable* best = *candidates.begin();
                    // toggle selection of the object with least depth (=first in the list)
                    best->setSelected(!best->isSelected());
                }
                break;
                // greebo: eReplace mode gets active as soon as the user holds the replace modifiers down
                // and clicks (by default: Alt-Shift). eReplace is only active during the first click
                // afterwards we are in cycle mode.
                // if cycle mode not enabled, enable it
                case SelectionSystem::eReplace: {
                    // select closest (=first in the list)
                    (*candidates.begin())->setSelected(true);
                }
                break;
                // select the next object in the list from the one already selected
                // greebo: eCycle is set if the user keeps holding the replace modifiers (Alt-Shift)
                // and does NOT move the mouse between the clicks, otherwise we fall back into eReplace mode
                // Note: The mode is set in SelectObserver::testSelect()
                case SelectionSystem::eCycle: {
                    // Cycle through the selection pool and activate the item right after the currently selected
                    SelectablesList::iterator i = candidates.begin();

                    while (i != candidates.end()) {
                        if ((*i)->isSelected()) {
                            // unselect the currently selected one
                            (*i)->setSelected(false);
                            // check if there is a "next" item in the list, if not: select the first item
                            ++i;
                            if (i != candidates.end()) {
                                (*i)->setSelected(true);
                            }
                            else {
                                (*candidates.begin())->setSelected(true);
                            }
                            break;
                        }
                        ++i;
                    } // while
                } // case
                break;
                default:
                break;
            } // switch
        }
    }
}
/* greebo: This gets called by SelectObserver if the user just clicks into the scene (without dragging)
 * It checks for any possible targets (in the "line of click") and takes the actions according
 * to the modifiers that are held down (Alt-Shift, etc.)
 */
void RadiantSelectionSystem::SelectPoint(const View& view,
										 const float device_point[2],
										 const float device_epsilon[2],
										 SelectionSystem::EModifier modifier,
										 bool face)
{
	ASSERT_MESSAGE(fabs(device_point[0]) <= 1.0f && fabs(device_point[1]) <= 1.0f, "point-selection error");
	// If the user is holding the replace modifiers (default: Alt-Shift), deselect the current selection
	if (modifier == SelectionSystem::eReplace) {
		if (face) {
			setSelectedAllComponents(false);
		}
		else {
			deselectAll();
		}
	}

#if defined (DEBUG_SELECTION)
	g_render_clipped.destroy();
#endif

	{
		View scissored(view);
		// Construct a selection test according to a small box with 2*epsilon edge length
		ConstructSelectionTest(scissored, SelectionBoxForPoint(device_point, device_epsilon));

		// Create a new SelectionPool instance and fill it with possible candidates
		SelectionVolume volume(scissored);
		// The possible candidates are stored in the SelectionPool
		SelectionPool selector;
		if (face) {
			Scene_TestSelect_Component(selector, volume, scissored, eFace);
		}
		else {
			Scene_TestSelect(selector, volume, scissored, Mode(), ComponentMode());
		}

		// Was the selection test successful (have we found anything to select)?
		if (!selector.failed()) {
			// Yes, now determine how we should interpret the click
			switch (modifier) {
				// If we are in toggle mode (Shift-Left-Click by default), just toggle the
				// selection of the "topmost" item
				case SelectionSystem::eToggle: {
					SelectableSortedSet::iterator best = selector.begin();
					// toggle selection of the object with least depth (=first in the list)
					if ((*best).second->isSelected())
						(*best).second->setSelected(false);
					else
						(*best).second->setSelected(true);
				}
				break;
				// greebo: eReplace mode gets active as soon as the user holds the replace modifiers down
				// and clicks (by default: Alt-Shift). eReplace is only active during the first click
				// afterwards we are in cycle mode.
				// if cycle mode not enabled, enable it
				case SelectionSystem::eReplace: {
					// select closest (=first in the list)
					(*selector.begin()).second->setSelected(true);
				}
				break;
				// select the next object in the list from the one already selected
				// greebo: eCycle is set if the user keeps holding the replace modifiers (Alt-Shift)
				// and does NOT move the mouse between the clicks, otherwise we fall back into eReplace mode
				// Note: The mode is set in SelectObserver::testSelect()
				case SelectionSystem::eCycle: {
					// Cycle through the selection pool and activate the item right after the currently selected
					SelectionPool::iterator i = selector.begin();
					while (i != selector.end()) {
						if ((*i).second->isSelected()) {
							// unselect the currently selected one
							(*i).second->setSelected(false);
							// check if there is a "next" item in the list, if not: select the first item
							++i;
							if (i != selector.end()) {
								i->second->setSelected(true);
							}
							else {
								selector.begin()->second->setSelected(true);
							}
							break;
						}
						++i;
					} // while
				} // case
				break;
				default:
				break;
			} // switch
		}
	}
}
// Handles the mouseDown event, basically determines which action should be performed (select or manipulate)
void RadiantWindowObserver::onMouseDown(const WindowVector& position, GdkEventButton* ev)
{
	// Retrieve the according ObserverEvent for the GdkEventButton
	ui::ObserverEvent observerEvent = GlobalEventManager().MouseEvents().getObserverEvent(ev);

	// Check if the user wants to copy/paste a texture
	if (observerEvent == ui::obsCopyTexture || observerEvent == ui::obsPasteTextureProjected ||
		observerEvent == ui::obsPasteTextureNatural || observerEvent == ui::obsPasteTextureCoordinates ||
		observerEvent == ui::obsPasteTextureToBrush || observerEvent == ui::obsJumpToObject)
	{
		// Get the mouse position
		DeviceVector devicePosition(device_constrained(window_to_normalised_device(position, _width, _height)));

		// Check the target object
		render::View scissored(*_selectObserver._view);
		ConstructSelectionTest(scissored, Rectangle::ConstructFromPoint(devicePosition, _selectObserver._epsilon));
		SelectionVolume volume(scissored);

		// Do we have a camera view (fill() == true)?
		if (_selectObserver._view->fill())
		{
			if (observerEvent == ui::obsJumpToObject) {
				CamWndPtr cam = GlobalCamera().getActiveCamWnd();
				if (cam != NULL) {
					cam->jumpToObject(volume);
				}
			}
			// If the apply texture modifier is held
			else if (observerEvent == ui::obsPasteTextureProjected) {
				// Paste the shader projected (TRUE), but not to an entire brush (FALSE)
				selection::algorithm::pasteShader(volume, true, false);
			}
			// If the copy texture modifier is held
			else if (observerEvent == ui::obsCopyTexture) {
				// Set the source texturable from the given test
				GlobalShaderClipboard().setSource(volume);
			}
			else if (observerEvent == ui::obsPasteTextureNatural) {
				// Paste the shader naturally (FALSE), but not to an entire brush (FALSE)
				selection::algorithm::pasteShader(volume, false, false);
			}
			else if (observerEvent == ui::obsPasteTextureCoordinates) {
				// Clone the texture coordinates from the patch in the clipboard
				selection::algorithm::pasteTextureCoords(volume);
			}
			else if (observerEvent == ui::obsPasteTextureToBrush) {
				// Paste the shader projected (TRUE), and to the entire brush (TRUE)
				selection::algorithm::pasteShader(volume, true, true);
			}
		}
	}

	// Have any of the "selection" events occurred?
	// greebo: This could be an "else if (observerEvent != obsNothing)" as well,
	// but perhaps there will be more events in the future that aren't selection events.
	if (observerEvent == ui::obsManipulate || observerEvent == ui::obsSelect ||
		observerEvent == ui::obsToggle || observerEvent == ui::obsToggleFace ||
		observerEvent == ui::obsToggleGroupPart ||
		observerEvent == ui::obsReplace || observerEvent == ui::obsReplaceFace)
	{
		_mouseDown = true;

		// Determine the current mouse position
		DeviceVector devicePosition(window_to_normalised_device(position, _width, _height));

		if (observerEvent ==  ui::obsManipulate && _manipulateObserver.mouseDown(devicePosition)) {
			// This is a manipulation operation, register the callbacks
			// Note: the mouseDown call in the if clause returned already true,
			// so a manipulator could be successfully selected
			_mouseMotionCallback = boost::bind(&ManipulateObserver::mouseMoved, &_manipulateObserver, _1);
			_mouseUpCallback = boost::bind(&ManipulateObserver::mouseUp, &_manipulateObserver, _1);

			_listenForCancelEvents = true;
		}
		else {
			// Call the mouseDown method of the selector class, this covers all of the other events
			_selectObserver.mouseDown(devicePosition);

			_mouseMotionCallback = boost::bind(&SelectObserver::mouseMoved, &_selectObserver, _1);
			_mouseUpCallback = boost::bind(&SelectObserver::mouseUp, &_selectObserver, _1);

			// greebo: the according actions (toggle face, replace, etc.) are handled in the mouseUp methods.
		}
	}
}