/***************************************************************************
                          geoobjects.cpp  -  description
                             -------------------
    begin                : Sun Feb 20 2000
    copyright            : (C) 2000 by Marc Bartsch
    email                : marc.bartsch@topmail.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 "geoobjects.h"
#include <iostream.h>

GeoObjects::GeoObjects()
{
	isChosen 								= false;
	isMovable 							= false;
	isTrace 								= false;
	initComplete 						= false;

	acceptsQPoint 					= false;
	acceptsQPointOnly				= true;
	acceptsGeoPoint 				= false;
	canBeDestroyed 					= false;

	size 										= 1;
	lineThickness 					= 1;
  zOrder 									= 0;
  radius 									= 0;
	level 									= -1;
	ID 											= -1;
	uniqueID 								= -1;

 	kapp->config()->setGroup( "Colors" );
	if ( kapp->config()->hasKey( "Draw" ) )
	{
		color	= kapp->config()->readColorEntry( "Draw" );
	}
	else
	{
		color 									= Qt::black;
	}
	identificator 					= "GeoObject";
}

GeoObjects::~GeoObjects()
{
}

void GeoObjects::paint( QPainter* )
{
}

void GeoObjects::move( QPoint* )
{
}

void GeoObjects::move()
{
}

void GeoObjects::initObject( GeoObjects* obj )
{
	//	I will take GeoObject as one of my new parents.
  parents.append( obj );

	if ( processParams( obj ) )
	{

		GeoObjects *actual;
		int newLevel = 1;

		for ( actual = parents.first(); actual; actual = parents.next() )
		{
			if ( actual->level >= newLevel )
			{
				newLevel = actual->level + 1;
			}
			actual->children.append( this );
			actual->setChosen( false );
		}
	
		level = newLevel;

		move();

		initComplete = true;
	}
	else
	{
		parents.current()->setChosen( true );
	}
}

void GeoObjects::initObject( QPoint* pt )
{
	coords.set( *pt );
	MetricsCenter::mapCanvasToCanvasAttachToGrid( coords );

	int newLevel = 1;
	level = newLevel;

	move();

	initComplete = true;
}

void GeoObjects::initObject( GeoObjects *pt, QPoint *pts )
{
	//	I will take GeoObject as one of my new parents.
  parents.append( pt );

	//	I can also add a QPoint, if GeoObject is the root of all objects.
	if ( pts != 0 )
	{
		coords.setX( pts->x() );
		coords.setY( pts->y() );
	}

	if ( processParams( pt ) )
	{

		GeoObjects *actual;
		int newLevel = 1;

		for ( actual = parents.first(); actual; actual = parents.next() )
		{
			if ( actual->level >= newLevel )
			{
				newLevel = actual->level + 1;
			}
			actual->children.append( this );
			actual->setChosen( false );
		}
	
		level = newLevel;

		move();

		initComplete = true;
	}
	else
	{
		parents.current()->setChosen( true );
	}
}

void GeoObjects::moveMe( Coordinates& )
{
}

void GeoObjects::moveMe( Coordinates& , double )
{
}

double GeoObjects::positionMe( Coordinates& )
{
	return 0;
}

void GeoObjects::setChosen( bool chosen )
{
	isChosen = chosen;
}

bool GeoObjects::getMovable()
{
	return isMovable;
}

int GeoObjects::getLevel()
{
	return level;
}

void GeoObjects::setLevel( int level )
{
	this->level = level;
}

void GeoObjects::setUniqueID( int uniqueID )
{
	this->uniqueID = uniqueID;
}

QPoint GeoObjects::getQPoint()
{
	return QPoint( 0, 0 );
}

Coordinates GeoObjects::getCoordinates()
{
	return coords;
}

Coordinates GeoObjects::getStartCoordinates()
{
	return startCoords.coords;
}

Coordinates	GeoObjects::getEndCoordinates()
{
	return endCoords.coords;
}

double GeoObjects::getRadius()
{
	return radius;
}

double GeoObjects::getAngle()
{
	return 0;
}

void GeoObjects::setTrace( bool tr )
{
	isTrace = tr;

	if ( !tr )
	{
		trace.clear();
	}
}

bool GeoObjects::getTrace()
{
	return isTrace;
}

void GeoObjects::setColor( QColor col )
{
	color = col;
}

QColor GeoObjects::getColor()
{
	return color;
}

void GeoObjects::setWidth( int widthValue )
{
	width = widthValue;
//	move();
}

void GeoObjects::setSize( int sizeValue )
{
	size = sizeValue;
//	move();
}

int GeoObjects::getSize()
{
	return size;
}

int GeoObjects::getID()
{
	return ID;
}

void GeoObjects::setMoveOffset( QPoint *pt )
{
	Coordinates coord1( *pt );
	moveOffset = getCoordinates() - coord1;
}

void GeoObjects::setMoveOffset( Coordinates & coords )
{
	moveOffset = getCoordinates() - coords;
}

bool GeoObjects::validParameter( GeoObjects* obj )
{
	//	Make sure, object is not among parents. Otherwise check whether
	//	this object can occur in infinite numbers.
	bool amongParents = parents.contains( obj );

	//	Run through params
	parameterList::iterator iter;
	iter = params.begin();

	while ( iter != params.end() )
	{
		//	Found the object in parameter list, or if it is a GeoPoint...
		if ( ( ( *iter ).ID == obj->getID() ) ||
			 	 ( ( ( *iter ).ID == ID_geoPoint ) && ( isGeoPoint( obj ) ) ) ||
			 	 ( ( ( *iter ).ID == ID_geoLine ) && ( isGeoLine( obj ) ) ) ||
			 	 ( ( *iter ).ID == ID_geoObject ) )
		{
     	if ( amongParents )
			{
				if ( ( *iter ).num == ID_infinite )
				{
					return true;
				}
			}
			else
			{
				if ( ( *iter ).num > 0 )
				{
					return true;
				}
			}
		}

		iter++;
	}

	return false;
}

bool GeoObjects::contains( QPoint* )
{
	return false;
}

QString GeoObjects::identify()
{
	return identificator;
}

bool GeoObjects::isGeoPoint()
{
	return isGeoPoint( this );
}

void GeoObjects::writeToFile( KSimpleConfig* )
{
}

void GeoObjects::readFromFile( KSimpleConfig* )
{
}

void GeoObjects::getObjectOverlay( QList <QRect> & )
{
}

void GeoObjects::deleteObjects( QList <GeoObjects> & )
{
}

void GeoObjects::addObjects( QList <GeoObjects> & )
{
}

void GeoObjects::convertParams( int list[], int length )
{
	int i = 0;
	myParams p;

	//	Convert parameters from array to list
	while ( i < length )
	{
		if ( list[i] != -1 )
		{
			p.ID = list[i];
			p.num = list[i+1];
			i++;i++;
		}
		else
		{			
			p.ID = -1;
			p.num = 0;
			i++;
		}
		params.push_back( p );
	}
}

bool GeoObjects::processParams( GeoObjects *obj )
{
	parameterList::iterator iter;
	parameterList::iterator itertest;
	parameterList::iterator iterBegin;
	parameterList::iterator iterEnd;
	int sum = 0;
	bool changed = false;

	iter = params.begin();
	iterBegin = params.begin();

	if ( iter == params.end() )
		return true;

	while ( iter != params.end() )
	{
		//	cout << ( *iter ).ID << ": " << ( *iter ).num << ", ";

		//	We found occurrence of object and count down
		if ( ( ( *iter ).ID == obj->getID() ) ||
			 	 ( ( ( *iter ).ID == ID_geoPoint ) && ( isGeoPoint( obj ) ) ) ||
			 	 ( ( ( *iter ).ID == ID_geoLine ) && ( isGeoLine( obj ) ) ) ||
			 	 ( ( *iter ).ID == ID_geoObject ) )
		{
			if ( (*iter).num > 0 )
			{
				(*iter).num -= 1;
			}
			changed = true;
		}		

		sum += (*iter).num;

		//	This is the end of this sequence
		if ( (*iter).ID == -1 )
		{
			iter++;
			iterEnd = iter;

			//	If nothing has changed delete whole sequence
			if ( !changed )
			{
				params.erase( iterBegin, iterEnd );
        iterBegin = iter;
       	//	Print out parameters to make sure
       	itertest = params.begin();
			}
			iterBegin = iter;

			//	If this sequence is down to zero with at least one change, return true
			if ( changed && ( sum == 0 ) )
			{
				return true;
			}
			sum = 0;
			changed = false;
		}
    else
		{
		iter++;
		}
	}

	return false;
}

bool GeoObjects::isGeoPoint( GeoObjects *obj )
{
	int myID = obj->getID();

	if ( myID == ID_point )
		return true;
	if ( myID == ID_pointxy )
		return true;
	if ( myID == ID_bisection )
		return true;
	if ( myID == ID_mirrorPoint )
		return true;
	if ( myID == ID_rotation )
		return true;
	if ( myID == ID_move )
		return true;
	if ( myID == ID_pointOnLine )
		return true;
	if ( myID == ID_pointOfConc )
		return true;

	return false;
}

bool GeoObjects::isGeoLine( GeoObjects *obj )
{
	int myID = obj->getID();

	if ( myID == ID_line )
		return true;
	if ( myID == ID_segment )
		return true;
	if ( myID == ID_parallel )
		return true;
	if ( myID == ID_ray )
		return true;
	if ( myID == ID_perpendicular )
		return true;
	if ( myID == ID_vector )
		return true;

	return false;
}

bool GeoObjects::getAcceptsQPoint()
{
	return acceptsQPoint;
}

bool GeoObjects::getAcceptsQPointOnly()
{
	return acceptsQPointOnly;
}

bool GeoObjects::getAcceptsGeoPoint()
{
	return acceptsGeoPoint;
}

bool GeoObjects::getCanBeDestroyed()
{
	return canBeDestroyed;
}

bool GeoObjects::getInitComplete()
{
	return initComplete;
}

void GeoObjects::setInitComplete( bool initComplete )
{
	this->initComplete = initComplete;
}

int GeoObjects::getZOrder()
{
	return zOrder;
}

void GeoObjects::setZOrder( int zOrder )
{
	this->zOrder = zOrder;
}

void GeoObjects::getLineOverlay( QList <QRect> & list, int size, Coordinates coord1, Coordinates coord2 )
{
	Coordinates coord3;

	coord3 = coord2 - coord1;
	coord3.normalize( size );

  QRect lastRect( 0, 0, size, size );
	QPoint center = coord2.getQPoint();
	lastRect.moveCenter( center );

	int counter = 0;

	//	Do it as long as rectangles don't intersect
	do
	{
		list.append( new QRect( 0, 0, size, size ) );
		center = QPoint( coord1.getI_X() + ( coord3.getD_X() * counter ), coord1.getI_Y() + ( coord3.getD_Y() * counter ) );
		list.current()->moveCenter( center );
		counter++;
	}
	while( !list.current()->intersects( lastRect ) );

	list.append( new QRect( lastRect ) );
}

void GeoObjects::getCircleOverlay( QList <QRect> & list, int size, QRect rect, Coordinates coord, double rad )
{
	//	Since this is a recursive method, start with end state
	Coordinates coord1, coord2, coord3, coord4;

	coord1 = Coordinates( rect.topLeft() );
	coord2 = Coordinates( rect.topRight() );
	coord3 = Coordinates( rect.bottomLeft() );
	coord4 = Coordinates( rect.bottomRight() );

	double radBig 	= rad - 15;
	double radSmall = rad + 10;

	if ( ( coord1.getDistance( coord ) < radBig ) &&
			 ( coord2.getDistance( coord ) < radBig ) &&
			 ( coord3.getDistance( coord ) < radBig ) &&
			 ( coord4.getDistance( coord ) < radBig ) )
	return;

	if ( ( coord1.getDistance( coord ) > radSmall ) &&
			 ( coord2.getDistance( coord ) > radSmall ) &&
			 ( coord3.getDistance( coord ) > radSmall ) &&
			 ( coord4.getDistance( coord ) > radSmall ) )
	return;

	//	Ok, the circle hits this rect. Has it got the right size or do we have to go on?
	if ( rect.width() < size )
	{
		list.append( new QRect( rect ) );
	}
	else
	{
		int rectRad = rect.width() / 2;

  	QRect rect1( rect );
  	rect1.setRight( rect1.left() + rectRad );
  	rect1.setBottom( rect1.top() + rectRad );

  	QRect rect2( rect );
  	rect2.setLeft( rect2.left() + rectRad );
  	rect2.setBottom( rect2.top() + rectRad );

  	QRect rect3( rect );
  	rect3.setRight( rect3.left() + rectRad );
  	rect3.setTop( rect3.top() + rectRad );

  	QRect rect4( rect );
  	rect4.setLeft( rect4.left() + rectRad );
  	rect4.setTop( rect4.top() + rectRad );

		getCircleOverlay( list, size, rect1, coord, rad );
		getCircleOverlay( list, size, rect2, coord, rad );
		getCircleOverlay( list, size, rect3, coord, rad );
		getCircleOverlay( list, size, rect4, coord, rad );
	}
}




