/***************************************************************************
                          io_engine.cpp  -  description
                             -------------------
    begin                : Sat Jun 30 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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <kprocess.h>
#include <kicontheme.h>

#include <tqregexp.h>
#include <tqfile.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "io_engine.moc"
#include "definitions.h"
#include "proto_base.h"
#include "proto_xboard.h"
#include "proto_uci.h"

io_engine::io_engine( TQWidget *parent, resource *Rsrc ) : io_base( parent, Rsrc )
{
	myType							= io_base::LOCAL;
	engine							= NULL;
	Log									= NULL;
	proto								= NULL;
	SafeToSend					= true;
	CleanBuffer					= true;
	SendSIGINT					= false;
	Forced							= true;
	Protocol						= XBoard;
	FIFO_In = "";
	FIFO_Out = "";
}
//
io_engine::~io_engine()
{
	if( engine )
	{
		Kill();
	}
	if( Log )
	{
		Log->close();
		delete Log;
	}
	if( proto )
	{
		delete proto;
 }
}
///////////////////////////////////////
//
//	io_engine::Start
//
///////////////////////////////////////
void io_engine::Start( const int side )
{
	TQStringList args;
	unsigned int tmp;
	int ID;
	bool Army;

	/* Stop accidents */
	if( engine != NULL )
		return;
	engine = new TDEProcess();

	/* Convert 'side' to 'ID' */
	if( side == WHITE )
	{
		ID = ENGINE_WHITE;
		Army = WHITE;
	}
	else if( side == BLACK )
	{
		ID = ENGINE_BLACK;
		Army = BLACK;
	}
	else if( side == WHITE_HELPER )
	{
		ID = ENGINE_WHITE_BK;
		Army = WHITE;
	}
	else
	{
		ID = ENGINE_BLACK_BK;
		Army = BLACK;
	}

	/* Get and parse engine config from resource */
	for( IT = myResource->engines.begin(); IT != myResource->engines.end(); ++IT )
	{
		if( (*IT).CurrentRef & ID )
			break;
	}
	if( IT == myResource->engines.end() )
	{
		kdError() << "io_engine::Start: Can not find engine resource ID " << ID << endl;
		return;
	}

	/* ...protocol */
	Protocol = (*IT).Protocol;
	switch( Protocol )
	{
		case UCI:
			proto = new proto_uci( myID );
			break;
		case XBoard:
		default:
			proto = new proto_xboard( myID );
			break;
	}
	connect( proto, TQ_SIGNAL( output( const TQString& ) ), this, TQ_SLOT( WriteFIFO( const TQString& ) ) );
	connect( proto, TQ_SIGNAL( output( const Command& ) ), this, TQ_SLOT( recvProtoCMD( const Command& ) ) );

	/* ...engine's display name */
	proto->parse( Command( myID, CMD_Set_Name, (*IT).Name ) );

	/* ...engine file name */
	(*engine) << (*IT).Filename;

	/* ...command line arguments */
	args = TQStringList::split( TQString(" "), (*IT).Arguments, false );
	for( tmp = 0; tmp < args.count(); tmp++ )
	{
		(*engine) << args[tmp];
	}

	/* ...log file */
	if( !(*IT).LogFile.isEmpty() )
	{
		Log = new TQFile( (*IT).LogFile );
		if( !Log->open( IO_WriteOnly | IO_Append ) )
			if( !Log->open( IO_WriteOnly ) )
				kdError() << "io_engine::Start: Can not open " << (*IT).LogFile << " for writing." << endl;
	}

	/* Showtime */
	if( !engine->start( TDEProcess::NotifyOnExit, TDEProcess::All ) )
	{
		kdError() << "io_engine::Start: Can not run the engine: " << (*IT).Filename << endl;
		return;
	}
	connect( engine, TQ_SIGNAL( wroteStdin( TDEProcess* ) ), this, TQ_SLOT( SendClear( TDEProcess* ) ) );
	connect( engine, TQ_SIGNAL( receivedStdout( TDEProcess*, char*, int ) ), this, TQ_SLOT( Recv( TDEProcess*, char*, int ) ) );
	connect( engine, TQ_SIGNAL( receivedStderr( TDEProcess*, char*, int ) ), this, TQ_SLOT( Recv( TDEProcess*, char*, int ) ) );

	proto->parse( Command( myID, CMD_Init ) );

	if( myResource->OPTION_Ponder )
		proto->parse( Command( myID, CMD_Ponder ) );
	else
		proto->parse( Command( myID, CMD_No_Pondering ) );

	proto->parse( Command( myID, CMD_NewGame ) );

	if( Army == WHITE )
		proto->parse( Command( myID, CMD_Play_White ) );
	else
		proto->parse( Command( myID, CMD_Play_Black ) );
	return;
}
///////////////////////////////////////
//
//	io_engine::Kill
//
///////////////////////////////////////
void io_engine::Kill( void )
{
	proto->parse( Command( myID, CMD_Exit ) );
	if( engine != NULL )
		delete engine;
}
///////////////////////////////////////
//
//	io_engine::sendToChild
//
///////////////////////////////////////
void io_engine::sendToChild( void )
{
	if( !SafeToSend || FIFO_Out.isEmpty() || !engine->isRunning() )
		return;

	/* Interrupt those engines that want or need it */
	if( SendSIGINT )
	{
		engine->kill( SIGINT );
		SendSIGINT = false;
	}

	/* Write it */
	SafeToSend = false;
	if( engine->writeStdin( FIFO_Out.latin1(), FIFO_Out.length() ) )
	{
		/* Print the output to the log file */
		if( Log )
		{
			FIFO_Out.prepend( "<- " );
			Log->writeBlock( FIFO_Out.latin1(), FIFO_Out.length() );
		}
		FIFO_Out = "";
	}
	else
		kdError() << "io_engine::sendToChild: Could not write to engine." << endl;
}
///////////////////////////////////////
//
//	io_engine::Recv
//
///////////////////////////////////////
void io_engine::Recv( TDEProcess*, char *buffer, int bufLen )
{
	char *newBuff = new char[bufLen + 1];
	strncpy( newBuff, buffer, bufLen );
	newBuff[bufLen] = 0;
	FIFO_In += newBuff;
	delete[] newBuff;

	if( FIFO_In.contains( TQChar('\n') ) )
	{
		TQString proc = FIFO_In.left( FIFO_In.findRev( TQChar('\n') ) );
		FIFO_In = FIFO_In.right( FIFO_In.length() - proc.length() );

		/* Split and Parse Full Lines of Input */
		TQStringList strList = TQStringList::split( "\n", proc );
		for( unsigned int loop = 0; loop < strList.count(); loop++ )
		{
			/* Print the input to the log file */
			if( Log )
			{
				TQString data = "-> " + strList[loop] + "\n";
				Log->writeBlock( data.latin1(), data.length() );
			}
			proto->parse( strList[loop] );
		}
	}
	return;
}
///////////////////////////////////////
//
//	io_engine::WriteFIFO
//
///////////////////////////////////////
void io_engine::WriteFIFO( const TQString &Data )
{
	TQString data = Data;
	if( data.isEmpty() )
		return;
	if( data.right(1) != "\n" )
		data += "\n";
	FIFO_Out += data;
	sendToChild();
	return;
}
///////////////////////////////////////
//
//	io_engine::recvCMD
//
///////////////////////////////////////
void io_engine::recvCMD( const Command &command )
{
	proto->parse( command );
}

///////////////////////////////////////
//
//	io_engine::recvProtoCMD
//
///////////////////////////////////////
void io_engine::recvProtoCMD( const Command &command )
{
	Command cmd = command;

	switch( cmd.getCommand() )
	{
		/* Command: Send_SIGTERM */
		case CMD_Send_SIGTERM:
			engine->kill();
			break;
		/* Command: Send_SIGINT */
		case CMD_Send_SIGINT:
			SendSIGINT = true;
			break;
		/* Command to Core */
		default:
			emit sendCMD( command );
			break;
 }
}

void io_engine::SendClear( TDEProcess* )
{
	SafeToSend = true;
	if( !FIFO_Out.isEmpty() )
		sendToChild();
}

