/***************************************************************************
                          core.cpp  -  description
                             -------------------
    begin                : Tue Oct 2 2001
    copyright            : (C) 2003 by Troy Corbin Jr.
    email                : tcorbin@users.sourceforge.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

/* KDE */
#include <tdefiledialog.h>
#include <tdemessagebox.h>

/* TQt */
#include <tqpushbutton.h>

/* Local */
#include "core.moc"
#include "match.h"
#include "list_pgn.h"
#include "thinbuttons.h"
#include "io_engine.h"
#include "io_internet.h"
#include "match_param.h"

/* Tabs */
#include "tabmanager.h"

///////////////////////////////////////
//
//	core::core
//
///////////////////////////////////////
core::core(TQWidget *parent, const char *name, resource *Rsrc ) : TQWidget(parent,name)
{
  myResource = Rsrc;
  listPGN = NULL;
	currentMatch = NULL;

  myButtonGroup = new thinbuttons( this, "ButtonGroup", myResource );
  myButtonGroup->show();
  connect( myButtonGroup, TQ_SIGNAL( leftClick(int) ), this, TQ_SLOT( setMatch(int) ) );
	connect( myButtonGroup, TQ_SIGNAL( menuItemSelected(int,int) ), this, TQ_SLOT( thinButtonMenu(int,int) ) );

  myCoreClock.start( 100, FALSE );
  resize();
}

///////////////////////////////////////
//
//	core::~core
//
///////////////////////////////////////
core::~core()
{
	myCoreClock.stop();
	delete myButtonGroup;
}

///////////////////////////////////////
//
//	core::createNewIO
//
///////////////////////////////////////
void core::createNewIO( int type, int ID, int side, int str )
{
	io_internet *internetioPtr = NULL;
	io_engine *engineioPtr = NULL;
//	io_email *ioEmailPtr = NULL;
  switch( type )
  {
		/* PLAYERTCP */
    case PLAYERTCP:
        if( ID == Null )
        {
          /* Create a NEW internetIO */
          internetioPtr = new io_internet( this, myResource );
          myIOMap.add( ID, internetioPtr );
          internetioPtr->setID( ID );
          connect( internetioPtr, TQ_SIGNAL( sendCMD( const Command& ) ), this, TQ_SLOT( command2Match( const Command& ) ) );
        }
        else
        {
          /* Use an existing internetIO */
          internetioPtr = (io_internet*) myIOMap.find(Null);
          if( internetioPtr == NULL )
          {
            kdWarning() << "core::createNewIO: Trying to connect a match to an internetIO that doesn't exsist." << endl;
          }
          else
          {
            myIOMap.add( ID, internetioPtr );
          }
        }
        break;
		/* PLAYEREXAMINE */
    case PLAYEREXAMINE:
        /* examine mode needs only 1 io mapped to it */
        if( ID == Null )
        {
          /* Create a NEW internetIO */
          internetioPtr = new io_internet( this, myResource );
          myIOMap.add( ID, internetioPtr );
          internetioPtr->setID( ID );
          connect( internetioPtr, TQ_SIGNAL( sendCMD( const Command& ) ), this, TQ_SLOT( command2Match( const Command& ) ) );
        }
        else
        {
          internetioPtr = (io_internet*)  myIOMap.find(ID);
          if( internetioPtr == NULL )
          {
            /* no io mapped to the ID yet, use the internetio */
            /* Use an existing internetIO */
            internetioPtr = (io_internet*) myIOMap.find(Null);
            if( internetioPtr == NULL )
            {
              kdWarning() << "core::createNewIO: Trying to connect a match to an internetIO that doesn't exsist." << endl;
            }
            else
            {
              myIOMap.add( ID, internetioPtr );
            }            
          }
          /* this ID is already mapped we don't need another one */
        }        
        break;
		/* PLAYERPC */
		case PLAYERPC:
				engineioPtr = new io_engine( this, myResource );
				myIOMap.add( ID, engineioPtr );
				engineioPtr->setID( ID );
				connect( engineioPtr, TQ_SIGNAL( sendCMD( const Command& ) ), this, TQ_SLOT( command2Match( const Command& ) ) );
				engineioPtr->Start( side );
				engineioPtr->recvCMD( Command( ID, CMD_Set_Difficulty, TQString().setNum( 1 << str ) ) );
				break;
		/* PLAYEREMAIL */
		case PLAYEREMAIL:
//				ioEmailPtr = new io_email( this, myResource );
//				myIOMap.add( ID, ioEmailPtr );
//				ioEmailPtr->setID( ID );
//				connect( ioEmailPtr, TQ_SIGNAL( sendCMD( const Command& ) ), this, TQ_SLOT( command2Match( const Command& ) ) );
				break;
    default:
        /* No IO needed */
        break;
  }
}

///////////////////////////////////////
//
//	core::newMatch(match_params* param);
//
///////////////////////////////////////
match* core::newMatch( match_param* param )
{
  match *match_ptr = NULL;
  int newID;

  /* create a new ID for the match */
  newID = myIDManager.getNewID();

  /* Create the match navigation button */
  myButtonGroup->create( newID );

  /* Create the match */
  match_ptr = new match(this, param, myResource);
  match_ptr->setID( newID );
  myMatchMap.add( newID, match_ptr );

  /* Create IOs */
  createNewIO( param->type(WHITE), newID, WHITE, param->strength(WHITE) );
  createNewIO( param->type(BLACK), newID, BLACK, param->strength(BLACK) );

  /* Connect Signals */
  connect( match_ptr, TQ_SIGNAL( sendCMD( const Command& ) ), this, TQ_SLOT( command2IO( const Command& ) ) );
  connect( &myCoreClock, TQ_SIGNAL( timeout() ), match_ptr, TQ_SLOT( tick() ) );

  /* Set this new match as the current match */
  setMatch( newID );
  setActiveWindow();

	/* Save our configuration settings */
	myResource->ConfigWrite();

  return match_ptr;
}

///////////////////////////////////////
//
//  core::setMatch
//
///////////////////////////////////////
void core::setMatch( int ID )
{
  /*
    Disconnect the current match from the display
  */
  if( currentMatch != NULL )
  {
    currentMatch->setCurrent( FALSE );
    disconnect( currentMatch, TQ_SIGNAL( setClocks() ), this, TQ_SLOT( slot_setClocks() ) );
    disconnect( currentMatch, TQ_SIGNAL( setStatusBar( const int&, const TQString& ) ), this, TQ_SLOT( slot_setStatusBar( const int&, const TQString& ) ) );
    disconnect( currentMatch, TQ_SIGNAL( setNotation() ), this, TQ_SLOT( slot_setNotation() ) );
    currentMatch->setVisibility( FALSE );
  }
	/*
			Now display the new match, and connect it's signals to the GUI
	*/
	currentMatch = myMatchMap.find( ID );
	currentMatch->setVisibility( TRUE );
	myButtonGroup->setButton( ID );
	connect( currentMatch, TQ_SIGNAL( setClocks() ), this, TQ_SLOT( slot_setClocks() ) );
	connect( currentMatch, TQ_SIGNAL( setStatusBar( const int&, const TQString& ) ), this, TQ_SLOT( slot_setStatusBar( const int&, const TQString& ) ) );
	connect( currentMatch, TQ_SIGNAL( setNotation() ), this, TQ_SLOT( slot_setNotation() ) );
	currentMatch->setCurrent( TRUE );
	emit requestResize();
	emit initMatch();
	currentMatch->resendStatusBar();

	/* Give Ourselves Focus */
	setActiveWindow();
	topLevelWidget()->raise();
}

///////////////////////////////////////
//
//	core::clearAll
//
///////////////////////////////////////
bool core::clearAll( void )
{
	while( myMatchMap.count() )
	{
		if( clearMatch() != TRUE ) return FALSE;
	}
	return TRUE;
}
///////////////////////////////////////
//
//	core::clearMatch
//
///////////////////////////////////////
bool core::clearMatch( int id )
{
	match *departingMatch, *nextMatch;
	io_base *ioPtr;

	if( id == Null )
	{
		if( currentMatch != NULL )
		{
			id = currentMatch->getID();
		}
		else
		{
			return FALSE;
		}
	}
	departingMatch = myMatchMap.find(id);

	/* A bad ID was passed */
	if( departingMatch == NULL )
	{
		kdError() << "core::clearMatch: Bad matchID " << id << " could not be cleared." << endl;
		return FALSE;
	}

	/* Save if needed */
	if( departingMatch->modified() )
	{
		if( save( id, TRUE, FALSE ) == FALSE )
		{
			return FALSE;
		}
	}

	/* Get ID of another match so it can become the current match */
	if( departingMatch == currentMatch )
	{
		/* Find another match in the map */
		nextMatch = --myMatchMap;
		if( nextMatch == NULL )
		{
			++myMatchMap;
			nextMatch = ++myMatchMap;
		}
		/* Set the new current match */
		if( nextMatch != NULL )
		{
			setMatch( nextMatch->getID() );
		}
		else
		{
			currentMatch = NULL;
			emit initMatch();
		}
	}

	/* Remove IOs in this match... */
	ioPtr = myIOMap.find(id);
	while( ioPtr != NULL )
	{
		myIOMap.remove( id );
		if( ioPtr->getType() != io_base::INTERNET )
		{
			delete ioPtr;
		}
		ioPtr = myIOMap.find(id);
	}

	/* ...and remove the match */
	myMatchMap.remove( id );
	myButtonGroup->remove( id );
	delete departingMatch;

	resize();
	return TRUE;
}
///////////////////////////////////////
//
//	core::resize
//
///////////////////////////////////////
void core::resize( void )
{
	int size = 8 + ( 1 * ( myResource->ThemeBorder == TRUE ) );

	if( currentMatch != NULL )
	{
		currentMatch->resize();
		myButtonGroup->resize( currentMatch->width() );
		myButtonGroup->move( 0, currentMatch->height() );
		setFixedSize( currentMatch->width(), currentMatch->height() + myButtonGroup->height() );
	}
	else
	{
		myButtonGroup->resize( myResource->ThemeSize * size );
		myButtonGroup->move( 0, myResource->ThemeSize * size );
		setFixedSize( myResource->ThemeSize * size, ( myResource->ThemeSize * size ) + myButtonGroup->height() );
	}
}
///////////////////////////////////////
//
//	core::command2IO
//
///////////////////////////////////////
void core::command2IO( const Command &command )
{
  io_base *io;
  int id = ((Command)command).getID();

  io = myIOMap.find(id);
  while( io != NULL )
  {
    io->recvCMD(command);
    io = myIOMap.findNext();
  }
}
///////////////////////////////////////
//
//	core::command2Match
//
///////////////////////////////////////
void core::command2Match( const Command &command )
{
	match_param *paramPtr;
	match *matchPtr;
	Command cmd = command;

	matchPtr = myMatchMap.find( cmd.getID() );
  while( matchPtr != NULL )
	{
		/* Catch Commands Meant for Core */
		switch( cmd.getCommand() )
		{
			case CMD_Hint:
				KMessageBox::information( this, cmd.getData(), i18n("Hint") );
				break;
			case CMD_Tell_User:
				KMessageBox::information( this, cmd.getData(), i18n( "Tell User" ) );
				break;
			case CMD_Tell_User_Error:
        KMessageBox::sorry( this, cmd.getData(), i18n( "Tell User Error" ) );
				break;
			case CMD_Tell_Opponent:
				paramPtr = matchPtr->getParam();
				if( ( paramPtr->type( WHITE ) == PLAYERLOCAL ) ||
						( paramPtr->type( BLACK ) == PLAYERLOCAL ) )
						KMessageBox::information( this, cmd.getData(), i18n( "Tell Opponent" ) );
				else
						command2IO( command );
				break;
			case CMD_Tell_Others:
				command2IO( command );
				break;
			case CMD_Tell_ICS:
				command2IO( command );
				break;
			case CMD_Tell_All:
				paramPtr = matchPtr->getParam();
				if( ( paramPtr->type( WHITE ) == PLAYERLOCAL ) ||
						( paramPtr->type( BLACK ) == PLAYERLOCAL ) )
						KMessageBox::information( this, cmd.getData(), i18n( "Tell All" ) );
				command2IO( command );
				break;
			case CMD_Out_Of_Book:
//			engine[enginePtr - 1]->sendCMD( CMD_Check_Book );
				break;
			default:
				/* Just send it to the Match */
				matchPtr->recvCMD( command );
				break;
		}
		matchPtr = myMatchMap.findNext();
	}
}
///////////////////////////////////////
//
//	core::flag
//
///////////////////////////////////////
bool core::flag( const bool Army )
{
	if( currentMatch == NULL ) return FALSE;
	return currentMatch->flag( Army );
	return false;
}
///////////////////////////////////////
//
//	core::clock
//
///////////////////////////////////////
TQString core::clock( const bool Army )
{
	if( currentMatch == NULL ) return TQString();
	return currentMatch->clock( Army );
	return "";
}
///////////////////////////////////////
//
//	core::modified
//
///////////////////////////////////////
bool core::modified( void )
{
	if( currentMatch == NULL ) return FALSE;
	return currentMatch->modified();
	return false;
}
///////////////////////////////////////
//
//	core::paused
//
///////////////////////////////////////
bool core::paused( void )
{
	if( currentMatch == NULL ) return FALSE;
	return currentMatch->paused();
	return false;
}
///////////////////////////////////////
//
//	core::isOnline
//
///////////////////////////////////////
bool core::isOnline( void )
{
  unsigned int index;
  for( index=0; index < myIOMap.count(); index++ )
  {
    if( myIOMap[index]->getType() == io_base::INTERNET )
    {
      return TRUE;
    }
  }
  return FALSE;
}

///////////////////////////////////////
//
//	core::onMove
//
///////////////////////////////////////
char core::onMove( void )
{
	if( currentMatch == NULL ) return Null;
	return currentMatch->onMove();
}
///////////////////////////////////////
//
//	core::caption
//
///////////////////////////////////////
TQString core::caption( void )
{
	TQButton *Button;
	if( currentMatch == NULL ) return TQString();
	Button = myButtonGroup->find( currentMatch->getID() );
	if( Button != NULL ) Button->setText( currentMatch->caption() );
	return currentMatch->caption();
}
///////////////////////////////////////
//
//	core::whiteInput
//
///////////////////////////////////////
char core::whiteInput(void)
{
	if( currentMatch == NULL ) return Null;
	return currentMatch->input(WHITE);
}
///////////////////////////////////////
//
//	core::blackInput
//
///////////////////////////////////////
char core::blackInput(void)
{
	if( currentMatch == NULL ) return Null;
	return currentMatch->input(BLACK);
}
///////////////////////////////////////
//
//	core::review
//
///////////////////////////////////////
void core::review( int Index )
{
	if( currentMatch == NULL ) return;
	currentMatch->review( Index );
}
///////////////////////////////////////
//
//	core::inputOnMove
//
///////////////////////////////////////
char core::inputOnMove( bool reverse )
{
	if( currentMatch == NULL ) return Null;
	return currentMatch->inputOnMove( reverse );
}
///////////////////////////////////////
//
//	core::notation
//
///////////////////////////////////////
TQStringList* core::notation( void )
{
	if( currentMatch == NULL ) return NULL;
	return currentMatch->notation();
}
///////////////////////////////////////
//
//	core::slot_setNotation
//
///////////////////////////////////////
void core::slot_setNotation( void )
{
	emit setNotation();
}
///////////////////////////////////////
//
//	core::slot_setStatusBar
//
///////////////////////////////////////
void core::slot_setStatusBar( const int &ID, const TQString& MSG )
{
  emit setStatusBar( ID, MSG );
}
///////////////////////////////////////
//
//	core::slot_setClocks
//
///////////////////////////////////////
void core::slot_setClocks( void )
{
	emit setClocks();
}
///////////////////////////////////////
//
//	core::print
//
///////////////////////////////////////
void core::print( int ID )
{
	match *m2p;

	if( ID == Null )
	{
		m2p = currentMatch;
	}
	else
	{
		m2p = myMatchMap.find( ID );
	}
	if( m2p == NULL ) return;
	m2p->print();
	return;
}
///////////////////////////////////////
//
//	core::load
//
///////////////////////////////////////
bool core::load( void )
{
	const TQString fileFilter( "*.pgn *.Pgn *.PGN|Portable Game Notation (.pgn)\n*|All Files" );
	TQString URL;
	URL = KFileDialog::getOpenFileName( TQString(), TQString( fileFilter ), this, i18n( "Load PGN..." ) );
	if( URL.isEmpty() ) return FALSE;
	return load( URL );
}
bool core::load( const TQString &IncURL )
{
	listPGN = new list_pgn( 0, "listPGN" );
	myResource->tabManager->addTab( listPGN, IncURL.section( '/', -1 ) );
	connect( listPGN, TQ_SIGNAL( selected( const TQString&, const int& ) ), this, TQ_SLOT( load( const TQString&, const int& ) ) );
	listPGN->setURL( IncURL );
return TRUE;
}
bool core::load( const TQString &url, const int &pos )
{
	newMatch( new match_param( myResource, PLAYERLOAD, PLAYERLOAD ) );
	return currentMatch->load( url, pos );
}
///////////////////////////////////////
//
//	core::save
//
///////////////////////////////////////
bool core::save( const int ID, const bool Prompt, const bool SaveAs )
{
	const TQString fileFilter( "*.pgn *.Pgn *.PGN|Portable Game Notation (.pgn)\n*|All Files" );
	match *match2bSaved;
	int answer;
	TQString URL;

	if( ID == Null )
	{
		/* Use current match */
		match2bSaved = currentMatch;
	}
	else
	{
		match2bSaved = myMatchMap.find( ID );
	}
	if( match2bSaved == NULL ) return FALSE;
	if( Prompt )
	{
		/* Prompt for the save */
		myResource->ConfigWrite();
		answer = KMessageBox::warningYesNoCancel( this,
																							i18n("Would you like to save this match?"),
																							i18n("Save Match?"),
																							TQString(),
																							TQString(),
																							TQString("PromptForSaving") );
		myResource->ConfigRead();
		if( answer == KMessageBox::Cancel )
		{
			return FALSE;
		}
		if( answer == KMessageBox::No )
		{
			return TRUE;
		}
	}
	/* Get the last URL we used */
	URL = match2bSaved->url();
	if( ( URL.isEmpty() ) || ( SaveAs ) )
	{
		/* Get a new URL */
		URL = KFileDialog::getSaveFileName( TQString(), TQString( fileFilter ), this, i18n( "Save Match..." ) );
		if( URL.isNull() )
		{
			return FALSE;
		}
		if( URL.findRev( ".pgn", -1, FALSE ) == -1 )
		{
			URL += ".pgn";
		}
	}
	if( match2bSaved->save( URL ) == FALSE )
	{
		emit setStatusBar( SAVE_ERROR );
		return FALSE;
	}
	emit setStatusBar( SAVE_OK );
	match2bSaved->setModified( FALSE );
	return TRUE;
}
///////////////////////////////////////
//
//	core::matchMenu
//
///////////////////////////////////////
void core::matchMenu( int opt )
{
	unsigned int index;
	switch(opt)
	{
		/* Offer / Accept Draw */
		case MENU_OFFER_DRAW:		// Fall Through
		case MENU_ACCEPT_DRAW:
			if( whiteInput() == PLAYERLOCAL )
			{
				command2Match( Command( currentMatch->getID(), CMD_Offer_Draw, TQString("W") ) );
			}
			if( blackInput() == PLAYERLOCAL )
			{
				command2Match( Command( currentMatch->getID(), CMD_Offer_Draw, TQString("B") ) );
			}
			command2IO( Command( currentMatch->getID(), CMD_Offer_Draw ) );
			break;
		/* Reject Draw */
		case MENU_REJECT_DRAW:
			command2IO( Command( currentMatch->getID(), CMD_Reject_Draw ) );
			break;
		/* Resign */
		case MENU_RESIGN:
			if( ( whiteInput() == PLAYERLOCAL ) && ( currentMatch->onMove() == WHITE ) )
			{
				command2IO( Command( currentMatch->getID(), CMD_White_Resign ) );
				currentMatch->recvCMD( Command( currentMatch->getID(), CMD_White_Resign ) );
			}
			if( ( blackInput() == PLAYERLOCAL ) && ( currentMatch->onMove() == BLACK ) )
			{
				command2IO( Command( currentMatch->getID(), CMD_Black_Resign ) );
				currentMatch->recvCMD( Command( currentMatch->getID(), CMD_Black_Resign ) );
			}
			break;
		/* Retract */
		case MENU_RETRACT:
			command2IO( Command( currentMatch->getID(), CMD_Retract_Move ) );
			currentMatch->retract();
			currentMatch->retract();
			emit setNotation();
			if( onMove() == WHITE )
				emit setStatusBar( WHITE_TURN );
			else
				emit setStatusBar( BLACK_TURN );
			break;
		/* Move Now */
		case MENU_MOVE_NOW:
			command2IO( Command( currentMatch->getID(), CMD_MoveNow ) );
			break;
		/* Hint */
		case MENU_HINT:
			currentMatch->requestHint();
			break;
		/* Orientation */
		case MENU_ORIENTATION:
			currentMatch->flip();
			break;
		/* Ponder */
		case MENU_PONDER:
			if( myResource->OPTION_Ponder )
			{
				command2IO( Command( currentMatch->getID(), CMD_Ponder ) );
			}
			else
			{
				command2IO( Command( currentMatch->getID(), CMD_No_Pondering ) );
			}
			break;
		/* Pause */
		case MENU_PAUSE:
			currentMatch->setPaused( !currentMatch->paused() );
			if( currentMatch->paused() )
			{
				command2IO( Command( currentMatch->getID(), CMD_Pause ) );
			}
			else
			{
				command2IO( Command( currentMatch->getID(), CMD_Resume ) );
			}
			break;
		/* Pause All */
		case MENU_PAUSEALL:
			if( myMatchMap.count() )
			{
				for( index = 0; index < myMatchMap.count(); index++ )
				{
					if( ( whiteInput() == PLAYERTCP ) || ( blackInput() == PLAYERTCP ) )
						continue;
					myMatchMap[index]->setPaused( !myMatchMap[index]->paused() );
					if( myMatchMap[index]->paused() )
					{
						command2IO( Command( currentMatch->getID(), CMD_Pause ) );
					}
					else
					{
						command2IO( Command( currentMatch->getID(), CMD_Resume ) );
					}
				}
			}
			break;
		/* Call Flag */
		case MENU_CALL_FLAG:
			if( whiteInput() == PLAYERLOCAL )
			{
				command2IO( Command( currentMatch->getID(), CMD_White_Called_Flag ) );
				currentMatch->recvCMD( Command( currentMatch->getID(), CMD_White_Called_Flag ) );
			}
			if( blackInput() == PLAYERLOCAL )
			{
				command2IO( Command( currentMatch->getID(), CMD_Black_Called_Flag ) );
				currentMatch->recvCMD( Command( currentMatch->getID(), CMD_Black_Called_Flag ) );
			}
			break;
		default:
			break;
	}
}
///////////////////////////////////////
//
//	core::thinButtonMenu
//
///////////////////////////////////////
void core::thinButtonMenu( int id, int item )
{
	switch( item )
	{
		case MENU_SAVE:
			save( id, FALSE, FALSE );
			break;
		case MENU_SAVEAS:
			save( id, FALSE, TRUE );
			break;
		case MENU_PRINT:
			print( id );
			break;
		case MENU_CLOSE:
			clearMatch( id );
			break;
		default:
			break;
	}
}

///////////////////////////////////////
//
//  core::resetServer
//
///////////////////////////////////////
void core::resetServer( void )
{
  command2IO( Command( Null, CMD_Reset_Server ) );
}

///////////////////////////////////////
//
//  core::goOffline()
//
///////////////////////////////////////
void core::goOffline(void)
{
  unsigned int index(0);
	io_base *ioPtr(NULL);

	while( index < myIOMap.count() )
	{
		if( myIOMap[index]->getType() == io_base::INTERNET )
		{
			ioPtr = myIOMap[index];

			if( myIOMap.keyAt(index) != Null )
			{
				clearMatch( myIOMap.keyAt(index) );
			}
			else
			{
				myIOMap.remove( Null );
			}

			index = 0;
			continue;
		}
		index++;
	}
	if( ioPtr != NULL )
		delete ioPtr;
}

