/* This file is part of the KDE project
   Copyright (C) 2002, The Karbon Developers

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include <math.h>

#include <qcursor.h>
#include <qlabel.h>

#include <klocale.h>
#include <KoPoint.h>
#include <KoRect.h>

#include <karbon_part.h>
#include <karbon_view.h>

#include <render/vpainter.h>
#include <render/vpainterfactory.h>
#include <visitors/vselectnodes.h>
#include <commands/vtransformcmd.h>
#include <visitors/vdrawselection.h>
#include <core/vselection.h>
#include <core/vcursor.h>
#include "vselectnodestool.h"
#include <vtransformnodes.h>
#include <commands/vdeletenodescmd.h>
#include <widgets/vcanvas.h>

#include <kdebug.h>

VSelectNodesTool::VSelectNodesTool( KarbonView* view )
	: VTool( view, "tool_select_nodes" ), m_state( normal ), m_select( true )
{
	registerTool( this );
}

VSelectNodesTool::~VSelectNodesTool()
{
}

void
VSelectNodesTool::activate()
{
	if( view() )
	{
		view()->setCursor( VCursor::needleArrow() );
		view()->part()->document().selection()->showHandle( false );
		view()->part()->document().selection()->setSelectObjects( false );
		// deselect all nodes
		view()->part()->document().selection()->selectNodes( false );
		view()->repaintAll( view()->part()->document().selection()->boundingBox() );
	}
	VTool::activate();
}

QString
VSelectNodesTool::statusText()
{
	if( m_state == normal )
		return i18n( "Editing Nodes" );
	else
		return QString( "" );
}

void
VSelectNodesTool::draw()
{
	VPainter *painter = view()->painterFactory()->editpainter();
	painter->setZoomFactor( view()->zoom() );
	painter->setRasterOp( Qt::NotROP );

	if( m_state == dragging )
	{
		painter->setPen( Qt::DotLine );
		painter->newPath();
		painter->moveTo( KoPoint( m_first.x(), m_first.y() ) );
		painter->lineTo( KoPoint( m_current.x(), m_first.y() ) );
		painter->lineTo( KoPoint( m_current.x(), m_current.y() ) );
		painter->lineTo( KoPoint( m_first.x(), m_current.y() ) );
		painter->lineTo( KoPoint( m_first.x(), m_first.y() ) );
		painter->strokePath();
	}
	else
	{
		VDrawSelection op( m_objects, painter, true, VSelection::handleSize() );
		VObjectListIterator itr = m_objects;
        for( ; itr.current(); ++itr )
			op.visit( *( itr.current() ) );
	}
}

void
VSelectNodesTool::setCursor() const
{
	if( m_state >= moving ) 
	{
		view()->setCursor( VCursor::needleMoveArrow() );
		return;
	}

	KoRect selrect = calcSelRect( last() );

	QPtrList<VSegment> segments = view()->part()->document().selection()->getSegments( selrect );
	if( segments.count() > 0  )
	{
		VSegment* seg = segments.at( 0 );
		for( int i = 0; i < seg->degree(); ++i )
			if( seg->pointIsSelected( i ) && selrect.contains( seg->point( i ) ) )
			{
				view()->setCursor( VCursor::needleMoveArrow() );
				break;
			}
	}
	else
		view()->setCursor( VCursor::needleArrow() );
}

void
VSelectNodesTool::mouseButtonPress()
{
	m_first = m_current = first();

	m_state = normal;
	m_select = true;

	recalc();

	view()->part()->document().selection()->setState( VObject::edit );
	view()->repaintAll( view()->part()->document().selection()->boundingBox() );
	view()->part()->document().selection()->setState( VObject::selected );

	VSelection* selection = view()->part()->document().selection();

	KoRect selrect = calcSelRect( m_current );

	// get segments with control points inside selection rect
	QPtrList<VSegment> segments = selection->getSegments( selrect );
	if( segments.count() > 0 )
	{
		VSegment *seg = segments.at( 0 );
		VSegment* prev = seg->prev();
		VSegment* next = seg->next();

		// allow moving bezier points only if one of the bezier points is within the selection rect
		// and no neighboring knot is selected
		if( segments.count() == 1 && ! selrect.contains( seg->knot() ) && ! seg->knotIsSelected() 
		&& ( prev && ! prev->knotIsSelected() ) )
		{
			if( selrect.contains( seg->point( 1 ) ) )
			{
				m_state = movingbezier1;
				if( next )
					next->selectPoint( 0, false );
			}
			else if( selrect.contains( seg->point( 0 ) ) )
			{
				m_state = movingbezier2;
				if( prev ) 
					prev->selectPoint( 1, false );
			}
		}
		else
		{
			for( VSegment *seg = segments.first(); seg; seg = segments.next() )
			{
				for( int i = 0; i < seg->degree(); ++i )
				{
					if( seg->pointIsSelected( i ) && selrect.contains( seg->point( i ) ) )
					{
						m_state = moving;
						break;
					}
				}
				if( m_state == moving )
					break;
			}
		}

		double minDist = -1.0;
		// use the nearest control point of all the segments as starting point
		for( VSegment *seg = segments.first(); seg; seg = segments.next() )
		{
			for( int i = 0; i < seg->degree(); ++i )
			{
				if( selrect.contains( seg->point( i ) ) )
				{
					KoPoint vDist = seg->point( i ) - m_current;
					double dist = vDist.x()*vDist.x() + vDist.y()*vDist.y();
					if( minDist < 0.0 || dist < minDist )
					{
						m_first = seg->point( i );
						minDist = dist;
					}
				}
			}
		}
		recalc();
	}
	else
		m_state = dragging;

	draw();
}

void 
VSelectNodesTool::rightMouseButtonPress()
{
	m_first = m_current = first();

	m_state = normal;
	m_select = false;

	recalc();

	view()->part()->document().selection()->setState( VObject::edit );
	view()->repaintAll( view()->part()->document().selection()->boundingBox() );
	view()->part()->document().selection()->setState( VObject::selected );

	draw();
}

bool
VSelectNodesTool::keyReleased( Qt::Key key )
{
	VSelection* selection = view()->part()->document().selection();

	switch( key )
	{
		// increase/decrease the handle size
		case Qt::Key_I:
		{
			uint handleSize = selection->handleSize();
			if( shiftPressed() ) 
				selection->setHandleSize( ++handleSize );
			else if( handleSize > 1 )
				selection->setHandleSize( --handleSize );
		}
		break;
		case Qt::Key_Delete:
			if( selection->objects().count() > 0 )
				view()->part()->addCommand( new VDeleteNodeCmd( &view()->part()->document() ), true );
		break;
		default: return false;
	}

	if( view() )
		view()->repaintAll( selection->boundingBox() );

	return true;
}

void
VSelectNodesTool::mouseButtonRelease()
{
	// erase old object:
	draw();

	VSelection* selection = view()->part()->document().selection();

	KoRect selrect = calcSelRect( last() );

	if( ctrlPressed() )
		selection->append( selrect.normalize(), false, false );
	else
		selection->append( selrect.normalize(), false, true );

	view()->selectionChanged();
	view()->part()->repaintAllViews();
	m_state = normal;
}

void 
VSelectNodesTool::rightMouseButtonRelease()
{
	// erase old object:
	draw();

	VSelection* selection = view()->part()->document().selection();

	KoRect selrect = calcSelRect( last() );

	selection->take( selrect.normalize(), false, false );

	view()->selectionChanged();
	view()->part()->repaintAllViews();
	m_state = normal;
}

void
VSelectNodesTool::mouseDrag()
{
	draw();

	recalc();

	draw();
}

void
VSelectNodesTool::mouseDragRelease()
{
	if( m_state >= moving )
	{
		view()->part()->document().selection()->setState( VObject::selected );
		VCommand *cmd;
		QPtrList<VSegment> segments;
		KoPoint _last = view()->canvasWidget()->snapToGrid( last() );
		if( m_state == movingbezier1 || m_state == movingbezier2 )
		{
			KoRect selrect = calcSelRect( m_first );
			segments = view()->part()->document().selection()->getSegments( selrect );
			cmd = new VTranslateBezierCmd( &view()->part()->document(), segments.at( 0 ),
					qRound( ( _last.x() - m_first.x() ) ),
					qRound( ( _last.y() - m_first.y() ) ),
					m_state == movingbezier2 );
		}
		else
		{
			cmd = new VTranslatePointCmd(
					&view()->part()->document(),
					qRound( ( _last.x() - m_first.x() ) ),
					qRound( ( _last.y() - m_first.y() ) ) );
		}
		view()->part()->addCommand( cmd, true );
		m_state = normal;
	}
	else
	{
		KoPoint fp = m_first;
		KoPoint lp = last();

		if ( (fabs(lp.x() - fp.x()) + fabs(lp.y()-fp.y())) < 3.0 )
		{
			// AK - should take the middle point here
			fp = last() - KoPoint(8.0, 8.0);
			lp = last() + KoPoint(8.0, 8.0);
		}

		// erase old object:
		draw();

		if( m_select )
		{
			view()->part()->document().selection()->append();	// select all
			view()->part()->document().selection()->append(
				KoRect( fp.x(), fp.y(), lp.x() - fp.x(), lp.y() - fp.y() ).normalize(),
				false, true );
		}
		else
		{
			view()->part()->document().selection()->take(
				KoRect( fp.x(), fp.y(), lp.x() - fp.x(), lp.y() - fp.y() ).normalize(),
				false, false );
		}
		view()->selectionChanged();
		view()->part()->repaintAllViews();
		m_state = normal;
	}
}

void
VSelectNodesTool::cancel()
{
	// Erase old object:
	if ( isDragging() )
	{
		draw();
		m_state = normal;
		view()->repaintAll( view()->part()->document().selection()->boundingBox() );
	}
}

void
VSelectNodesTool::recalc()
{
	if( m_state == dragging )
	{
		m_current = last();
	}
	else if( m_state == moving || m_state == movingbezier1 || m_state == movingbezier2 )
	{
		KoPoint _last = view()->canvasWidget()->snapToGrid( last() );
		double distx = _last.x() - m_first.x();
		double disty = _last.y() - m_first.y();
		// move operation
		QWMatrix mat;
		mat.translate( distx, disty );

		// Copy selected objects and transform:
		m_objects.clear();
		VObject* copy;

		VTransformNodes op( mat );

		VObjectListIterator itr = view()->part()->document().selection()->objects();
		for ( ; itr.current() ; ++itr )
		{
			if( itr.current()->state() != VObject::deleted )
			{
				copy = itr.current()->clone();
				copy->setState( VObject::edit );
				op.visit( *copy );
				m_objects.append( copy );
			}
		}
	}
}

void
VSelectNodesTool::setup( KActionCollection *collection )
{
	m_action = static_cast<KRadioAction *>(collection -> action( name() ) );

	if( m_action == 0 )
	{
		m_action = new KRadioAction( i18n( "Select Nodes Tool" ), "14_selectnodes", Qt::SHIFT+Qt::Key_H, this, SLOT( activate() ), collection, name() );
		m_action->setToolTip( i18n( "Select Nodes" ) );
		m_action->setExclusiveGroup( "select" );
		//m_ownAction = true;
	}
}

KoRect 
VSelectNodesTool::calcSelRect( const KoPoint &pos ) const
{
	double tolerance = view()->part()->document().selection()->handleSize() / view()->zoom();
	return KoRect( pos.x() - tolerance, pos.y() - tolerance, 2 * tolerance + 1.0, 2 * tolerance + 1.0 );
}
