 /*
  *  copyright (C) 2004
  *  Umbrello UML Modeller Authors <uml-devel@ uml.sf.net>
  */

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#include <qwmatrix.h> // need for inverseWorldMatrix.map

#include "toolbarstate.h"
#include "umlview.h"
#include "umlwidget.h"
#include "associationwidget.h"
#include "uml.h"

ToolBarState::ToolBarState(UMLView *umlView) : m_pUMLView(umlView)
{
	m_pMouseEvent = NULL;
	m_bWidgetSelected = false;
	init();
}

ToolBarState::~ToolBarState()
{
	if (m_pMouseEvent) delete m_pMouseEvent;
}

void ToolBarState::init()
{
	m_pUMLView->viewport()->setMouseTracking( false );
	m_bIsButtonPressed = false;
}

void ToolBarState::setMouseEvent (QMouseEvent* ome, const QEvent::Type &type)
{
	if (m_pMouseEvent) delete m_pMouseEvent;

	m_pMouseEvent = new QMouseEvent(type, m_pUMLView->inverseWorldMatrix().map(ome->pos()),
					  ome->button(),ome->state());
}

void ToolBarState::mousePress(QMouseEvent* ome)
{
	setMouseEvent (ome, QEvent::MouseButtonPress);
	m_bIsButtonPressed = true;

	m_pUMLView->viewport()->setMouseTracking(true);

	// TODO: emit sucks
	m_pUMLView->emitRemovePopupMenu();

	// TODO: Check who needs this.
	m_pUMLView->setPos( m_pMouseEvent->pos() );

	m_pUMLView->setPaste(false);

	setSelectedWidget(m_pMouseEvent);
}

void ToolBarState::mouseRelease(QMouseEvent* ome)
{
	setMouseEvent (ome, QEvent::MouseButtonRelease);
	m_bIsButtonPressed = false;

	// Set the position of the mouse
	// TODO, should only be available in this state?
	m_pUMLView->getPos().setX(m_pMouseEvent->x());
	m_pUMLView->getPos().setY(m_pMouseEvent->y());

	// TODO: Should not be called by an Sequence message Association.
	UMLWidget *onW = m_pUMLView->getOnWidget();
	if (onW)
		onW->mouseReleaseEvent(m_pMouseEvent);
	AssociationWidget *moveAssoc = m_pUMLView->getMoveAssoc(); 
	if (moveAssoc)
		moveAssoc->mouseReleaseEvent(m_pMouseEvent);

	// Default, rightbutton changes the tool.
	// The arrow tool overrides the changeTool() function.
	changeTool();
}

void ToolBarState::changeTool()
{
	if (m_pMouseEvent->state() == QMouseEvent::RightButton)
	{
		/* if the user right clicks on the diagram, first the default tool is
		 * selected from the toolbar; this only happens when the default tool
		 * wasn't selected yet AND there is no other widget under the mouse
		 * pointer
		 * in any other case the right click menu will be shown
		 * */
		UMLApp::app()->getWorkToolBar()->setDefaultTool();
	}

	if (m_pMouseEvent->state() != QMouseEvent::LeftButton)
	{
		m_pUMLView->viewport()->setMouseTracking( false );
	}
}


void ToolBarState::mouseDoubleClick(QMouseEvent* ome)
{
	setMouseEvent(ome, QEvent::MouseButtonDblClick);


	UMLWidget *onW = m_pUMLView->getOnWidget();
	AssociationWidget *moveAssoc = m_pUMLView->getMoveAssoc(); 
	if (onW && onW->onWidget( m_pMouseEvent->pos()))
	{
		onW->mouseDoubleClickEvent( m_pMouseEvent );
	}
	else if (moveAssoc && moveAssoc->onAssociation( m_pMouseEvent->pos() ))
	{
		moveAssoc->mouseDoubleClickEvent( m_pMouseEvent );
	}
	else
	{
		m_pUMLView->setOnWidget(NULL);
		m_pUMLView->setMoveAssoc(NULL);
		m_pUMLView->clearSelected();
	}
}

void ToolBarState::mouseMove(QMouseEvent* ome)
{
	setMouseEvent(ome, QEvent::MouseMove);

	UMLWidget *onW = m_pUMLView->getOnWidget();
	AssociationWidget *moveAssoc = m_pUMLView->getMoveAssoc(); 
	if (onW)
		onW->mouseMoveEvent( m_pMouseEvent );
	else if (moveAssoc)
		moveAssoc->mouseMoveEvent( m_pMouseEvent );

	if (m_bIsButtonPressed)
	{
		int vx = ome->x();
		int vy = ome->y();
		int contsX = m_pUMLView->contentsX();
		int contsY = m_pUMLView->contentsY();
		int visw = m_pUMLView->visibleWidth();
		int vish = m_pUMLView->visibleHeight();
		int dtr = visw - (vx-contsX);
		int dtb = vish - (vy-contsY);
		int dtt =  (vy-contsY);
		int dtl =  (vx-contsX);
		if (dtr < 30) m_pUMLView->scrollBy(30-dtr,0);
		if (dtb < 30) m_pUMLView->scrollBy(0,30-dtb);
		if (dtl < 30) m_pUMLView->scrollBy(-(30-dtl),0);
		if (dtt < 30) m_pUMLView->scrollBy(0,-(30-dtt));
	}

}

// TODO: Remove parameter?
bool ToolBarState::setSelectedWidget(QMouseEvent * me)
{
	m_pUMLView->setMoveAssoc(NULL);
	m_pUMLView->setOnWidget(NULL);

	UMLWidget* backup = 0;
	UMLWidget* boxBackup = 0;

	// Check widgets.
	UMLWidgetListIt it( m_pUMLView->getWidgetList() );
	UMLWidget* obj = 0;
	while ( (obj = it.current()) != 0 ) {
		++it;
		if( !obj->isVisible() || !obj->onWidget(me->pos()) )
			continue;
		//Give text object priority,
		//they can easily get into a position where
		//you can't touch them.
		//Give Boxes lowest priority, we want to be able to move things that
		//are on top of them.
		if (obj->getBaseType() == Uml::wt_Text)
		{
			m_pUMLView->setOnWidget( obj );
			obj ->  mousePressEvent( me );
			m_bWidgetSelected = true;
			return true;
		} else if (obj->getBaseType() == Uml::wt_Box) {
			boxBackup = obj;
		} else {
			backup = obj;
		}
	}//end while
	//if backup is set then let it have the event
	if(backup) {
		backup -> mousePressEvent( me );
		m_pUMLView->setOnWidget( backup );

		m_bWidgetSelected = true;
		return true;
	}

	// Check messages.
	MessageWidgetListIt mit( m_pUMLView->getMessageList() );
	obj = 0;
	while ((obj = (UMLWidget*)mit.current()) != 0) {
		if (obj->isVisible() && obj->onWidget(me->pos())) {
			m_pUMLView->setOnWidget( obj );
			obj ->  mousePressEvent( me );
			m_bWidgetSelected = true;
			return true;
		}
		++mit;
	}

	// Boxes have lower priority.
	if (boxBackup) {
		boxBackup -> mousePressEvent( me );
		m_pUMLView->setOnWidget( boxBackup );

		m_bWidgetSelected = true;
		return true;
	}

	// Check associations.
	AssociationWidgetListIt assoc_it(m_pUMLView->getAssociationList());
	AssociationWidget* assocwidget = 0;
	while((assocwidget=assoc_it.current())) {
		if( assocwidget -> onAssociation( me -> pos() ))
		{
			// TODO: Fix this. It makes a callback to the association mousePressEvent function.
			assocwidget->mousePressEvent(me);
			m_pUMLView->setMoveAssoc(assocwidget);

			m_bWidgetSelected = true;
			return true;
		}
		++assoc_it;
	}
	m_pUMLView->setMoveAssoc(NULL);
	m_pUMLView->setOnWidget(NULL);

	m_bWidgetSelected = false;
	return false;
}
