/***************************************************************************
                          kgeo.cpp  -  description
                             -------------------
    begin                : Sam Aug 11 19:50:48 CEST 2001
    copyright            : (C) 2001 by Marc Bartsch
    email                : marc.bartsch@web.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 files for QT
#include <qdir.h>
#include <qprinter.h>
#include <qpainter.h>

// include files for KDE
#include <kiconloader.h>
#include <kmessagebox.h>
#include <kfiledialog.h>
#include <kmenubar.h>
#include <klocale.h>
#include <kconfig.h>
#include <kstdaction.h>

// application specific includes
#include "kgeo.h"
#include "kgeo.moc"

void KGeoApp::savePreferences()
{
	//	Save preferences window in global config file.
	kapp->config()->setGroup( "Startup" );
	kapp->config()->writeEntry( "Splashscreen", preferencesDialog->SplashScreen->isChecked() );
	kapp->config()->writeEntry( "Maximize",  		preferencesDialog->Maximize->isChecked() );
	kapp->config()->writeEntry( "DragMode",  		preferencesDialog->DragMode->isChecked() );

	kapp->config()->setGroup( "Grid" );
	kapp->config()->writeEntry( "GridLines", preferencesDialog->GridPoint->isChecked());
	kapp->config()->writeEntry( "Axes",  		preferencesDialog->Coordinates->isChecked());
	kapp->config()->writeEntry( "Numbers",  preferencesDialog->Numbers->isChecked());
	kapp->config()->writeEntry( "HighestXValue",  preferencesDialog->HighestXValue->value());
	kapp->config()->writeEntry( "HighestYValue",  preferencesDialog->HighestYValue->value());
	kapp->config()->writeEntry( "DistanceRatio",  preferencesDialog->DistanceRatio->value());

	kapp->setOverrideCursor(waitCursor);

	// 	Save current drawing to a temporary file and load it again after we changed
	//	the metrics, so the drawing reflects all changes.
	QString tempFile = locateLocal("appdata", "kgeo.temp");

	QFile file( tempFile );
	file.remove();
  file.close();

	KSimpleConfig *config = new KSimpleConfig( tempFile );
	saveObjects( config );
  config->sync();

	//	Change metrics.
	MetricsCenter::xMax = preferencesDialog->HighestXValue->value();
	MetricsCenter::yMax = preferencesDialog->HighestYValue->value();
	MetricsCenter::grid = preferencesDialog->GridPoint->isChecked();
	MetricsCenter::axes = preferencesDialog->Coordinates->isChecked();
	MetricsCenter::numbers = preferencesDialog->Numbers->isChecked();
	MetricsCenter::xDistanceRatio =  preferencesDialog->DistanceRatio->value();
	MetricsCenter::generateMetrics();

	//	Load temporary file.
	loadObjects( config );
	delete config;

	canvas->setNumberRange( preferencesDialog->HighestXValue->value(), preferencesDialog->HighestYValue->value() );
	canvas->showGridLines( preferencesDialog->GridPoint->isChecked() );
	canvas->showAxes( preferencesDialog->Coordinates->isChecked() );
	canvas->showNumbers( preferencesDialog->Numbers->isChecked() );
	canvas->setUnit( preferencesDialog->DistanceRatio->value() );
	canvas->drawGrid();
	canvas->drawObjects( objectList );

	kapp->restoreOverrideCursor();
}

void KGeoApp::getPreferences()
{
	//	Read preferences window from config object.
	kapp->config()->setGroup( "Startup" );
	preferencesDialog->SplashScreen->setChecked(	kapp->config()->readBoolEntry( "Splashscreen", TRUE ) );
	preferencesDialog->Maximize->setChecked( 			kapp->config()->readBoolEntry( "Maximize", TRUE ) );
	preferencesDialog->DragMode->setChecked( 			kapp->config()->readBoolEntry( "DragMode", TRUE ) );

	kapp->config()->setGroup("Grid");
	preferencesDialog->GridPoint->setChecked( 		kapp->config()->readBoolEntry( "GridLines", TRUE ) );
	preferencesDialog->Coordinates->setChecked( 	kapp->config()->readBoolEntry( "Axes", TRUE ) );
	preferencesDialog->Numbers->setChecked( 			kapp->config()->readBoolEntry( "Numbers", TRUE ) );
	preferencesDialog->HighestXValue->setValue( 	kapp->config()->readNumEntry( "HighestXValue", 11 ) );
	preferencesDialog->HighestYValue->setValue( 	kapp->config()->readNumEntry( "HighestYValue", 7 ) );
	preferencesDialog->DistanceRatio->setValue( 	kapp->config()->readNumEntry( "DistanceRatio", 5 ) );
}

void KGeoApp::deselectParamObjects()
{
	//	When creating a new object, the parameter objects are all highlighted.
	//	This has to be set to not selected when the draw mode changes before the
	//	new object has completely been drawn.
	GeoObjects *actual;

	for ( actual = paramObjects.first(); actual; actual = paramObjects.next() )
	{
		actual->setChosen( false );
	}
	paramObjects.clear();

	canvas->drawObjects( objectList );
}

void KGeoApp::changeMousePointer( Coordinates coords )
{
	QList <GeoObjects> list;
	QCursor cursor = standardCursor;
	QPoint pt = coords.getQPoint();

	if ( drawingMode ==  ID_drawingModeMovingObjects )
	{
		//	In move mode, mouse cursor should change over movable objects only
		getAllMovableObjects( &pt, list );

		if ( list.count() > 0 )
		{
			cursor =  KCursor::handCursor();
		}
	}

	//	In construction mode, mouse cursor should only change over objects
	//	that are valid parameters for the new object
	if ( ( drawingMode ==  ID_drawingModeConstructing ) )
	{
		getAllValidObjects( newObject, &pt, list );

		if ( list.count() > 0 )
		{
			cursor =  KCursor::handCursor();
		}
		//	This means now, that we are not over a valid object. Cursor should
		//	be cross for new points or normal for no possible action at all.
		else if ( ( newObject->getAcceptsGeoPoint() ) ||
						  ( !newObject->getAcceptsGeoPoint() && newObject->getAcceptsQPoint() && newObject->getAcceptsQPointOnly() ) )
		{
			cursor =  KCursor::crossCursor();
		}
		else
		{
			cursor =  KCursor::arrowCursor();
		}
	}

	canvas->setCursor( cursor );
}

GeoObjects * KGeoApp::selectObject( QList <GeoObjects> list )
{
	QList <GeoObjects> pointsOnly;
	GeoObjects *actual, *selected;
  QListIterator<GeoObjects> iter( list );

	selected = iter.toFirst();

	for ( actual = iter.toFirst(); actual; actual = ++iter )
	{
		if( actual->isGeoPoint() )
		{
			pointsOnly.append( actual );
		}
	}

	if ( pointsOnly.count() > 0 )
	{
	  QListIterator<GeoObjects> iterPoints( pointsOnly );

  	for ( actual = iterPoints.toFirst(); actual; actual = ++iterPoints )
  	{
 			if ( actual->getLevel() < selected->getLevel() )
  			{
 				selected = actual;
   		}
  	}
	}
	else
	{
  	for ( actual = iter.toFirst(); actual; actual = ++iter )
  	{
 			if ( actual->getLevel() < selected->getLevel() )
  			{
 				selected = actual;
   		}
  	}
	}

	return selected;
}

void KGeoApp::insertObject ( Coordinates coords )
{
	QPoint pt = coords.getQPoint();

	//	First, get parameters that determine which init method of
	//	the object has to be called.
	QList <GeoObjects> list;
	getAllValidObjects( newObject, &pt, list );
	GeoObjects *tempObject = list.first();
  bool acceptsExistGeoObj = ( list.count() != 0 );
	bool acceptsQPoint = newObject->getAcceptsQPoint();
	bool acceptsQPointOnly = newObject->getAcceptsQPointOnly();
	bool acceptsNewPoint = newObject->getAcceptsGeoPoint();
	bool deletesParamObjects = ( newObject->getID() == ID_eraser );

	//	Second, analyse those parameters and call individual init methods.
	if ( acceptsQPointOnly && acceptsQPoint && !acceptsNewPoint && !acceptsExistGeoObj )
	{
		newObject->initObject( &pt );
	}
	else if ( acceptsQPoint && acceptsExistGeoObj )
	{
		newObject->initObject( tempObject, &pt );
	}
	else if ( !acceptsQPoint && acceptsExistGeoObj )
	{
		newObject->initObject( tempObject );
	}
	else if ( !acceptsQPoint && acceptsNewPoint )
	{
		tempObject = new Point( &pt );
		objectList.append( tempObject );
		newObject->initObject( tempObject );
	}
	else
	{
		return;
	}

	//	Third, add used parameter objects to param list, so that they can be
	//	deselected at the end. This will either be a new point or the object
	//	under the mouse cursor.
	if ( tempObject && !deletesParamObjects )
	{
		paramObjects.append( tempObject );
	}

	//	Fourth, check whether this objects is complete
	if ( newObject->getInitComplete() )
	{
		//	Some objects have created new objects or want to delete some:
		newObject->addObjects( objectList );
		newObject->deleteObjects( objectList );

		//	If this is a tool object, it can now be destroyed
		if ( newObject->getCanBeDestroyed() )
		{
			delete newObject;
			newObject = 0;
		}
		else
		{
			objectList.append( newObject );
			newObject = 0;
		}

  	deselectParamObjects();
		move();
		kapp->config()->setGroup( "Startup" );
		if ( kapp->config()->readBoolEntry( "DragMode", TRUE ) )
		{
			startMoveMode();
		}
		else
		{
			toolBar2Clicked( currentButtonID );
		}
	}

	//	Fifth, draw all objects
	canvas->drawObjects( objectList );
}

void  KGeoApp::selectPoint( Coordinates coords )
{
	//	Get list of movable objects under mouse cursor
	QList <GeoObjects> list;

	getAllMovableObjects( coords, list );
	currentObject = selectObject( list );

	if ( currentObject != 0 )
	{
  	//	Get list of all children, because they will move, too.
    getObjectsRecursion( currentObject, list );

		//	And now create sorted list that contains those objects that will be moved
		moveList.clear();
		GeoObjects *actual;
		GeoObjects *actualMove;
    QListIterator<GeoObjects> it1( list );
    QListIterator<GeoObjects> it2( moveList );

		//	Go through all Objects in unsorted list and insert them
		//	into moveList according to their level.
		//	This makes sure, that parents will always be before children
		for ( actual = it1.toFirst(); actual; actual = ++it1 )
		{
  		for ( actualMove = it2.toFirst(); actualMove; actualMove = ++it2 )
  		{
				if ( ( actualMove->getLevel() > actual->getLevel() ) && ( moveList.find( actual ) == -1 ) )
				{
					moveList.find( actualMove );
					moveList.insert( moveList.at(), actual );
				}
  		}

			if ( moveList.find( actual ) == -1 )
			{
				moveList.append( actual );
			}
		}

		//	All objects that don't move.
		QList <GeoObjects> staticList;
		getInvertedList( objectList, list, staticList );

		//	Prepare moving
		canvas->prepareMoving( staticList, moveList );
		currentObject->setMoveOffset( coords );

		startDragMode();
	}
}

void  KGeoApp::movePoint( Coordinates coords )
{
	QListIterator<GeoObjects> it( moveList );
  GeoObjects *actual = it.toFirst();
	QPoint pt = coords.getQPoint();

	//	Move first object with point pt. The rest not.
	actual->move( &pt );
	actual = ++it;

	//	Move every object
	for ( actual = it.current(); actual; actual = ++it )
	{
		actual->move();
	}

	if ( currentObject!=0 )
	{
		canvas->drawMovedObjects( moveList );
	}
}

void  KGeoApp::move()
{
	QListIterator<GeoObjects> it( objectList );
  GeoObjects *actual = it.toFirst();

	//	Move every object
	for ( actual = it.current(); actual; actual = ++it )
	{
		actual->move();
	}
}

void  KGeoApp::releaseMouse( Coordinates )
{
	//	Since pt does not correspond with the virtual grid, pt might
	//	have to be moved to attach to the underlying grid
	if ( moveList.first()->parents.count() == 0 )
	{
  	Coordinates coords;
  	coords = moveList.first()->getCoordinates();
  	moveList.first()->setMoveOffset( coords );
		MetricsCenter::mapCanvasToCanvasAttachToGrid( coords );
  	movePoint( coords );
  }

	canvas->drawObjects( objectList );
	startMoveMode();
}

void KGeoApp::slotMouseRightPressEvent( QPoint * )
{
	kontextMenu->exec( QCursor::pos() );
}

void KGeoApp::slotMouseRightPressEvent( Coordinates )
{
	kontextMenu->exec( QCursor::pos() );
}

void KGeoApp::printCoordsInStatusline ( Coordinates coords )
{
	//	Print current mouseposition in statusbar. Positions are calculated in
	//	virtual coordinates.
	MetricsCenter::mapCanvasToGrid( coords );

  QString myCoords = " x = " + coords.getD_X( 2 ) + " : y = " + coords.getD_Y( 2 );

  statusBar()->changeItem( myCoords, 2 );
}

void KGeoApp::printFilenameInWindowTitle()
{
   setPlainCaption( Str_AppName  " - " + currentFilename );
}

void KGeoApp::keyPressEvent( QKeyEvent *ev )
{
	if ( ev->ascii() == 27 ) // ESC
	{
		if ( 	currentButtonID != ID_buttonPointer )
		{
	  	deselectParamObjects();
			move();
  		startMoveMode();
			canvas->drawObjects( objectList );
		}
	}
	else
	{
		ev->ignore();
	}
}


