/***************************************************************************
                     challenge_graph.cpp  -  description
                             -------------------
    begin                : Mon Jan 7 2002
    copyright            : (C) 2003 by Eric Faccer
    email                : e.faccer@qut.edu.au
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 <unistd.h>
#include <string.h>
#include <stdlib.h>

#include <kiconloader.h>
#include <kstddirs.h>
#include <tdepopupmenu.h>

#include <tqapplication.h>
#include <tqcanvas.h>
#include <tqlabel.h>
#include <tqpen.h>
#include <tqscrollview.h>
#include <tqwidget.h>
#include <tqrect.h>
#include <tqbrush.h>
#include <tqpainter.h>
#include <tqregexp.h>

#include "challenge_graph.moc"
#include "challenge_graph_view.h"
#include "challenge_rectangle.h"
#include "command.h"
#include "definitions.h"

/* SIZE is for the size of the challenge square. It could become a class variable */
#define SIZE 8
#define OFFSET 2
#define SPACER SIZE+OFFSET

///////////////////////////////////////
//
//	Challenge_Graph::Constructor
//
///////////////////////////////////////
Challenge_Graph::Challenge_Graph( TQWidget* parent, const char* name, resource *Rsrc )
	: TQVBox(parent, name), myResource(Rsrc)
{
	max_rating = 2600;
	max_time = 60;
	seek = FALSE;

	graph = new TQCanvas( 0, "Challenge_Graph" );
	myStatusBar = new TQLabel( this, "Challenge_Graph_Status_Bar" );
	myView = new Challenge_Graph_View( *graph, this, "Challenge_Graph_View", 0, myStatusBar );

	TQT_BASE_OBJECT_NAME::connect(myView, TQT_SIGNAL(leftClick(int)), TQT_SLOT(selectMatch(int)));
	TQT_BASE_OBJECT_NAME::connect(myView, TQT_SIGNAL(rightClick(Challenge_Game*, const TQPoint&)), TQT_SLOT(display_menuSeek(Challenge_Game*, const TQPoint&)));


	/* Setup Style for myStatusBar */
	myStatusBar->setAlignment( TQt::AlignAuto | TQt::AlignVCenter | TQt::SingleLine );
	myStatusBar->setFrameStyle( TQFrame::Panel | TQFrame::Sunken );
	clear();

	menuSeek = new TDEPopupMenu( this );
	menuSeek->setCheckable( TRUE );
	menuSeek->insertItem( i18n("Seek Matches"), this, TQT_SLOT( menuFunct(int) ), 0, MENU_SEEK );
	menuSeek->insertSeparator();
	menuSeek->insertItem( i18n("Accept This Match"), this, TQT_SLOT( menuFunct(int) ), 0, MENU_ACCEPT_MATCH );
	menuSeek->insertItem( i18n("Tell..."), this, TQT_SLOT( menuFunct(int) ), 0, MENU_TELL );
	menuSeek->insertItem( i18n("Assess..."), this, TQT_SLOT( menuFunct(int) ), 0, MENU_ASSESS );
	menuSeek->insertItem( i18n("Player Info"), this, TQT_SLOT( menuFunct(int) ), 0, MENU_FINGER );
	menuSeek->insertItem( TQIconSet( myResource->LoadIcon( TQString("history"), TDEIcon::Small ) ),
												i18n("Player History"), this, TQT_SLOT( menuFunct(int) ), 0, MENU_HISTORY );
	menuSeek->insertSeparator();
	menuSeek->insertItem( i18n("Add to Friends"), this, TQT_SLOT( menuFunct(int) ), 0, MENU_NOTIFY );
	menuSeek->insertItem( i18n("Ignore This Player"), this, TQT_SLOT( menuFunct(int) ), 0, MENU_CENSOR );
	menuSeek->setItemChecked( MENU_SEEK, FALSE );
}
///////////////////////////////////////
//
//	Challenge_Graph::Destructor
//
///////////////////////////////////////
Challenge_Graph::~Challenge_Graph()
{
}
///////////////////////////////////////
//
//	Challenge_Graph::resizeEvent
//
///////////////////////////////////////
void Challenge_Graph::resizeEvent( TQResizeEvent *e )
{
	if( e->size() != e->oldSize() )
	{
		TQSize newsize = e->size();
		x_size = newsize.width() - 8;
		y_size = newsize.height() - myStatusBar->height();
		graph->resize( x_size, y_size );
		createBackground();
	}
}
///////////////////////////////////////
//
//	Challenge_Graph::add
//
///////////////////////////////////////
void Challenge_Graph::add(Challenge_Game *seek)
{
	double AdjustedClock = (float)seek->clock() + ( ( (float)seek->increment() / 60.0 ) * 20.0 );
	double my_x = (float)x_size / (float)max_time * AdjustedClock;
	double mag = (float)max_rating / (float)y_size;
	double my_y = ( max_rating - seek->rating() ) / mag;

	//bleah
	//a bit of a hack for the status bar real estate & values larger then MAX
	if ( seek->rating() < 250)
		my_y = (max_rating - 250) / mag;
	else if ( seek->rating() >= max_rating )
		my_y = 10;

	if ( seek->clock() > (max_time-3) )
		my_x = x_size/max_time * (max_time-3);

	int time_control_x = (int)(my_x);
	int rating_y = (int)(my_y);

	if ( isEmpty(time_control_x, rating_y) )
		drawChallenge(time_control_x, rating_y, seek->rated(), seek);
	else
		addTo_Nearest_Neighbour(time_control_x, rating_y, seek->rated(), seek);
}
///////////////////////////////////////
//
//	Challenge_Graph::clear
//
///////////////////////////////////////
void Challenge_Graph::clear()
{
	myView->reset();
	graph->update();
}
///////////////////////////////////////
//
//	Challenge_Graph::drawChallenge
//
///////////////////////////////////////
void Challenge_Graph::drawChallenge(int time_control_x, int rating_y, bool rated, Challenge_Game *challenge)
{
	/* This next peice of unelegant code is fairly unnecessary. It is to keep everything on the screen */
	/* It's based on the theory of prophylaxis */
	int my_x = time_control_x;
	int my_y = rating_y;

	if( my_y >= y_size - SPACER)
		my_y = y_size - SPACER;
	if( my_y < SPACER )
		my_y = SPACER;
	if( my_x >= x_size - ( SPACER ) )
		my_x = x_size - ( SPACER ); /*Since squares draw from the top left */
	if( my_x < SPACER )
		my_x = SIZE;

	TQCanvasPolygonalItem *item = new Challenge_Rectangle(my_x, my_y, SIZE, SIZE, graph, challenge);
	
	item->setPen( myResource->COLOR_GraphForeground );
	item->move( time_control_x, rating_y );
	item->show();

	if( rated )
		item->setBrush( TQBrush(myResource->COLOR_GraphForeground) );
	else
		item->setBrush( TQBrush(myResource->COLOR_GraphBackground, TQt::SolidPattern) );

	item->show();
	graph->update();
}
///////////////////////////////////////
//
//	Challenge_Graph::isEmpty
//
///////////////////////////////////////
bool Challenge_Graph::isEmpty(int x, int y)
{
	TQCanvasItemList l = graph->collisions( TQRect(x, y, SIZE, SIZE) );

	if( l.count() )
		return false;
	return true;
}

/* This function does a spiral search for the nearest neighbour */
/* Pre: TRUE
   Post: Finds the nearest non-overlapping position to the original coordinates */
///////////////////////////////////////
//
//	Challenge_Graph::addTo_Nearest_Neighbour
//
///////////////////////////////////////
bool Challenge_Graph::addTo_Nearest_Neighbour(int orig_x, int orig_y, bool rated, Challenge_Game * challenge, int searchdepth)
{
	int right = 1;
	int down = 2;
	int left = 2;
	int up = 3;

	int x = orig_x;
	int y = orig_y - ( SIZE + OFFSET );

	/* Check the location on top*/
	if ( isEmpty( x, y ) )
	{
		drawChallenge( x, y, rated, challenge );
		return true;
	}

	/* Now search in a spiral */
	while( right < searchdepth )
	{
		for( int i = 0; i < right; i++ )
		{
			x += SPACER;
			if( isEmpty( x, y ) )
			{
				drawChallenge( x, y, rated, challenge );
				return true;
			}
		}
		for( int i = 0; i < down; i++ )
		{
			y += SPACER;
			if( isEmpty( x, y ) )
			{
				drawChallenge( x, y, rated, challenge );
				return true;
			}
		}
		for( int i = 0; i < left; i++ )
		{
			x -= SPACER;
			if( isEmpty( x, y) )
			{
				drawChallenge( x, y, rated, challenge );
				return true;
			}
		}
		for( int i = 0; i < up; i++ )
		{
			y -= SPACER;
			if( isEmpty( x, y ) )
			{
				drawChallenge( x, y, rated, challenge );
				return true;
			}
		}

		/* Grow the spiral bounds */
		right += 2;
		down += 2;
		left += 2;
		up += 2;
	}
	return false;
}
///////////////////////////////////////
//
//	Challenge_Graph::createBackground
//
///////////////////////////////////////
void Challenge_Graph::createBackground( void )
{
	TQPainter painter;
	TQColor ink;
	TQWMatrix matrix;
	int colorTotal;

	background.resize( y_size, x_size );
	background.fill( myResource->COLOR_GraphBackground );

	/* Find out if we have a dark bgcolor or a light one. */
	colorTotal = (	myResource->COLOR_GraphBackground.red() +
									myResource->COLOR_GraphBackground.blue() +
									myResource->COLOR_GraphBackground.green() );

	/* Set our ink to something that will contrast well */
	if( colorTotal < 384 ) // 384 = 50% gray
		ink = myResource->COLOR_GraphBackground.light();
	else
		ink = myResource->COLOR_GraphBackground.dark();

	/* Paint the text on */
	painter.begin( &background );
	painter.setFont( myResource->FONT_Standard );
	painter.setPen( ink );
	painter.drawText( 64, 12, i18n( "Rating" ) );
	painter.end();
	matrix.rotate( -90.0 );
	background = background.xForm( matrix );
	painter.begin( &background );
	painter.setFont( myResource->FONT_Standard );
	painter.setPen( ink );
	painter.drawText( 64, y_size - 8, i18n( "Time" ) );
	painter.end();

	graph->setBackgroundPixmap( background );
}
///////////////////////////////////////
//
//	Challenge_Graph::selectMatch
//
///////////////////////////////////////
void Challenge_Graph::selectMatch( int matchID )
{
	if( matchID )
		emit sendCMD( Command( 0, CMD_Start_Match, TQString::number( matchID ) ) );
}
///////////////////////////////////////
//
//	Challenge_Graph::menuFunct
//
///////////////////////////////////////
void Challenge_Graph::menuFunct( int funct )
{
	switch( funct )
	{
		case MENU_SEEK:
			emit sendCMD( Command( 0, CMD_Toggle_Seek ) );
			break;
		case MENU_FINGER:
			emit sendCMD( Command( 0, CMD_Player_Finger, selectedPlayerName ) );
			break;
		case MENU_TELL:
			emit sendCMD( Command( 0, CMD_Set_Input, TQString( "tell %1 " ).arg( selectedPlayerName ) ) );
			break;
		case MENU_NOTIFY:
			emit sendCMD( Command( 0, CMD_Add_Friend, selectedPlayerName ) );
			break;
		case MENU_CENSOR:
			emit sendCMD( Command( 0, CMD_Ignore_Player, selectedPlayerName ) );
			break;
		case MENU_HISTORY:
			emit sendCMD( Command( 0, CMD_Player_History, selectedPlayerName ) );
			break;
		case MENU_ACCEPT_MATCH:
			emit sendCMD( Command( 0, CMD_Start_Match, TQString::number( selectedMatchID ) ) );
			break;
		case MENU_ASSESS:
			emit sendCMD( Command( 0, CMD_Assess, selectedPlayerName ) );
			break;
		default:
			break;
	}
}
///////////////////////////////////////
//
//	Challenge_Graph::updateSoughtList
//
///////////////////////////////////////
void Challenge_Graph::updateSoughtList( void )
{
	unsigned int loop;
	Challenge_Game *cg;

	clear();
	for( loop = 0; loop < SF_01.count(); loop++ )
	{
		cg = new Challenge_Game(	SF_01[loop],
															SF_02[loop],
															SF_03[loop],
															SF_04[loop],
															SF_05[loop],
															SF_06[loop],
															SF_07[loop] );
		add( cg );
	}
	SF_01.clear();
	SF_02.clear();
	SF_03.clear();
	SF_04.clear();
	SF_05.clear();
	SF_06.clear();
	SF_07.clear();
	seek = TRUE;
}
///////////////////////////////////////
//
//	Challenge_Graph::addSoughtItem
//
///////////////////////////////////////
void Challenge_Graph::addSoughtItem( const TQString &src )
{
	TQStringList fields = TQStringList::split( TQChar(' '), src, FALSE );
	SF_01 << fields[2]; // Name
	SF_02 << fields[1]; // Rating
	SF_03 << fields[6]; // Match Type
	SF_04 << fields[5]; // Is Rated?
	SF_05 << fields[3]; // Base Time
	SF_06 << fields[4]; // Increment
	SF_07 << fields[0]; // ID#
}
///////////////////////////////////////
//
//	Challenge_Graph::display_menuSeek
//
///////////////////////////////////////
void Challenge_Graph::display_menuSeek( Challenge_Game *Item, const TQPoint &Pos )
{
	bool enable;
	if( Item != NULL )
	{
		selectedPlayerName = Item->_player.replace( TQRegExp("\\(.+\\)"), TQString("") );
		selectedMatchID = Item->id();
		enable = TRUE;
	}
	else
	{
		enable = FALSE;
	}
	menuSeek->setItemChecked( MENU_SEEK, seek );
	menuSeek->setItemEnabled( MENU_FINGER, enable );
	menuSeek->setItemEnabled( MENU_TELL, enable );
	menuSeek->setItemEnabled( MENU_NOTIFY, enable );
	menuSeek->setItemEnabled( MENU_CENSOR, enable );
	menuSeek->setItemEnabled( MENU_HISTORY, enable );
	menuSeek->setItemEnabled( MENU_ACCEPT_MATCH, enable );
	menuSeek->setItemEnabled( MENU_ASSESS, enable );
	menuSeek->popup( Pos );
}
///////////////////////////////////////
//
//	Challenge_Graph::recvCMD
//
///////////////////////////////////////
void Challenge_Graph::recvCMD( const Command& command )
{
	switch(((Command)command).getCommand())
	{
		case CMD_Add_Sought_Match:
			addSoughtItem( ((Command)command).getData() );
			break;
		case CMD_Show_Sought_List:
			updateSoughtList();
			break;
		case CMD_Hide_Sought_List:
			clear();
			seek = FALSE;
			break;
		default:
			break;
	}
}
