/* Class AbTop */

#include <qpopupmenu.h>
#include <qtimer.h>
#include <qclipboard.h>

#include <kaction.h>
#include <kapplication.h>
#include <kconfig.h>
#include <kdialogbase.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kmenubar.h>
#include <kstdaccel.h>
#include <kglobal.h>
#include <kstatusbar.h>
#include <kstdaction.h>
#include <kstdgameaction.h>
#include <kedittoolbar.h>
#include <kkeydialog.h>
#include <kdebug.h>

#include "AbTop.h"
#include "Board.h"
#include "BoardWidget.h"
#include "EvalDlgImpl.h"
#include "EvalScheme.h"
#include "Network.h"
#include "version.h"

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

// #define MYTRACE 1

AbTop::AbTop()
  :KMainWindow(0)
{
  timerState = noGame;

  myPort = Network::defaultPort;
  currentEvalScheme = 0;
  net = 0;

  actValue = 0;
  stop = false;
  editMode = false;
  spyLevel = 0;
  pastePossible = true;


  timer = new QTimer;
  connect( timer, SIGNAL(timeout()), this, SLOT(timerDone()) );

  board = new Board();
  setMoveNo(0);

  connect( board, SIGNAL(searchBreak()), this, SLOT(searchBreak()) );
  Q_CHECK_PTR(board);
  boardWidget = new BoardWidget(*board,this);  

#ifdef SPION
  spy = new Spy(*board);
#endif

  connect( boardWidget, SIGNAL(updateSpy(QString)),
	   this, SLOT(updateSpy(QString)) );

  setCentralWidget(boardWidget);
  boardWidget->show();

  // this creates the GUI
  setupActions();
  createGUI();
  setupStatusBar();
  setMinimumSize(200,300);


  // RMB context menu
  setupRMBMenu();
  connect( boardWidget, SIGNAL(rightButtonPressed(int,const QPoint&)),
	   this, SLOT(rightButtonPressed(int,const QPoint&)) );

  connect( boardWidget, SIGNAL(edited(int)),
	   this, SLOT(edited(int)) );

  connect( board, SIGNAL(updateBestMove(Move&,int)),
	   this, SLOT(updateBestMove(Move&,int)) );

  connect( boardWidget, SIGNAL(moveChoosen(Move&)), 
	   this, SLOT(moveChoosen(Move&)) );

  /* default */
  easy();
  play_red();
  showMoveLong = true;
  showSpy = false;
  renderBalls = true;
  
  updateStatus();
  updateActions();
}

AbTop::~AbTop()
{
  /* Unregister from other abalone processes */
  if (net)
    delete net;
  delete timer;
#ifdef SPION
  delete spy;
#endif
}


/**
 * Create all the actions...
 *
 * The GUI will be built in createGUI using a XML file
 *
 */

void AbTop::setupActions()
{
  KStdGameAction::gameNew( this, SLOT(newGame()), actionCollection() );
  KStdGameAction::quit( this, SLOT(quit()), actionCollection() );

  (void) new KAction( i18n("&Stop Search"), "stop", Key_S, this,
		      SLOT(stopSearch()), actionCollection(), "game_stop");

  (void) new KAction( i18n("Take &Back"), "back",
		      KStdAccel::key(KStdAccel::Prior), this,
		      SLOT(back()), actionCollection(), "game_back");

  (void) new KAction( i18n("&Forward"), "forward",
		      KStdAccel::key(KStdAccel::Next), this,
		      SLOT(forward()), actionCollection(), "game_forward");

  (void) new KAction( i18n("&Hint"), "hint", Key_H, this,
		      SLOT(suggestion()), actionCollection(), "game_hint");

  KStdAction::copy( this, SLOT(copy()), actionCollection());
  KStdAction::paste( this, SLOT(paste()), actionCollection());

  (void) new KAction( i18n("&Restore Position"),
		      KStdAccel::key(KStdAccel::Open), 
		      this, SLOT(restorePosition()), 
		      actionCollection(), "edit_restore" );

  (void) new KAction( i18n("&Save Position"),
		      KStdAccel::key(KStdAccel::Save), 
		      this, SLOT(savePosition()), 
		      actionCollection(), "edit_save" );

  KToggleAction *ta;

  ta = new KToggleAction( i18n("&Network Play"), "net", Key_N,
			  actionCollection(), "game_net");
  connect(ta, SIGNAL(toggled(bool)), this, SLOT(gameNetwork(bool)));

  ta = new KToggleAction( i18n("&Modify"), "edit",
			  CTRL+Key_Insert, 
			  actionCollection(), "edit_modify");
  connect(ta, SIGNAL(toggled(bool)), this, SLOT( editModify(bool)));   

  KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection());
  KStdAction::showToolbar(this, SLOT(toggleToolbar()), actionCollection());
  KStdAction::showStatusbar(this, SLOT(toggleStatusbar()), actionCollection());
  KStdAction::keyBindings(this, SLOT(configureKeys()), actionCollection());
  KStdAction::configureToolbars(this,SLOT(configureToolbars()), actionCollection());
  KStdAction::saveOptions( this, SLOT(writeConfig()), actionCollection());

  KStdAction::preferences( this, SLOT(configure()), actionCollection());

  ta =  new KToggleAction( i18n("&Move Slow"), 0,
			   actionCollection(), "option_moveSlow");
  connect(ta, SIGNAL(toggled(bool)), this, SLOT(optionMoveSlow(bool)));

  ta =  new KToggleAction( i18n("&Render Balls"), 0,
			   actionCollection(), "option_renderBalls");
  connect(ta, SIGNAL(toggled(bool)), this, SLOT(optionRenderBalls(bool)));

  ta =  new KToggleAction( i18n("&Spy"), 0,
			   actionCollection(), "option_showSpy");
  connect(ta, SIGNAL(toggled(bool)), this, SLOT(optionShowSpy(bool)));


  KRadioAction *ra;

  ra =  new KRadioAction( i18n("&Easy"), 0,
			  actionCollection(), "level_easy");
  ra->setExclusiveGroup( "Level" );
  ra->setChecked( false );
  connect(ra, SIGNAL(toggled(bool)), this, SLOT(levelEasy(bool)));

  ra =  new KRadioAction( i18n("&Normal"), 0,
			  actionCollection(), "level_normal");
  ra->setExclusiveGroup( "Level" );
  ra->setChecked( true );
  connect(ra, SIGNAL(toggled(bool)), this, SLOT(levelNormal(bool)));

  ra =  new KRadioAction( i18n("&Hard"), 0,
			  actionCollection(), "level_hard");
  ra->setExclusiveGroup( "Level" );
  ra->setChecked( false );
  connect(ra, SIGNAL(toggled(bool)), this, SLOT(levelNormal(bool)));

  ra =  new KRadioAction( i18n("&Challenge"), 0,
			  actionCollection(), "level_challange");
  ra->setExclusiveGroup( "Level" );
  ra->setChecked( false );
  connect(ra, SIGNAL(toggled(bool)), this, SLOT(levelChallange(bool)));

  ra =  new KRadioAction( i18n("&Red"), 0,
			  actionCollection(), "iplay_red");
  ra->setExclusiveGroup( "IPlay" );
  ra->setChecked( true );
  connect(ra, SIGNAL(toggled(bool)), this, SLOT(iplayRed(bool)));

  ra =  new KRadioAction( i18n("&Yellow"), 0,
			  actionCollection(), "iplay_yellow");
  ra->setExclusiveGroup( "IPlay" );
  ra->setChecked( false );
  connect(ra, SIGNAL(toggled(bool)), this, SLOT(iplayYellow(bool)));

  ra =  new KRadioAction( i18n("&Both"), 0,
			  actionCollection(), "iplay_both");
  ra->setExclusiveGroup( "IPlay" );
  ra->setChecked( false );
  connect(ra, SIGNAL(toggled(bool)), this, SLOT(iplayBoth(bool)));

  ra =  new KRadioAction( i18n("&None"), 0,
			  actionCollection(), "iplay_none");
  ra->setExclusiveGroup( "IPlay" );
  ra->setChecked( false );
  connect(ra, SIGNAL(toggled(bool)), this, SLOT(iplayNone(bool)));
}

void AbTop::toggleMenubar()
{
  if (menuBar()->isVisible())
    menuBar()->hide();
  else
    menuBar()->show();
}

void AbTop::toggleToolbar()
{
  QToolBar *bar = toolBar();
  if(bar->isVisible())
    bar->hide();
  else
    bar->show();
}

void AbTop::toggleStatusbar()
{
  if (statusBar()->isVisible())
    statusBar()->hide();
  else
    statusBar()->show();
}

void AbTop::configure()
{
  KDialogBase *dlg = new KDialogBase( 0, "ConfigureEvaluation", true,
				      i18n("Configure Evaluation"),
				      KDialogBase::Ok | KDialogBase::Cancel, 
				      KDialogBase::Ok, true);

  EvalDlgImpl *edlg = new EvalDlgImpl(dlg,board);
  dlg->setMainWidget(edlg);
  if (dlg->exec()) {
    *currentEvalScheme = *(edlg->evalScheme());
    board->setEvalScheme(currentEvalScheme);
  }
  delete edlg;
}



void AbTop::configureKeys()
{
  KKeyDialog::configureKeys(actionCollection(), xmlFile(), true, this);
}


void AbTop::configureToolbars()
{
  KEditToolbar *dlg = new KEditToolbar(guiFactory(),this);

  if (dlg->exec())
    createGUI();

  delete dlg;
}


void AbTop::setupRMBMenu()
{
  /* use XML description */
  rmbMenu = static_cast<QPopupMenu*> (factory()->container("rmbPopup",this));
}


/* Right Mouse button pressed in BoardWidget area */
void AbTop::rightButtonPressed(int /* field */, const QPoint& pos)
{
  rmbMenu->popup( pos );
}

/* Read config options
 *
 * menu must already be created!
 */
void AbTop::readConfig()
{
  kdDebug() << "Reading config..." << endl;

  KConfig* config = kapp->config();
  config->setGroup("Options");
	
  readOptions(config);

  /* Size, Statusbar/Menubar */
  QString entry;

  config->setGroup("Appearance");
  if (!(entry = config->readEntry("Geometry")).isNull()) {
    int x=215, y=280;    
    sscanf(entry.ascii(),"%dx%d",&x,&y);
    resize(x,y);
  }

  if (!(entry = config->readEntry("MenuBar")).isNull()) {
    if (entry == QString::fromLatin1("Enabled"))
      menuBar()->show();
    else {
      menuBar()->hide();

      KToggleAction *a = static_cast<KToggleAction*>
	(actionCollection()->action(KStdAction::stdName(KStdAction::ShowMenubar)));
      if (a) a->setChecked(false);
    }
  }

  if (!(entry = config->readEntry("StatusBar")).isNull()) {
    if (entry == QString::fromLatin1("Enabled"))
      statusBar()->show();
    else {
      statusBar()->hide();

      KToggleAction *a = static_cast<KToggleAction*>
	(actionCollection()->action(KStdAction::stdName(KStdAction::ShowStatusbar)));
      if (a) a->setChecked(false);
    }
  }

  if (!(entry = config->readEntry("ToolBar")).isNull()) {
    if (entry == QString::fromLatin1("Enabled"))
      toolBar("mainToolBar")->show();
    else {
      toolBar("mainToolBar")->hide();

      KToggleAction *a = static_cast<KToggleAction*>
	(actionCollection()->action(KStdAction::stdName(KStdAction::ShowToolbar)));
      if (a) a->setChecked(false);
    }
  }

  currentEvalScheme = new EvalScheme("Current");
  currentEvalScheme->read(config);
  board->setEvalScheme( currentEvalScheme );
}

void AbTop::readOptions(KConfig* config)
{	
  QString entry;
  if (!(entry = config->readEntry("Level")).isNull()) {
    if (entry == "Easy")  easy();
    else if (entry == "Normal") normal();
    else if (entry == "Hard") hard();
    else if (entry == "Challange") challange();
  }

  if (!(entry = config->readEntry("Computer")).isNull()) {
    if (entry == "Red") play_red();
    else if (entry == "Yellow") play_yellow();
    else if (entry == "Both") play_both();
    else if (entry == "None") play_none();
  }

  showMoveLong = false; // default
  if (!(entry = config->readEntry("MoveSlow")).isNull()) 
    showMoveLong = (entry == "Yes");
  ((KToggleAction*) actionCollection()->action("option_moveSlow"))
    ->setChecked( showMoveLong );

  renderBalls = true; // default  
  if (!(entry = config->readEntry("RenderBalls")).isNull()) 
    renderBalls = (entry == "Yes");
  boardWidget->renderBalls(renderBalls);
  ((KToggleAction*) actionCollection()->action("option_renderBalls"))
    ->setChecked( renderBalls );

  showSpy = true; // default
  if (!(entry = config->readEntry("ShowSpy")).isNull()) 
    showSpy = (entry == "Yes");
  board->updateSpy(showSpy);
  ((KToggleAction*) actionCollection()->action("option_showSpy"))
    ->setChecked( showSpy );
}

void AbTop::readProperties(KConfig *config)
{
  QString entry;
	
  readOptions(config);

  currentEvalScheme = new EvalScheme("Current");
  currentEvalScheme->read(config);
  board->setEvalScheme( currentEvalScheme );

	
  if (!(entry = config->readEntry("TimerState")).isNull())
    timerState = entry.toInt();
  if (timerState == noGame) return;

  if (!(entry = config->readEntry("GameStopped")).isNull())  
    stop = (entry == "Yes");

  int mNo = 0;
  if (!(entry = config->readEntry("Position")).isNull()) {
    mNo = board->setState(entry);
    boardWidget->updatePosition(true);
  }
  setMoveNo(mNo, true);

  show();
  playGame();
}

void AbTop::writeConfig()
{
  kdDebug() << "Writing config..." << endl;

  KConfig* config = kapp->config();
  config->setGroup("Options");

  writeOptions(config);

  QString entry;
  config->setGroup("Appearance");
  config->writeEntry("Geometry", entry.sprintf("%dx%d",width(),height()) );  
  config->writeEntry("MenuBar", menuBar()->isVisible() ? "Enabled": "Disabled");
  config->writeEntry("ToolBar", toolBar("mainToolBar")->isVisible() ? "Enabled": "Disabled");
  config->writeEntry("StatusBar", statusBar()->isVisible() ? "Enabled": "Disabled");
  if (currentEvalScheme)
    currentEvalScheme->save(config);
  config->sync();
}


void AbTop::writeOptions(KConfig *config)
{
  QString entry;

  if (((KRadioAction*) actionCollection()->action("level_easy"))
      ->isChecked()) {
    entry = "Easy";
  }
  else if (((KRadioAction*) actionCollection()->action("level_normal"))
	   ->isChecked()) {
    entry = "Normal";
  }
  else if (((KRadioAction*) actionCollection()->action("level_hard"))
	   ->isChecked()) {
    entry = "Hard";
  }
  else
    entry = "Challange";

  config->writeEntry("Level",entry);

  if (((KRadioAction*) actionCollection()->action("iplay_red"))
      ->isChecked()) {
    entry = "Red";
  }
  else if (((KRadioAction*) actionCollection()->action("iplay_yellow"))
	   ->isChecked()) {
    entry = "Yellow";
  }
  else if (((KRadioAction*) actionCollection()->action("iplay_both"))
	   ->isChecked()) {
    entry = "Both";
  }
  else
    entry = "None";

  config->writeEntry("Computer",entry);

  entry = showMoveLong ? "Yes" : "No";
  config->writeEntry("MoveSlow",entry);

  entry = renderBalls ? "Yes" : "No";
  config->writeEntry("RenderBalls",entry);
	
  entry = showSpy ? "Yes" : "No";
  config->writeEntry("ShowSpy",entry);
}

void AbTop::saveProperties(KConfig *config)
{
  QString entry;
  
  writeOptions(config);
  if (currentEvalScheme)
    currentEvalScheme->save(config);
	
  config->writeEntry("TimerState",entry.setNum(timerState));

  if (timerState == noGame) return;

  config->writeEntry("GameStopped", stop ? "Yes":"No");
  config->writeEntry("Position", board->getState(moveNo));
  config->sync();
}

void AbTop::closeEvent(QCloseEvent *)
{
  quit();
}

void AbTop::savePosition()
{
  KConfig* config = kapp->config();
  config->setGroup("SavedPosition");
  config->writeEntry("Position", board->getState(moveNo));
}

void AbTop::restorePosition()
{
  KConfig* config = kapp->config();
  config->setGroup("SavedPosition");
  QString  entry = config->readEntry("Position");
  
  timerState = notStarted;
  timer->stop();	
  board->begin(Board::color1);
  stop = false;
  setMoveNo( board->setState(entry), true );

  if (net)
    net->broadcast( board->getASCIIState( moveNo ).ascii() );

  boardWidget->updatePosition(true);

  playGame();
}

void AbTop::setupStatusBar()
{
  QString tmp;
  KStatusBar* status = statusBar();

  QString t = i18n("Press %1 for a new game")
    .arg( actionCollection()->action("game_new")->shortcut().seq(0).toString());
  statusLabel = new QLabel( t, status, "statusLabel" );
  status->addWidget(statusLabel,1,false);

  // PERMANENT: Moving side + move No.

  // validPixmap, only visible in Modify mode: is position valid ?
  warningPix = BarIcon( "warning" );
  okPix = BarIcon( "ok" );
  validLabel = new QLabel( "", status, "validLabel" );
  validLabel->setFixedSize( 18, statusLabel->sizeHint().height() );
  validLabel->setAlignment( AlignCenter );
  validLabel->hide();
  validShown = false;

  redBall = BarIcon( "redball" );
  yellowBall = BarIcon( "yellowball" );
  noBall = BarIcon( "noball" );
  ballLabel = new QLabel( "", status, "ballLabel" );
  ballLabel->setPixmap(noBall);
  ballLabel->setFixedSize( 18, statusLabel->sizeHint().height() );
  ballLabel->setAlignment( AlignCenter );
  status->addWidget(ballLabel, 0, true);

  moveLabel = new QLabel( i18n("Move %1").arg("--"), status, "moveLabel" );
  status->addWidget(moveLabel, 0, true);

#ifdef MYTRACE	
  /* Create a toolbar menu for debugging output level */
  KToolBar *tb = toolBar("mainToolBar");
  if (tb) {
    QPopupMenu* spyPopup = new QPopupMenu;
    spy0 = BarIcon( "spy0" );
    spy1 = BarIcon( "spy1" );
    spy2 = BarIcon( "spy2" );
    spy3 = BarIcon( "spy3" );
    spyPopup->insertItem(spy0, 0);
    spyPopup->insertItem(spy1, 1);
    spyPopup->insertItem(spy2, 2);
    spyPopup->insertItem(spy3, 3);	
    connect( spyPopup, SIGNAL(activated(int)), 
	     this, SLOT(setSpy(int)) );
    tb->insertButton(spy0, 30, spyPopup,
			    TRUE, i18n("Spy"));
  }
#endif

}



void AbTop::updateSpy(QString s)
{
  if (showSpy) {
    if (s.isEmpty()) {      
      updateStatus();
      //      statusBar()->clear();
    }
    else
      statusLabel->setText(s);
  }
}

void AbTop::updateBestMove(Move& m, int value)
{
  if (showSpy) {
    boardWidget->showMove(m,3);
    boardWidget->showMove(m,0,false);

    QString tmp;
    tmp.sprintf("%s : %+d", (const char*) m.name().utf8(), value-actValue);
    updateSpy(tmp);
    kapp->processEvents();
  }
}


void AbTop::updateStatus()
{
  QString tmp;
  bool showValid = false;

  if (!editMode && timerState == noGame) {
    tmp = i18n("Move %1").arg("--");
    ballLabel->setPixmap(noBall);
  }
  else {
    tmp = i18n("Move %1").arg(moveNo/2 + 1);
    ballLabel->setPixmap( (board->actColor() == Board::color1)
			  ? redBall : yellowBall);
  }
  moveLabel->setText(tmp);

  if (editMode) {
    tmp = QString("%1: %2 %3 - %4 %5")
      .arg( i18n("Edit") )
      .arg( i18n("Red") ).arg(boardWidget->getColor1Count())
      .arg( i18n("Yellow") ).arg(boardWidget->getColor2Count());
    validLabel->setPixmap( (board->validState() == Board::invalid) 
			   ? warningPix:okPix );
    showValid = true;
  }
  else if (timerState == noGame) {
    tmp = i18n("Press %1 for a new game")
      .arg( actionCollection()->action("game_new")->shortcut().toString());
  }
  else {
    if (timerState == gameOver) {
      tmp = (board->actColor() == Board::color2) ? 
	      i18n("Red won"):i18n("Yellow won");
      validLabel->setPixmap( warningPix );
      showValid = true;
    }
    else {
      tmp = QString("%1 - %2")
	.arg( (board->actColor() == Board::color1) ? 
	      i18n("Red"):i18n("Yellow") )
	.arg( iPlayNow() ?
	      i18n("I am thinking...") : i18n("It's your turn!") );
    }
  }
  statusLabel->setText(tmp);
  if (validShown != showValid) {
    if (showValid) {
      statusBar()->addWidget(validLabel);
      validLabel->show();
    }
    else {
      statusBar()->removeWidget(validLabel);
      validLabel->hide();
    }
    validShown = showValid;
  }
  statusBar()->clear();
  statusBar()->repaint();
}

void AbTop::edited(int vState)
{
  if (vState == Board::empty)
    timerState = noGame;

  updateStatus();
}

/* only <stop search>, <hint>, <take back> have to be updated */
void AbTop::updateActions()
{
  bool iPlay = iPlayNow();

  /* New && Copy always on */

  /* Paste */
  pastePossible = !iPlay;
  ((KAction*)actionCollection()->action("edit_paste"))->setEnabled(!iPlay);
  
  /* Edit */
  ((KAction*)actionCollection()->action("edit_modify"))->setEnabled(!iPlay);

  /* Stop search */
  ((KAction*)actionCollection()->action("game_stop"))->setEnabled(iPlay);

  /* Back */
  bool bBack = (editMode && moveNo>0) || 
    (board->movesStored() >=1 && !iPlay);
  ((KAction*)actionCollection()->action("game_back"))->setEnabled(bBack);
	
  /* Forward */
  bool bForward = editMode && moveNo<999;
  ((KAction*)actionCollection()->action("game_forward"))->setEnabled(bForward);

  /* Hint */
  bool bHint = !editMode && !iPlay && (haveHint().type != Move::none);
  ((KAction*)actionCollection()->action("game_hint"))->setEnabled(bHint);
}

/* let the program be responsive even in a long search... */
void AbTop::searchBreak()
{
  kapp->processEvents();
}


void AbTop::setSpy(int id )
{
  toolBar("mainToolBar")->setButtonPixmap(30, (id==0)?spy0:(id==1)?spy1:(id==2)?spy2:spy3 );
  spyLevel = id;
  board->setSpyLevel(spyLevel);
}

void AbTop::timerDone()
{
  int interval = 400;

  switch(timerState) {
  case noGame:
  case notStarted:
    return;
  case showMove:
  case showMove+2:
  case showSugg:
  case showSugg+2:
  case showSugg+4:
    boardWidget->showMove(actMove, 2);
    interval = 200;
    break;
  case showMove+1:
  case showMove+3:
  case showSugg+1:
  case showSugg+3:
    boardWidget->showMove(actMove, 3);
    break;
  case showSugg+5:
    interval = 800;
  case showMove+4:
    boardWidget->showMove(actMove, 4);
    break;
  case showMove+5:
    boardWidget->showMove(actMove, 0);
    timerState = moveShown;
    playGame();
    return;
  case showSugg+6:
    boardWidget->showMove(actMove, 0);
    timerState = notStarted;
    return;
  }
  timerState++;
  timer->start(interval,TRUE);
}

void AbTop::userMove()
{
    /* User has to move */
    static MoveList list;
    
    list.clear();
    board->generateMoves(list);

    if (list.getLength() == 0) {
      stop = true;
      timerState = gameOver;
      playGame();
    }
    else
      boardWidget->choseMove(&list);
}

bool AbTop::iPlayNow()
{
  if (editMode ||
      (board->validState() != Board::valid) ||
      timerState == gameOver)
    return false;

  int c = board->actColor();
    
  /* color1 is red */
  return ((iplay == cBoth) || 
	  ((c == Board::color1) && (iplay == cRed) ) ||
	  ((c == Board::color2) && (iplay == cYellow) ));
}

void AbTop::playGame()
{
  if (timerState == moveShown) {
    if (actMove.type != Move::none) {
      board->playMove(actMove);
      moveNo++; // actColor in board is changed in playMove

      if (net)
	net->broadcast( board->getASCIIState( moveNo ).ascii() );
    }
    actValue = - board->calcEvaluation();
    boardWidget->updatePosition(true);
    timerState = notStarted;
  }
  if (!board->isValid()) { 
    stop = true;
    timerState = gameOver;
  }

  updateStatus();
  updateActions();
  boardWidget->setCursor(crossCursor);
  if (stop) return;


  if (!iPlayNow()) {
    userMove();
    return;
  }
  boardWidget->setCursor(waitCursor);	
  kapp->processEvents();

  if (moveNo <4) {
    /* Chose a random move making the position better for actual color */

    /* If comparing ratings among color1/2 on move, we have to negate one */
    int v = -board->calcEvaluation(), vv;
    do {
      actMove = board->randomMove();
      board->playMove(actMove);
      vv = board->calcEvaluation();
      board->takeBack();
    } while( (board->actColor() == Board::color1) ? (vv<v) : (vv>v) );
  }
  else {
    actMove = (board->bestMove());

    if (actMove.type == Move::none) {
      stop = true;
      timerState = gameOver;
      playGame();
      return;
    }
  }

  timerState = showMoveLong ? showMove : showMove+3;
  timerDone();
}

void AbTop::moveChoosen(Move& m)
{
  actMove = m;
  timerState = moveShown;
  playGame();
}

void AbTop::newGame()
{
  /* stop a running animation */
  timerState = notStarted;
  timer->stop();	

  /* reset board */
  board->begin(Board::color1);
  boardWidget->updatePosition(true);
  setMoveNo(0, true);

  if (net)
    net->broadcast( board->getASCIIState( moveNo ).ascii() );

  /* if not in EditMode, start Game immediately */
  if (!editMode) {
    stop = false;	
    playGame();
  }
}

/* Copy ASCII representation into Clipboard */
void AbTop::copy()
{
  QClipboard *cb = QApplication::clipboard();
  cb->setText( board->getASCIIState( moveNo ).ascii() );
}

void AbTop::paste()
{
  if (!pastePossible) return;

  QClipboard *cb = QApplication::clipboard();
  pastePosition( cb->text().ascii() );
  /* don't do this in pastePosition: RECURSION !! */

  if (net)
    net->broadcast( board->getASCIIState( moveNo ).ascii() );
}

void AbTop::pastePosition(const char * text)
{
  if (!pastePossible) return;
  if ( text ) {
    timerState = notStarted;
    timer->stop();	
    board->begin(Board::color1);
    stop = false;

    int mNo = board->setASCIIState(text);
    if (mNo<0) mNo=0;
    setMoveNo( mNo, true);

    boardWidget->updatePosition(true);

    if ( (board->validState()==Board::invalid) && !editMode) {
      ((KToggleAction*)actionCollection()->action("edit_modify"))->setChecked(true);
      return;
    }

    playGame();
  }
}


void AbTop::gameNetwork(bool on)
{
  if (!on) {
    if (net != 0) {
      delete net;
      net = 0;
    }
    return;
  }

  if (myPort == 0) myPort = Network::defaultPort;
  net = new Network(myPort);
  char *h, h2[100];
  int p, i;
  for(h = hosts.first(); h!=0; h=hosts.next()) {
    for(i=0;h[i]!=0 && h[i]!=':';i++);
    if (h[i]==':')
      p = atoi(h+i+1);
    else
      p = 0;

    if (p == 0) p = Network::defaultPort;
    strncpy(h2,h,i);
    h2[i]=0;
    net->addListener(h2, p);
  }
  QObject::connect(net, SIGNAL(gotPosition(const char *)),
		   this, SLOT(pastePosition(const char *)) );
}
  

void AbTop::editModify(bool on)
{
  int vState = board->validState();

  editMode = boardWidget->setEditMode( on );
  if (vState != Board::valid)
    timerState = noGame;

  updateActions();
  updateStatus();
  if (!editMode && vState == Board::valid) {
    actMove.type = Move::none;
    timerState = moveShown;
    playGame();
  }  
}

void AbTop::stopGame()
{
  stop = true;
  board->stopSearch();
}

void AbTop::stopSearch()
{
  // When computer plays both, switch back to human for next color
  if (iplay == cBoth) {
    if (board->actColor() == Board::color1)
      play_red();
    else
      play_yellow();
  }
  board->stopSearch();
}

void AbTop::quit()
{
  board->stopSearch();
  kapp->quit();
}

void AbTop::continueGame()
{
  if (timerState != noGame && timerState != gameOver) {
    stop = false;
    if (timerState == notStarted)
	    playGame();
  }
}

/**
 * Reset the Move number of the actual game to <m>
 * If <update> is true, update GUI actions and redraw statusbar
 */
void AbTop::setMoveNo(int m, bool updateGUI)
{
  moveNo = m;

  board->setActColor( ((moveNo%2)==0) ? Board::color1 : Board::color2 );

  if (updateGUI) {
    updateStatus();
    updateActions();
  }
}  


/* "Back" action activated
 *
 * If in edit mode, simple go 1 back
 * If in a game, go back 2 if possible
 */
void AbTop::back()
{
  if (editMode) {
    if (moveNo > 0) 
      setMoveNo(moveNo-1, true);
    return;
  }

  if (moveNo < 1) return;

  if (timerState == gameOver)
    timerState = notStarted;
  if (timerState != notStarted) return;

  /* If possible, go 2 steps back */
  if (moveNo>0 && board->takeBack()) moveNo--;
  if (moveNo>0 && board->takeBack()) moveNo--;
  setMoveNo( moveNo, true );
  
  boardWidget->updatePosition(true);

  userMove();
}

/* Only for edit Mode */
void AbTop::forward()
{
  if (editMode) {
    if (moveNo < 999) 
      setMoveNo(moveNo+1, true);
    return;
  }
}

Move AbTop::haveHint()
{
  static Move m;
  static int oldMoveNo = 0;

  if (timerState != notStarted) {
    m.type = Move::none;
  }
  else if (moveNo != oldMoveNo) {
    MoveList list;
    
    oldMoveNo = moveNo;
    m = board->nextMove();
    board->generateMoves(list);
    if (!list.isElement(m,0))
      m.type = Move::none;
  }
  return m;
}


void AbTop::suggestion()
{
  if (timerState != notStarted) return;
  Move m = haveHint();
  if (m.type == Move::none) return;

  actMove = m;

  timerState = showSugg;
  timerDone();
}

void AbTop::setLevel(int d)
{
  depth = d;
  board->setDepth(depth);
  //  kdDebug() << "Level set to " << d << endl;
}

void AbTop::levelEasy(bool on)      { if (on) setLevel(2); }
void AbTop::levelNormal(bool on)    { if (on) setLevel(3); }
void AbTop::levelHard(bool on)      { if (on) setLevel(4); }
void AbTop::levelChallange(bool on) { if (on) setLevel(5); }

void AbTop::easy()
{  
  ((KRadioAction*) actionCollection()->action("level_easy"))
    ->setChecked( true );
  levelEasy(true);
}

void AbTop::normal()
{  
  ((KRadioAction*) actionCollection()->action("level_normal"))
    ->setChecked( true );
  levelNormal(true);
}

void AbTop::hard()
{  
  ((KRadioAction*) actionCollection()->action("level_hard"))
    ->setChecked( true );
  levelHard(true);
}

void AbTop::challange()
{  
  ((KRadioAction*) actionCollection()->action("level_challange"))
    ->setChecked( true );
  levelChallange(true);
}

void AbTop::setIplay(int v)
{
  iplay = v;
  continueGame();
}

void AbTop::iplayRed(bool on)    { if (on) setIplay(cRed); }
void AbTop::iplayYellow(bool on) { if (on) setIplay(cYellow); }
void AbTop::iplayBoth(bool on)   { if (on) setIplay(cBoth); }
void AbTop::iplayNone(bool on)   { if (on) setIplay(cNone); }

void AbTop::play_red()
{
  ((KRadioAction*) actionCollection()->action("iplay_red"))
    ->setChecked( true );
  iplayRed(true);
}

void AbTop::play_yellow()
{
  ((KRadioAction*) actionCollection()->action("iplay_yellow"))
    ->setChecked( true );
  iplayYellow(true);
}

void AbTop::play_both()
{
  ((KRadioAction*) actionCollection()->action("iplay_both"))
    ->setChecked( true );
  iplayBoth(true);
}

void AbTop::play_none()
{
  ((KRadioAction*) actionCollection()->action("iplay_none"))
    ->setChecked( true );
  iplayNone(true);

}

void AbTop::optionMoveSlow(bool on)
{
  showMoveLong = on;
}

void AbTop::optionRenderBalls(bool on)
{
  renderBalls = on;
  boardWidget->renderBalls(renderBalls);
}

void AbTop::optionShowSpy(bool on)
{
  showSpy = on;
  board->updateSpy(showSpy);

#ifdef SPION
  if (showSpy)
    spy->show();
  else {
    spy->nextStep();
    spy->hide();
  }
#endif

}


void AbTop::help()
{
  kapp->invokeHelp();
}



#include "AbTop.moc"
