/* This file is part of the KDE project
   Copyright (C) 2001, 2002, 2003 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 <tqcursor.h>
#include <tqevent.h>
#include <tqlabel.h>

#include <klocale.h>

#include <karbon_part.h>
#include <karbon_view.h>
#include <core/vcolor.h>
#include <core/vcomposite.h>
#include <core/vfill.h>
#include <core/vstroke.h>
#include <core/vglobal.h>
#include <core/vcursor.h>
#include <render/vpainter.h>
#include <render/vpainterfactory.h>
#include "vpolylinetool.h"
#include <commands/vshapecmd.h>
#include <commands/vcommand.h>
#include <widgets/vcanvas.h>

VPolylineTool::VPolylineTool( KarbonView *view )
	: VTool( view, "tool_polyline" )
{
	m_bezierPoints.setAutoDelete( true );
	registerTool( this );
	m_crossCursor = new TQCursor( VCursor::createCursor( VCursor::CrossHair ) );
}

VPolylineTool::~VPolylineTool()
{
	delete m_crossCursor;
}

TQString
VPolylineTool::contextHelp()
{
	TQString s = i18n( "<qt><b>Polyline tool:</b><br>" );
	s += i18n( "- <i>Click</i> to add a node and <i>drag</i> to set its bezier vector.<br>" );
	s += i18n( "- Press <i>Ctrl</i> while dragging to edit the previous bezier vector.<br>" );
	s += i18n( "- Press <i>Shift</i> while dragging to change the curve in a straight line.<br>" );
	s += i18n( "- Press <i>Backspace</i> to cancel the last curve.<br>" );
	s += i18n( "- Press <i>Esc</i> to cancel the whole polyline.<br>" );
	s += i18n( "- Press <i>Enter</i> or <i>double click</i> to end the polyline.</qt>" );

	return s;
}

void
VPolylineTool::activate()
{
	VTool::activate();
	view()->statusMessage()->setText( i18n( "Polyline Tool" ) );
	view()->setCursor( *m_crossCursor );

	m_bezierPoints.clear();
	m_close = false;

	connect( view()->part()->commandHistory(), TQT_SIGNAL(commandExecuted()), this, TQT_SLOT(commandExecuted()) );
}

void
VPolylineTool::initializePath( VPath &path )
{
	KoPoint* p1 = m_bezierPoints.first();
	KoPoint* p2;
	KoPoint* p3;
	KoPoint* p4;

	path.moveTo( *p1 );

	while(
		( p2 = m_bezierPoints.next() ) &&
		( p3 = m_bezierPoints.next() ) &&
		( p4 = m_bezierPoints.next() ) )
	{
		if ( *p1 == *p2 )
			if ( *p3 == *p4 )
				path.lineTo( *p4 );
			else
				//polyline->curve1To( *p3, *p4 );
				path.curveTo( *p3, *p4, *p4 );
		else
			if ( *p3 == *p4 )
				//polyline->curve2To( *p2, *p4 );
				path.curveTo( *p2, *p2, *p4 );
			else
				path.curveTo( *p2, *p3, *p4 );
		p1 = p4;
	}
}

void
VPolylineTool::createObject()
{
	VPath* polyline = 0L;
	if( m_bezierPoints.count() > 2 )
	{
		polyline = new VPath( 0L );
		if( polyline )
		{
			initializePath( *polyline );
			if( m_close )
				polyline->close();

			VShapeCmd* cmd = new VShapeCmd(
				&view()->part()->document(),
				i18n( "Polyline" ),
				polyline,
				"14_polyline" );
	
			view()->part()->addCommand( cmd, true );
		}
	}

	m_bezierPoints.clear();
	m_close = false;
}

void
VPolylineTool::deactivate()
{
	m_bezierPoints.removeLast();
	m_bezierPoints.removeLast();
	
	createObject();

	disconnect( view()->part()->commandHistory(), TQT_SIGNAL(commandExecuted()), this, TQT_SLOT(commandExecuted()) );
}

void
VPolylineTool::draw()
{
	VPainter* painter = view()->painterFactory()->editpainter();
	painter->setRasterOp( TQt::NotROP );

	if( m_bezierPoints.count() > 2 )
	{
		VPath polyline( 0L );
		initializePath( polyline );

		polyline.setState( VObject::edit );
		polyline.draw( painter, &polyline.boundingBox() );
	}
}

void
VPolylineTool::drawBezierVector( KoPoint& start, KoPoint& end )
{
	VPainter* painter = view()->painterFactory()->editpainter();

	painter->save();

	float zoomFactor = view()->zoom();

	painter->setRasterOp( TQt::NotROP );
	painter->newPath();
/*  VStroke stroke( TQt::blue, 0L, 1.0 );
	TQValueList<float> array;
	array << 2.0 << 3.0;
	stroke.dashPattern().setArray( array );*/
	painter->setPen( TQt::DotLine /*stroke*/ );
	painter->setBrush( TQt::NoBrush );

	painter->moveTo( start ); 
	painter->lineTo( end );
	painter->strokePath();
	painter->setRasterOp( TQt::XorROP );
	painter->newPath();
	painter->setPen( TQt::yellow );

	float width = 2.0;

	painter->moveTo( KoPoint(
		end.x() - width / zoomFactor,
		end.y() - width / zoomFactor ) );
	painter->lineTo( KoPoint(
		end.x() + width / zoomFactor,
		end.y() - width / zoomFactor ) );
	painter->lineTo( KoPoint(
		end.x() + width / zoomFactor,
		end.y() + width / zoomFactor ) );
	painter->lineTo( KoPoint(
		end.x() - width / zoomFactor,
		end.y() + width / zoomFactor ) );
	painter->lineTo( KoPoint(
		end.x() - width / zoomFactor,
		end.y() - width / zoomFactor ) );

	painter->strokePath();
	painter->restore();
}

void
VPolylineTool::mouseMove()
{
	if( m_bezierPoints.count() != 0 )
	{
		KoPoint _last = view()->canvasWidget()->snapToGrid( last() );
		draw();

		m_bezierPoints.removeLast();
		m_bezierPoints.removeLast();
		m_bezierPoints.append( new KoPoint( _last ) );
		m_bezierPoints.append( new KoPoint( _last ) );

		draw();
	}
}

void
VPolylineTool::mouseButtonPress()
{
	KoPoint _last = view()->canvasWidget()->snapToGrid( last() );
	if( m_bezierPoints.count() != 0 )
	{
		draw();
		m_bezierPoints.removeLast();
		m_bezierPoints.removeLast();
		m_bezierPoints.append( new KoPoint( _last ) );
	}

	m_lastVectorEnd = m_lastVectorStart = _last;

	m_bezierPoints.append( new KoPoint( _last ) );
	m_bezierPoints.append( new KoPoint( _last ) );
	drawBezierVector( m_lastVectorStart, m_lastVectorEnd );
	draw();
}

void
VPolylineTool::mouseButtonRelease()
{
	KoPoint _last = view()->canvasWidget()->snapToGrid( last() );
	if( m_bezierPoints.count() == 2 )
	{
		drawBezierVector( m_lastVectorStart, m_lastVectorEnd );

		m_bezierPoints.removeLast();
		m_bezierPoints.append( new KoPoint( _last ) );

		VPainter* painter = view()->painterFactory()->editpainter();
		painter->save();
		painter->setZoomFactor( view()->zoom() );
		painter->setRasterOp( TQt::XorROP );
		VStroke stroke( TQt::yellow, 0L, 1.0 );
		painter->setPen( stroke );
		painter->setBrush( TQt::yellow );
		painter->newPath();
		painter->drawNode( m_lastVectorStart, 2 );
		painter->strokePath();
		painter->restore();
	}
	else
	{
		drawBezierVector( m_lastVectorStart, m_lastVectorEnd );
		draw();
		m_bezierPoints.removeLast();
		KoPoint* p = new KoPoint( *m_bezierPoints.last() );
		m_bezierPoints.removeLast();
		KoPoint* b = new KoPoint( *m_bezierPoints.last() );
		m_bezierPoints.removeLast();

		if( shiftPressed() )
		{
			m_bezierPoints.removeLast();
			m_bezierPoints.append( new KoPoint( *m_bezierPoints.last() ) );
			m_bezierPoints.append( new KoPoint( *p ) );
			m_bezierPoints.append( new KoPoint( *p ) );
			m_bezierPoints.append( new KoPoint( *p ) );
			m_lastVectorStart = m_lastVectorEnd = *p;
		}
		else if( ctrlPressed() )
		{
			m_bezierPoints.removeLast();
			m_lastVectorStart = *m_bezierPoints.last();
			m_bezierPoints.append( new KoPoint( _last ) );
			m_bezierPoints.append( new KoPoint( *b ) );
			m_bezierPoints.append( new KoPoint( *p ) );
			m_bezierPoints.append( new KoPoint( *p - ( *b - *p ) ) );
			m_lastVectorEnd = _last;
		}
		else
		{
			m_bezierPoints.append( new KoPoint( _last ) );
			m_bezierPoints.append( new KoPoint( *p ) );
			m_bezierPoints.append( new KoPoint( *p - ( _last - *p ) ) );
			m_lastVectorStart = *p;
			m_lastVectorEnd = _last;
		}
		if( m_bezierPoints.count() > 2 && p->isNear( *m_bezierPoints.first(), 3 ) )
		{
			m_bezierPoints.append( new KoPoint( _last ) );
			m_close = true;
			createObject();
			return;
		}
	}

	m_bezierPoints.append( new KoPoint( _last ) );
	m_bezierPoints.append( new KoPoint( _last ) );

	draw();
}

void
VPolylineTool::rightMouseButtonRelease()
{
	// end line without adding new points
	m_bezierPoints.removeLast();
	m_bezierPoints.removeLast();

	createObject();
}

void
VPolylineTool::mouseButtonDblClick()
{
	createObject();
}

void
VPolylineTool::mouseDrag()
{
	KoPoint _last = view()->canvasWidget()->snapToGrid( last() );

	if( m_bezierPoints.count() == 2 )
	{
		drawBezierVector( m_lastVectorStart, m_lastVectorEnd );

		m_bezierPoints.removeLast();
		m_bezierPoints.append( new KoPoint( _last ) );
		m_lastVectorEnd = _last;

		drawBezierVector( m_lastVectorStart, m_lastVectorEnd );
	}
	else
	{
		drawBezierVector( m_lastVectorStart, m_lastVectorEnd );
		draw();

		m_bezierPoints.removeLast();
		KoPoint* p = new KoPoint( *m_bezierPoints.last() );
		m_bezierPoints.removeLast();
		KoPoint* b = new KoPoint( *m_bezierPoints.last() );
		m_bezierPoints.removeLast();

		if( shiftPressed() )
		{
			m_bezierPoints.removeLast();
			m_bezierPoints.append( new KoPoint( *m_bezierPoints.last() ) );
			m_bezierPoints.append( new KoPoint( *p ) );
			m_bezierPoints.append( new KoPoint( *p ) );
			m_bezierPoints.append( new KoPoint( *p ) );
			m_lastVectorStart = m_lastVectorEnd = *p;
		}
		else if( ctrlPressed() )
		{
			m_bezierPoints.removeLast();
			m_lastVectorStart = *m_bezierPoints.last();
			m_bezierPoints.append( new KoPoint( _last ) );
			m_bezierPoints.append( new KoPoint( *b ) );
			m_bezierPoints.append( new KoPoint( *p ) );
			m_bezierPoints.append( new KoPoint( *p - ( *b - *p ) ) );
			m_lastVectorEnd = _last;
		}
		else
		{
			m_bezierPoints.append( new KoPoint( _last ) );
			m_bezierPoints.append( new KoPoint( *p ) );
			m_bezierPoints.append( new KoPoint( *p - ( _last - *p ) ) );
			m_lastVectorStart = *p;
			m_lastVectorEnd = _last;
		}

		draw();
		drawBezierVector( m_lastVectorStart, m_lastVectorEnd );
	}
}

void
VPolylineTool::mouseDragRelease()
{
	mouseButtonRelease();
}

void
VPolylineTool::mouseDragShiftPressed()
{
}

void
VPolylineTool::mouseDragCtrlPressed()
{
	// Moves the mouse to the other bezier vector position.
	if( m_bezierPoints.count() > 3 )
	{
		KoPoint p;
		p = *m_bezierPoints.at( m_bezierPoints.count() - 4) - *m_bezierPoints.at( m_bezierPoints.count() - 3 );

		view()->setPos( p );
	}
}

void
VPolylineTool::mouseDragShiftReleased()
{
}

void
VPolylineTool::mouseDragCtrlReleased()
{
	if( m_bezierPoints.count() > 3 )
	{
		KoPoint p;
		p = *m_bezierPoints.at( m_bezierPoints.count() - 3) - *m_bezierPoints.at( m_bezierPoints.count() - 4 );

		view()->setPos( p );
	}
}

void
VPolylineTool::cancel()
{
	draw();

	m_bezierPoints.clear();
}

void
VPolylineTool::cancelStep()
{
	draw();

	if ( m_bezierPoints.count() > 6 )
	{
		m_bezierPoints.removeLast();
		m_bezierPoints.removeLast();
		m_bezierPoints.removeLast();
		KoPoint p1 = *m_bezierPoints.last();
		m_bezierPoints.removeLast();
		m_bezierPoints.removeLast();
		m_bezierPoints.append( new KoPoint( p1 ) );
		m_bezierPoints.append( new KoPoint( p1 ) );

		view()->setPos( p1 );
	}
	else
	{
		m_bezierPoints.clear();
	}

	draw();
}

void
VPolylineTool::accept()
{
	activate();
}

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

	if( m_action == 0 )
	{
	        KShortcut shortcut( TQt::Key_Plus );
      		shortcut.append( KShortcut( TQt::Key_F9 ) );
		m_action = new KRadioAction( i18n( "Polyline Tool" ), "14_polyline", shortcut, this, TQT_SLOT( activate() ), collection, name() );
		m_action->setToolTip( i18n( "Polyline" ) );
		m_action->setExclusiveGroup( "freehand" );
		//m_ownAction = true;
	}
}

void 
VPolylineTool::commandExecuted()
{
	cancel();
}

#include "vpolylinetool.moc"