/***************************************************************************
                          io_internet.cpp  -  description
                             -------------------
    begin                : Thu Aug 16 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 <ksock.h>
#include <kprocess.h>
#include <tdemessagebox.h>

/* TQt */
#include <tqobject.h>
#include <tqwidget.h>
#include <tqregexp.h>
#include <tqdns.h>
#include <tqsocket.h>
#include <tqptrlist.h>

/* Local */
#include "io_internet.moc"
#include "resource.h"
#include "dlg_login.h"
#include "dlg_challenge.h"
#include "match.h"
#include "core.h"
#include "audio.h"
#include "../config.h"

static const int TXT_Std = 0;
static const int TXT_Pvt = 1;
static const int TXT_Ch  = 2;
static const int TXT_Sht = 3;
static const int TXT_Wsp = 4;
static const int TXT_Not = 5;

io_internet::io_internet( TQWidget *parent, resource *rsrc )
{
  myResource = rsrc;
  challenge = NULL;
  myTimeseal = NULL;
  socket = NULL;
  loginStage = LOGIN_STAGE_NAME;
  lineBuffer = "";
  Log = NULL;

  /* set type */
  this->myType = io_base::INTERNET;

  /* initialize various variables */
  this->waiting_for_move_list = false;
  this->myParent = parent;
  connected = false; /* we are not connected to a server */

  /* create the login dialog and show it to the user */
  loginDlg = new dlg_login( myParent, "LoginDialog", myResource);
  connect(loginDlg, TQT_SIGNAL( okClicked() ), this, TQT_SLOT( connectToServer() ) );
  connect(loginDlg, TQT_SIGNAL( cancelClicked() ), this, TQT_SLOT( selfDestruct() ) );
	connect(loginDlg, TQT_SIGNAL( login(TQString, TQString) ), this, TQT_SLOT( setUserInfo(TQString, TQString) ) );
}

///////////////////////////////////////////////////////////
// Destructor
///////////////////////////////////////////////////////////
io_internet::~io_internet()
{
  TabMap::Iterator i;

  /* Close and remove socket */
  if( socket != NULL )
  {
    if(socket->socket() != -1)
    {
      send("quit");
    }
    delete socket;
  }
  /* Close and remove logfile */
  if( Log )
  {
    Log->close();
    delete Log;
    Log = NULL;
  }
  /* Remove Timeseal */
  if( myTimeseal != NULL )
  {
    myTimeseal->kill();
    delete myTimeseal;
  }
  /* Close and remove Tabs */

  for(i = myTabList.begin(); i != myTabList.end(); i++)
  {
    if(myResource->tabManager->isTab(*i))
    {
      myResource->tabManager->removeTab(TQT_TQOBJECT(*i));
    }
  }
}

void io_internet::connectToServer()
{
  serverList::iterator i;
  TQWidget *tempTab;
  TQWidget *consoleTab;
  TQValueList<Command>::iterator j;

  tqApp->mainWidget()->setCursor( myResource->CURSOR_Thinking );
  /* Get and parse server config from resource */
  myServer = NULL;
  for( i = myResource->servers.begin(); i != myResource->servers.end(); i++ )
  {
    if( (*i).CurrentRef )
    {
      myServer = &(*i);
    }
  }
  if(myServer == NULL)
  {
    /* No server configured. Notify User and die gracefully */
    tqApp->mainWidget()->setCursor( myResource->CURSOR_Standard );
    kdError() << "internetio::internetio: Can not find server resource CurrentRef " << endl;
    KMessageBox::sorry( (TQWidget*)myParent, i18n( "There are no servers configured.\nPlease make sure you have at least one server configured." ), i18n( "Cannot find a server."));
    TQApplication::postEvent( tqApp->mainWidget(), new TQCustomEvent( EVENT_Del_IO_Net ) );
    return;
  }

  if(myServer->Port == 0)
  {
    myServer->Port = 5000;
  }

  if(!myServer->Timeseal.isEmpty())
  {
    myTimeseal = new TDEProcess();
    (*myTimeseal) << myServer->Timeseal << myServer->URL << TQString().setNum( myServer->Port )
                  << TQString( "-p" ) << TQString().setNum(myServer->Port + 1);
    if(!myTimeseal->start())
    {
      /* Couldn't start Timeseal. Notify User and die gracefully */
      tqApp->mainWidget()->setCursor( myResource->CURSOR_Standard );
      KMessageBox::sorry( (TQWidget*)myParent, i18n( "Knights can not start Timeseal.\nPlease make sure you have the correct path and filename configured." ), i18n( "Cannot find Timeseal."));
      TQApplication::postEvent( tqApp->mainWidget(), new TQCustomEvent( EVENT_Del_IO_Net ) );
      return;
    }
    socket = new TDESocket("127.0.0.1", myServer->Port + 1, 30);
    if(socket->socket() == -1)
    {
      /* try again on a different port, somehow the port hasn't been freed yet */
      delete socket;
      myTimeseal->kill();

      myTimeseal = new TDEProcess();
      (*myTimeseal) << myServer->Timeseal << myServer->URL << TQString().setNum( myServer->Port )
                    << TQString( "-p" ) << TQString().setNum(myServer->Port + 2);
      if(!myTimeseal->start())                                              
      {
        /* Couldn't start Timeseal. Notify User and die gracefully */
        tqApp->mainWidget()->setCursor( myResource->CURSOR_Standard );
        KMessageBox::sorry( (TQWidget*)myParent, i18n( "Knights can not start Timeseal.\nPlease make sure you have the correct path and filename configured." ), i18n( "Cannot find Timeseal."));
        TQApplication::postEvent( tqApp->mainWidget(), new TQCustomEvent( EVENT_Del_IO_Net ) );
        return;
      }
      socket = new TDESocket("127.0.0.1", myServer->Port + 2, 30);
      if(socket->socket() == -1)
      {
        /* if we can't do it the second time, give up */
        /* Couldn't connect to server through Timeseal. Notify User and die gracefully */
        tqApp->mainWidget()->setCursor( myResource->CURSOR_Standard );
        KMessageBox::sorry( (TQWidget*)myParent, i18n( "Knights is unable to connect to the server.\n Please make sure your internet connection is working and try again."), i18n( "Cannot connect to server(timeseal)."));
        TQApplication::postEvent( tqApp->mainWidget(), new TQCustomEvent( EVENT_Del_IO_Net ) );
        return;
      }
    }
  }
  else
  {
    socket = new TDESocket(myServer->URL.utf8(), myServer->Port, 30);
    if(socket->socket() == -1)
    {
      /* Couldn't connect to server. Notify User and die gracefully */
      tqApp->mainWidget()->setCursor( myResource->CURSOR_Standard );
      KMessageBox::sorry( (TQWidget*)myParent, i18n( "Knights is unable to connect to the server.\n Please make sure your internet connection is working and try again."), i18n( "Cannot connect to server."));
      TQApplication::postEvent( tqApp->mainWidget(), new TQCustomEvent( EVENT_Del_IO_Net ) );
      return;
    }
  }
  socket->enableRead(true);
  socket->enableWrite(true);

  /* connect a signal to readReady */
  connect(socket, TQT_SIGNAL(readEvent(TDESocket *)), this, TQT_SLOT(readCommand(TDESocket *)));

  /* setup the seekTimer and turn it off by default */
  seekTimer = new TQTimer(this);
  connect(seekTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(processSeekTimer()));

  /* succesfully connected to the server, turn connected on and flush the command buffer */
  connected = true;
  for(j = myCommandBuffer.begin(); j != myCommandBuffer.end(); j++)
  {
    recvCMD(*j);
  }

  /*
      Create ICS Related Tabs

			These will need to be moved into thier own functions because we want to be able to open
			and close these tabs at will, or have multiple open at once. ALL communication to our tabs
			needs to be done with SIGNALS & SLOTS to facilitate this.
	*/
	consoleTab = new Console( 0, myServer->Name.utf8(), myResource );
	myTabList[consoleTab] = consoleTab;
	myResource->tabManager->addTab( myTabList[consoleTab], i18n( "%1 Console" ).arg( myServer->Name ) );
  connect( myTabList[consoleTab], TQT_SIGNAL( sendCMD( const Command& ) ), this, TQT_SLOT( recvCMD( const Command& ) ) );
	connect( this, TQT_SIGNAL( sendCMD( const Command& ) ), myTabList[consoleTab], TQT_SLOT( recvCMD( const Command& ) ) );

	tempTab = new tab_SeekList( 0, "seekList", myResource );
	myTabList[tempTab] = tempTab;
	myResource->tabManager->addTab( myTabList[tempTab], i18n( "Sought Matches List" ) );
  connect( myTabList[tempTab], TQT_SIGNAL( sendCMD( const Command& ) ), this, TQT_SLOT( recvCMD( const Command& ) ) );
	connect( this, TQT_SIGNAL( sendCMD( const Command& ) ), myTabList[tempTab], TQT_SLOT( recvCMD( const Command& ) ) );

	tempTab = new Challenge_Graph( 0, "seekGraph", myResource );
	myTabList[tempTab] = tempTab;
	myResource->tabManager->addTab( myTabList[tempTab], i18n( "Sought Matches Graph" ) );
  connect( myTabList[tempTab], TQT_SIGNAL( sendCMD( const Command& ) ), this, TQT_SLOT( recvCMD( const Command& ) ) );
	connect( this, TQT_SIGNAL( sendCMD( const Command& ) ), myTabList[tempTab], TQT_SLOT( recvCMD( const Command& ) ) );

	myResource->tabManager->showTab(consoleTab);

  /* ...log file */
  if( !myServer->LogFile.isEmpty() )
  {
    Log = new TQFile( myServer->LogFile );
    if( !Log->open( IO_WriteOnly | IO_Append ) )
    {
      if( !Log->open( IO_WriteOnly ) )
			{
				kdError() << "Can not open " << myServer->LogFile << " for writing." << endl;
			}
    }
  }
	tqApp->mainWidget()->setCursor( myResource->CURSOR_Standard );
}

void io_internet::recvCMD(const Command& command)
{
  TQString error_message;

  if(!connected)
  {
    myCommandBuffer.push_back(command);
    return;
  }
  
  switch(((Command)command).getCommand())
  {
    case CMD_Move:
      send(((Command)command).getMove().SAN);
      break;
    case CMD_Reset_Server:
      sendUserSettings();
      break;
    case CMD_Toggle_Seek:
      if(seekTimer->isActive())
      {
        /* timer running stop it */
        seekTimer->stop();
        emit sendCMD( Command( 0, CMD_Hide_Sought_List ) );
      }
      else
      {
        /* timer not running start it */
        seekTimer->start(myResource->Seek_Timer * 100);
        /* send a sought now */
        processSeekTimer();
      }
      break;
    case CMD_Player_Finger:
      send("$finger " + ((Command)command).getData());
      break;
    case CMD_Add_Friend:
      send("$+notify " + ((Command)command).getData());
      break;
    case CMD_Ignore_Player:
      send("$+censor " + ((Command)command).getData());
      break;
    case CMD_Player_History:
      send("$history " + ((Command)command).getData());
      break;
    case CMD_Start_Match:
      send("$play " + ((Command)command).getData());
      break;
    case CMD_Assess:
      send("$assess " + ((Command)command).getData());
      break;
    case CMD_Set_Input:
      sendCMD( command );
      break;
    case CMD_Send_To_ICS:
			if(((Command)command).getData().contains(TQRegExp("^(?:\\.|tell)")))
			{
				writeToConsole(((Command)command).getData(), "K_PVT");
      }
			send(((Command)command).getData());
      break;
    case CMD_Examine_Forward:
      send("$forward 1");
      break;
    case CMD_Examine_Backward:
      send("$backward 1");
      break;
		case CMD_White_Resign:
		case CMD_Black_Resign:
			send("$resign");
			break;
		case CMD_Offer_Draw:
			send("$draw");
			break;
		case CMD_Reject_Draw:
			send("$decline t draw");
			break;
    default:
      /* do nothing unknown command */
      kdWarning() << "InternetIO::sendCMD received an unknown command: " << ((Command)command).getCommand() << endl;
 	}
}

///////////////////////////////////////
//
//  io_internet::send
//
///////////////////////////////////////
void io_internet::send(const TQString& msg)
{
	TQString tmp(msg);
	ssize_t len;

  /* Attach events to specific outgoing text */
  if( ( tmp == "accept" ) ||
      ( tmp == "decline" ) ||
      ( tmp.left(6) == "match " ) )
  {
    if( challenge != NULL )
    {
      delete challenge;
      nullifyChallenge();
    }
  }
  if( tmp.right(1) != "\n" )
	{
		tmp += "\n";
	}

  len = write(socket->socket(), tmp.latin1(), tmp.length() );

	if( Log )
	{
		Log->writeBlock( TQString("<< ").latin1(), 3 );
		Log->writeBlock( tmp.latin1(), tmp.length() );
	}
  if( len < (signed)tmp.length() )
	{
		kdWarning() << "io_internet::Send: Failed to write full block of data to socket." << endl;
	}
}

///////////////////////////////////////
//
//	io_internet::readCommand
//
///////////////////////////////////////
void io_internet::readCommand(TDESocket* socket)
{
  char buffer[READ_BUFFER_SIZE];
  TQString tmp;
  TQStringList lines;
  
  memset(buffer, 0, READ_BUFFER_SIZE);
  read(socket->socket(), buffer, READ_BUFFER_SIZE);
  tmp = buffer;

  tmp =  lineBuffer + tmp;
  if( Log )
  {
    Log->writeBlock( tmp.latin1(), tmp.length() );
  }
  if(loginStage != LOGIN_STAGE_LOGGED_IN)
  {
    // route all the data to the login parser
    parseLoginData(tmp);
    this->parseMode = NORMAL_MODE;
  }
  else
  {
    lines = TQStringList::split( TQRegExp("\n\r?"), tmp, FALSE );
    if(!(tmp.endsWith("\n\r") || tmp.endsWith("\n"))) 
    {
      lineBuffer = (*(--lines.end()));
    }
    else
    {
      lineBuffer = "";
    }
    for(TQStringList::iterator i = lines.begin(); i != lines.end(); i++)
    {
      if(lineBuffer == "" || i != --lines.end())
      {
        (*i).replace( TQRegExp( "\\a" ), "" );
        parseLine(*i);
      }
    }
  }
}
///////////////////////////////////////
//
//	io_internet::sendUserName
//
///////////////////////////////////////
void io_internet::sendUserName()
{
  loginStage = LOGIN_STAGE_PASSWORD;
  send(userName);
}
///////////////////////////////////////
//
//	io_internet::sendPassword
//
///////////////////////////////////////
void io_internet::sendPassword()
{
  send(passWord);
}

///////////////////////////////////////
//
//  io_internet::parseLoginData
//
///////////////////////////////////////
void io_internet::parseLoginData( TQString data )
{
  TQStringList lines;

  if(loginStage == LOGIN_STAGE_NAME)
  {
    if(data.contains( "login:" ))
    {
			sendUserName();
    }
    lines = TQStringList::split( TQRegExp("\n\r?"), data, FALSE );
    for(TQStringList::iterator i = lines.begin(); i != lines.end(); i++)
    {
      writeToConsole((*i), "K_STD");
    }
  }
  else if(loginStage == LOGIN_STAGE_PASSWORD)
  {
    if(data.contains("**** Invalid password! ****") ||
       data.contains("Sorry, names can only consist of lower and upper case letters.  Try again.") ||
			 data.contains("If you are not a registered player, enter guest or a unique ID."))
    {
      loginDlg = new dlg_login( myParent, "LoginDialog", myResource);
			loginDlg->disableServerSelect();
		  connect(loginDlg, TQT_SIGNAL( okClicked() ), this, TQT_SLOT( sendUserName() ) );
		  connect(loginDlg, TQT_SIGNAL( cancelClicked() ), this, TQT_SLOT( selfDestruct() ) );
			connect(loginDlg, TQT_SIGNAL( login(TQString, TQString) ), this, TQT_SLOT( setUserInfo(TQString, TQString) ) );
    }
    else if(data.contains("Press return to enter the server as"))
    {
      TQRegExp guestName("Logging you in as \"(\\w*)\"");
      int pos = guestName.search(data);
      if(pos > -1)
      {
        userName = guestName.cap(1);
      }
      send("\n");
    }
    else if(data.contains("password:"))
    {
      sendPassword();
    }
    else
    {
      loginStage = LOGIN_STAGE_LOGGED_IN;
      sendUserSettings();
    }
    lines = TQStringList::split( TQRegExp("\n\r?"), data, FALSE );
    for(TQStringList::iterator i = lines.begin(); i != lines.end(); i++)
    {
      writeToConsole((*i), "K_STD");
    }
  }
  else
  {
    lines = TQStringList::split( TQRegExp("\n\r?"), data, FALSE );
    for(TQStringList::iterator i = lines.begin(); i != lines.end(); i++)
    {
      writeToConsole((*i), "K_STD");
    }
  }
}

///////////////////////////////////////
//
//  io_internet::ParseLine
//
///////////////////////////////////////
void io_internet::parseLine( TQString line )
{
  int i, j;
  TQString tmp;

  switch(parseMode)
  {
    case NORMAL_MODE: /* determine which mode we should go into */
      if(line.contains(TQRegExp("^\\s*\\d{1,3}\\s+(?:\\d{1,4}|\\+\\+\\+\\+|\\-\\-\\-\\-)\\s+\\w{3,17}(\\(C\\))?\\s+\\d{1,3}\\s+\\d{1,3}")))
      {
        updateSoughtList(line);
        parseMode = UPDATE_SOUGHT_MODE;
      }
      /* CHALLENGE */
      else if( line.contains(TQRegExp("^\\s*Challenge: ")))
      {
        myResource->play( SND_CHALLENGE );
        if( challenge != NULL )
        {
          delete challenge;
        }
        challenge = new dlg_challenge( 0, "Challenge", myResource );
        connect( challenge, TQT_SIGNAL( destroyed() ), this, TQT_SLOT( nullifyChallenge() ) );
        connect( challenge, TQT_SIGNAL( user1Clicked() ), this, TQT_SLOT( acceptChallenge() ) );
        connect( challenge, TQT_SIGNAL( user2Clicked() ), this, TQT_SLOT( declineChallenge() ) );
        line.replace(TQRegExp("^\\s*Challenge: "), "");
        challenge->setValues( line, userName );
        parseMode = CHALLENGE_MODE;
      }
      else if( ( line.left(15) == "Challenge from " ) && ( line.right(9) == " removed." ) )
      {
        if( challenge != NULL )
        {
          delete challenge;
        }
      }
      /* SOUGHT GAME */
      else if(line.contains("seeking"))
      {
      //  writeToConsole("seeking", "K_CH");
      }
      /* PRIVATE TELL */
      else if(line.contains(TQRegExp(".+ tells you: .*")))
      {
        /* First grab the user name so we can auto-respond later */
        emit sendCMD( Command( 0, CMD_Set_Src_Tell, line.section(' ', 0, 0) ) );
        myResource->play( SND_TELL );
        writeToConsole(line, "K_PVT");
      }
      /* SAY */
      else if(line.contains( TQRegExp(".+ says: .*")))
      {
        myResource->play(SND_SAY);
        writeToConsole(line, "K_PVT");
        return;
      }
      /* WHISPER & KIBITZ */
      else if(line.contains(TQRegExp(".+ whispers: .*")) || line.contains(TQRegExp(".+ kibitzes: .*")))
      {
        writeToConsole(line, "K_WSP");
      }
      /* Important System Messages: Use Whisper Color */
      else if(line.contains(TQRegExp("declines the draw request\\.$" )))
      {
        writeToConsole(line, "K_WSP");
      }
      /* DRAW OFFER */
      else if(line.right(19) == " offers you a draw.")
      {
        writeToConsole(line, "K_WSP");
      }
      else if( line.contains( TQRegExp(".+rating adjustment:.+" ) ) )
      {
        writeToConsole(line, "K_WSP");
      }
      /* SHOUTS */
      else if( line.contains( TQRegExp("^c?t?s?-?shouts: ") ) )
      {
        writeToConsole(line, "K_SHT");
      }
      /* NOTIFY */
      else if((line.contains(TQRegExp("\\s*Notification:"))) ||
          (line.contains(TQRegExp("\\s*Present company includes:"))) ||
          (line.contains(TQRegExp("\\s*Your arrival was noted by:"))))
      {
        writeToConsole(line, "K_NOT");
        myResource->play( SND_NOTIFICATION );
      }
      /* CHANNEL TELLS */
      else if(line.contains(TQRegExp( ".\\(\\d+\\):" )))
      {
        /* First grab the channel # so we can auto-respond later */
        j = line.find(TQString("):"));
        i = line.findRev(TQString("("), j) + 1;
        emit sendCMD( Command( 0, CMD_Set_Src_Channel, line.mid(i, j - i) ) );
        writeToConsole(line, "K_CH");
      }
      else if(line.contains(TQRegExp("^<12>\\s")))
      {
        /* a game move */
        parseStyle12(line, PARSE12_MODE_MOVE);
      }
      else if(line.contains(TQRegExp("^<b1>\\s")))
      {
        /* a bughouse piece has been passed or a piece has been captured in crazyhouse */
        writeToConsole(line, "K_CH");
      }
      else if(line.contains(TQRegExp("^\\\\")))
      {
        writeToConsole(line, lastTag);
      }
      else if(line.contains(TQRegExp("^\\{?Game \\d+")) && line.contains("Creating", TRUE))
      {
        parseMode = NEW_GAME_MODE;
      }
      else if(line.contains("You are now observing game"))
      {
        parseMode = OBSERVE_GAME_MODE;
      }
      else if(line.startsWith("Movelist for game"))
      {
        TQRegExp gameNumber("\\d+");
        int pos = gameNumber.search(line);
        if(pos > -1)
        {
          ficsMoveListNumber = gameNumber.cap(0).toInt();
        }
        parseMode = PARSE_MOVE_LIST_MODE;
      }
      else if((line.contains(TQRegExp("^\\{?Game \\d+")) || line.contains(TQRegExp("Game \\d+"))) &&
               (
                 line.contains(" forfeits by disconnection", TRUE) ||
                 line.contains(" forfeits by disconnection}", TRUE) ||
                 line.contains(" forfeits on time} ", TRUE) ||
                 line.contains(" forfeits on time ", TRUE) ||
                 line.contains(" resigns} ", TRUE) ||
                 line.contains(" resigns ", TRUE) ||
                 line.contains(" Game drawn by mutual agreement ", TRUE) ||
                 line.contains(" Game drawn by mutual agreement} ", TRUE) ||
                 line.contains(", has lost contact or quit.", TRUE) ||
                 line.contains(" checkmated ", TRUE) ||
                 line.contains(" checkmated} ", TRUE) ||
                 line.contains("lost connection", TRUE) ||
                 line.contains("has no material to mate", TRUE)
               )
             )
      {
        sendEndOfGameCommand(line);
      }
      else
      {
        /* don't know what to do with it, just send it to the console */
        /* don't write the prompt to the console */
				if( line.contains( TQRegExp( "^a?d?f?g?s?z?ics% " ) ) ||
						line.contains( TQRegExp( "^cex% " ) ) ||
						line.contains( TQRegExp( "^chess% " ) ) )
						break;
        writeToConsole(line, "K_STD");
      }
      break;
    case UPDATE_SOUGHT_MODE:
      if(line.contains(TQRegExp("\\d+\\s+ads? displayed.")))
      {
        updateSoughtList(line);
        parseMode = NORMAL_MODE;
      }
      else
      {
        updateSoughtList(line);
      }
      break;
    case NEW_GAME_MODE:
      if(line.contains(TQRegExp("<12>\\s")))
      {
        /* a game move */
        parseStyle12(line, PARSE12_MODE_NEW);
      }
      else if((line.startsWith("fics%") && line.length() == 6))
      {
        parseMode = NORMAL_MODE;
      }
      break;
    case OBSERVE_GAME_MODE:
      if(line.contains(TQRegExp("<12>\\s")))
      {
        /* a game move */
        parseStyle12(line, PARSE12_MODE_NEW);
        send("moves");
        parseMode = NORMAL_MODE;
      }
      break;
    case CHALLENGE_MODE:
      if(line.startsWith("You can \"accept\" or \"decline\", or propose"))
      {
        parseMode = NORMAL_MODE;
      }
      break;
    case PARSE_MOVE_LIST_MODE:
      if(!line.contains("{Still in progress}"))
      {
        if(line.contains(TQRegExp("\\d\\.")))
        {
          parseMoveList(line);
        }
      }
      else
      {
        parseMode = NORMAL_MODE;
      }
      break;
    default: /* do nothing */
      break;
  };
}

///////////////////////////////////////
//
//	io_internet::ParseStyle12
//
///////////////////////////////////////
void io_internet::parseStyle12(TQString line, const unsigned int Mode)
{
//  kdWarning() << line << endl;

  struct ChessMove move;
  TQStringList fields;
  TQString position_line = "";
  match_param *param = NULL;
  Command::clearMove(&move);
  switch(Mode)
  {
    case PARSE12_MODE_NEW:
    {
      /* a new game that we are playing, or observing */
      param = new match_param(this->myResource);
      fields = TQStringList::split( TQChar(' '), line, FALSE );

      /* set white time control */
      TCPList tmpListWhite(param->time(WHITE));
      TCP tmpTCPWhite = tmpListWhite[0];
      tmpTCPWhite.Seconds = fields[20].toInt() * 60;
      tmpTCPWhite.Increment = fields[21].toInt();
      tmpListWhite[0] = tmpTCPWhite;
      param->setTime(WHITE, tmpListWhite);

      /* set black time control */
      TCPList tmpListBlack(param->time(BLACK));
      TCP tmpTCPBlack = tmpListBlack[0];
      tmpTCPBlack.Seconds = fields[20].toInt() * 60;
      tmpTCPBlack.Increment = fields[21].toInt();
      tmpListBlack[0] = tmpTCPBlack;
      param->setTime(BLACK, tmpListBlack);

      if((userName.upper() == fields[17].upper()) &&
         ((fields[19].toInt() == -1) || (fields[19].toInt() == 1)))
      {
        /* I am playing white */
        param->setType(WHITE, PLAYERLOCAL);
      }
      else if(fields[19].toInt() != 2)
      {
        /* I am not playing white */
        param->setType(WHITE, PLAYERTCP);
      }

      if((userName.upper() == fields[18].upper()) &&
         ((fields[19].toInt() == -1) || (fields[19].toInt() == 1)))
      {
        /* I am playing black */
        param->setType(BLACK, PLAYERLOCAL);
      }
      else if(fields[19].toInt() != 2)
      {
        /* I am not playing black */
        param->setType(BLACK, PLAYERTCP);
      }

      if(fields[19].toInt() == 2)
      {
        param->setType(WHITE, PLAYEREXAMINE);
        param->setType(BLACK, PLAYEREXAMINE);
      }
      //param->setVariation(something); figure out how to get the variation
      param->setName(WHITE, fields[17]);
      param->setName(BLACK, fields[18]);
      /* tell core to connect us to a new match */
      fics_to_knights[fields[16].toInt()] = ((core*)myParent)->newMatch(param)->getID();
    }
    case PARSE12_MODE_MOVE:
    {
      fields = TQStringList::split( TQChar(' '), line, FALSE );

      /* various ICS stuff for ChessMove */
      move.ICS_ClockTicking = fields[31].toInt();
      move.ICS_PawnPushFile = fields[10].toShort();
      move.ICS_MoveCounter = fields[15].toInt();
      if(fields[9] == "W")
      {
        move.ICS_OnMove = BLACK;
      }
      else if( fields[9] == "B" )
      {
        move.ICS_OnMove = WHITE;
      }

      switch( fields[19].toInt() )
      {
        /* Examining a game */
        case 2:
          move.ICS_Mode = ICS_Examine;
          if(fics_to_knights[fields[16].toInt()] == 0)
          {
            /* no new game started yet, call parsestyle 12 with a different mode */
            parseStyle12(line, PARSE12_MODE_NEW);
            return;
          }
          break;
        /* Observing a game */
        case -2:
        case 0:
          move.ICS_Mode = ICS_Observe;
          break;
        /* Playing a game */
        default:
          move.ICS_Mode = ICS_Normal;
          break;
      }

      /* Verbose Coordinate Notation of previous move ( USE AS CAN ) */
      strcpy(move.CAN, fields[27].right(fields[27].length() - 2).replace(TQRegExp("-"), "").utf8());
      /* SAN  */
      strcpy(move.SAN, fields[29].replace(TQRegExp("\\+"), "").replace(TQRegExp("#"), "").utf8());

      /* fill the line for the command */
      position_line += fields[1];  /* Internal Rank #7 */
      position_line += fields[2]; /* Internal Rank #6 */
      position_line += fields[3]; /* Internal Rank #5 */
      position_line += fields[4]; /* Internal Rank #4 */
      position_line += fields[5]; /* Internal Rank #3 */
      position_line += fields[6]; /* Internal Rank #2 */
      position_line += fields[7]; /* Internal Rank #1 */
      position_line += fields[8]; /* Internal Rank #0 */
      /* Can White Castle Short (boolean) */
      position_line += fields[11];
      /* Can White Castle Long (boolean) */
      position_line += fields[12];
      /* Can Black Castle Short (boolean) */
      position_line += fields[13];
      /* Can Black Castle Long (boolean) */
      position_line += fields[14];

      Command command(fics_to_knights[fields[16].toInt()], CMD_Move, fields[24].toInt() * 100,
                      fields[25].toInt() * 100, move);
      command.setData(position_line);
      emit sendCMD(command);
      break;
    }
    default:
      break;
  }
}

///////////////////////////////////////
//
//	io_internet::ParsePlayer
//
///////////////////////////////////////
//void io_internet::ParsePlayer( TQString Handle )
//{
//	player.Raw = Handle;
	/* SysAdmin */
//	if( Handle.contains( TQRegExp("\\(\\*\\)") ) )
//	{
//		player.SysAdmin = TRUE;
//		Handle.replace( TQRegExp("\\(\\*\\)"), TQString("") );
//	}
//	else player.SysAdmin = FALSE;
	/* ServiceRep */
//	if( Handle.contains( TQRegExp("\\(SR\\)") ) )
//	{
//		player.ServiceRep = TRUE;
//		Handle.replace( TQRegExp("\\(SR\\)"), TQString("") );
//	}
//	else player.ServiceRep = FALSE;
	/* Computer */
//	if( Handle.contains( TQRegExp("\\(C\\)") ) )
//	{
//		player.Computer = TRUE;
//		Handle.replace( TQRegExp("\\(C\\)"), TQString("") );
//	}
//	else player.Computer = FALSE;
	/* Unregistered */
//	if( Handle.contains( TQRegExp("\\(U\\)") ) )
//	{
//		player.Unregistered = TRUE;
//		Handle.replace( TQRegExp("\\(U\\)"), TQString("") );
//	}
//	else player.Unregistered = FALSE;
//	return;
//}
///////////////////////////////////////
//
//	io_internet::nullifyChallenge
//
///////////////////////////////////////
void io_internet::nullifyChallenge( void )
{
	challenge = NULL;
}

///////////////////////////////////////
//
//	io_internet::declineChallenge
//
///////////////////////////////////////
void io_internet::declineChallenge( void )
{
	send( "decline" );
	delete challenge;
	challenge = NULL;
}
///////////////////////////////////////
//
//	io_internet::acceptChallenge
//
///////////////////////////////////////
void io_internet::acceptChallenge( void )
{
  send( challenge->values() );
  delete challenge;
  challenge = NULL;
}

///////////////////////////////////////
//
//	io_internet::writeToConsole
//
///////////////////////////////////////
void io_internet::writeToConsole(TQString text, TQString tag)
{
  lastTag = tag;
  /* Remove Bells */
  text.replace( TQRegExp("\\x0007") , "" );
  /* Replace misc characters with rich-text friendly counterparts */
  text.replace( TQRegExp("\\x003c"), "&#60;" );
  text.replace( TQRegExp("\\x007c"), "&#124;" );
	text.replace( TQRegExp( "\\f"), "");
  text.replace( TQRegExp( "\\n"), "");
  text.replace( TQRegExp( "\\r*" ), "" );
  emit sendCMD( Command( 0, CMD_Append_To_Console, "<" + tag + ">" + text + "</" + tag + ">" ) );
}

///////////////////////////////////////
//
//	io_internet::updateSoughtList
//
///////////////////////////////////////
void io_internet::updateSoughtList(TQString soughtLine)
{
  /* "ADS DISPLAYED" MESSAGE */
  if(soughtLine.contains(TQRegExp("\\d+\\s+ads? displayed.")))
  {
		emit sendCMD( Command( 0, CMD_Show_Sought_List ) );
  }
  else
  {
		emit sendCMD( Command( 0, CMD_Add_Sought_Match, soughtLine ) );
  }
}

///////////////////////////////////////
//
//  io_internet::sendUserSettings()
//
///////////////////////////////////////
void io_internet::sendUserSettings()
{
  send("set style 12");
  send(TQString("set interface Knights %1").arg(_VERSION_));
  send(TQString("set private %1").arg(myResource->OPTION_Private));
  send(TQString("set kibitz %1").arg(myResource->OPTION_Kibitz));
  send(TQString("set tell %1").arg(myResource->OPTION_Tell));
  send(TQString("set shout %1").arg(myResource->OPTION_Shout));
  send(TQString("set seek %1").arg(myResource->OPTION_Seek));
  send(TQString("set tolerance %1").arg(myResource->OPTION_Profanity));
}

///////////////////////////////////////
//
//  io_internet::parseMoveList(TQString data)
//
///////////////////////////////////////
void io_internet::parseMoveList(TQString line)
{
  TQStringList two_plys;
  struct ChessMove move;
  Command command;
  int move_counter = 0;

  /* white */
  two_plys = TQStringList::split(TQRegExp("\\s+"), line, FALSE);
  Command::clearMove(&move);
  move_counter = two_plys[0].left(two_plys[0].length() - 1).length(); /* remove the . */
  move.ICS_MoveCounter = move_counter;
  move.ICS_Mode = ICS_Movelist;
  move.ICS_OnMove = WHITE;
  strcpy(move.SAN, two_plys[1].utf8());
  command.setCommand((int&)CMD_Move);
  command.setID(fics_to_knights[ficsMoveListNumber]);
  command.setMove(move);
  emit sendCMD(command);

  /* black */
  if(two_plys.size() > 3)
  {
    Command::clearMove(&move);
    move.ICS_MoveCounter = move_counter;
    move.ICS_Mode = ICS_Movelist;
    move.ICS_OnMove = BLACK;
    strcpy(move.SAN, two_plys[3].utf8());
    command.setCommand((int&)CMD_Move);
    command.setID(fics_to_knights[ficsMoveListNumber]);
    command.setMove(move);
    emit sendCMD(command);
  }
}

///////////////////////////////////////
//
//  io_internet::sendEndOfGameCommand(TQString line)
//
///////////////////////////////////////
void io_internet::sendEndOfGameCommand(TQString line)
{
  TQStringList fields;
  Command command;
  
  fields = TQStringList::split( TQChar(' '), line, FALSE );
  if(fields[1].endsWith(":"))
  {
    fields[1] = fields[1].left(fields[1].length() - 1);
  }
  command.setID(fics_to_knights[fields[1].toInt()]);
  fields[fields.count() - 1] = fields[fields.count() - 1].stripWhiteSpace();
  if(fields[fields.count() - 1] == "1-0" )
  {
    if(fields[fields.count() - 2].contains("resigns"))
    {
      command.setCommand((int&)CMD_Black_Resign);
    }
    else if(fields[fields.count() - 2].contains("time"))
    {
      command.setCommand((int&)CMD_White_Called_Flag);
    }
    else
    {
      command.setCommand((int&)CMD_Result_White);
    }
  }
  else if(fields[fields.count() - 1] == "0-1")
  {
    if(fields[fields.count() - 2].contains("resigns"))
    {
      command.setCommand((int&)CMD_White_Resign);
    }
    else if(fields[fields.count() - 2].contains("time"))
    {
      command.setCommand((int&)CMD_Black_Called_Flag);
    }
    else
    {
      command.setCommand((int&)CMD_Result_Black);
    }
  }
  else if(fields[fields.count() - 1] == "1/2-1/2")
  {
    command.setCommand((int&)CMD_Result_Draw);
  }
  else
  {
    command.setCommand((int&)CMD_Lost_Contact);
  }
  emit sendCMD(command);
}

///////////////////////////////////////
//
//  io_internet::processSeekTimer()
//
///////////////////////////////////////
void io_internet::processSeekTimer()
{
  /* timer timed out, send out a sought */
  send("sought");
}

///////////////////////////////////////
//
//  io_internet::selfDestruct()
//
///////////////////////////////////////
void io_internet::selfDestruct()
{
 	/* cause the io_internet to delete itself */
	TQApplication::postEvent( tqApp->mainWidget(), new TQCustomEvent( EVENT_Del_IO_Net ) );
}

///////////////////////////////////////
//
//  io_internet::setUserInfo()
//
///////////////////////////////////////
void io_internet::setUserInfo(TQString userName, TQString passWord)
{
	this->userName = userName;
	this->passWord = passWord;	
}

