/*
 * puzzle.cpp -- Copyright (C) 1998 by M. G"otze
 */

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

/*extern "C" {
#include <mediatool.h>
}*/

#include <qbitmap.h>
#include <qbrush.h>
#include <qcolor.h>
#include <qimage.h>
#include <qpaintd.h>
#include <qpainter.h>
#include <qpen.h>
#include <qpixmap.h>
#include <qstring.h>
#include <qwidget.h>
#include <qfont.h>
#include <qdatetime.h>
#include <qlist.h>
#include <qpoint.h>
#include <qcursor.h>
#include <qfileinfo.h>

#include <kapp.h>


#include <klocale.h>
#include <kglobal.h>
#include <kstddirs.h>
#include <kdebug.h>
#include <kfiledialog.h>
#include <kmessagebox.h>
#include <kconfig.h>
#include <kstatusbar.h>
//#include <kaudio.h>

#include <stdio.h>
#include "puzzle.h"
#include "score.h"
#include "cursors.h"

const char *dataFileExt = ".png";
const char *defImageFileName = "puzzle0.jpg";

KSlidePuzzle::KSlidePuzzle(QWidget *parent, const char *name,
  const tOpt opt) : QWidget(parent, name), state(0), options(opt)
{
	QString str;
   KConfig *config = kapp->config();
  config->setGroup("Options");
  if(config->readNumEntry("BackgroundColor",0)==0)
  {
		scn. frameFileName="frame0";
		scn.tilesFileName="tiles0";
	}
	else
  {
		scn. frameFileName="frame1";
		scn.tilesFileName="tiles1";
	}
	scn.frameSize=565;
	scn.puzOffset=5;
	scn. imgOffset=47;
	scn.borderColor=QColor(80, 32, 16);
	scn.fixCoeffs[0]= 1;
   scn.fixCoeffs[1]=0.625;
   scn.fixCoeffs[2]=16;

	score=0;
	nbmoves=0;
	imagepath=KGlobal::dirs()->findResourceDir("data","kslide/kslideui.rc");
	imagepath+="kslide/";
	maxlevel=0;
	displaytimerid=0;
	gametimerid=0;
	while(-1)
	{
		str=imagepath+"pics/puzzle";
		str.sprintf("%s%i.jpg",str.latin1(),maxlevel);
		if(fopen(str.latin1(),"r")!=NULL)
			maxlevel++;
		else
			break;
	}
  kdDebug()<<QString("Found %1 levels").arg(maxlevel)<<endl;
  srand(time(0));
  puz.prevImage = "";
  tempFileName = "";
	level=0;
	maxmoves=-1;
	currentmoves=-1;
	usetimer=-1;
	counterGivingHint_i=0;
	maxhints=-1;
	currenthints=0;
	gamelevel=3;
  puz.prevMousePos = QCursor::pos();
  puz.path = imagepath.latin1();
  puz.mode = 1;                // default mode is classical game with 4x4 tiles
  puz.size = puz.scrambling = 0;
  setFixedSize(scn.frameSize + 2 * scn.puzOffset, scn.frameSize +
    2 * scn.puzOffset);
  for (int i = 0; i <= 4; i++)
    puz.cursors[i] = new QCursor(QBitmap(32, 32, cursorsData[2*i], TRUE),
      QBitmap(32, 32, cursorsData[2*i+1], TRUE));
  puz.cursors[5] = (QCursor*) &arrowCursor;
  puz.cursors[6] = (QCursor*) &waitCursor;
  setCursor(5);
  puz.frame = new QPixmap(getDataFileName(fnFrame));
  QBitmap *mask = new QBitmap(getDataFileName(fnFrameMask));
  puz.frame->setMask(*mask);
  delete mask;
  for (tInd i = 0; i < maxTiles * maxTiles; i++)
    puz.tiles[i] = 0;
  ani.buf = 0;
/*  audio = new KAudio();
  if (audio->serverStatus()) {                 // if sound initalization failed
    delete audio;
    audio = 0;*/
    setOption(opSoundOn, FALSE);
//  }
  setMouseTracking(TRUE);
  setFont(QFont("courier", 16, 75));
	connect( this, SIGNAL(sigPuzzleSolved()), this, SLOT(changeLevel()));
	connect(this, SIGNAL(sigChangeLevel()), this, SLOT(slotChangeLevel()));
}

KSlidePuzzle::~KSlidePuzzle()
{
	killTimers();
  // only the first five cursor pointers have been allocated!
/*  for (int i = 0; i <= 4; i++)
    delete puz.cursors[i];
  for (tInd i = 0; i < maxTile(); i++)
    delete puz.tiles[i];
  delete tempFileName;
  delete puz.frame;*/
//  delete ani.buf;
//  delete audio;
}

void KSlidePuzzle::paintEvent(QPaintEvent*)
{
  bitBlt(this, scn.puzOffset, scn.puzOffset, puz.frame);
  drawPuzzle();
}

void KSlidePuzzle::drawPuzzle()
{
  for (tInd i = 0; i < maxTile(); i++)
    drawTile(i);
}

void KSlidePuzzle::timerEvent(QTimerEvent* e)
{
  QString display;
  if(e->timerId()==gametimerid)
  {
  	killTimer(gametimerid);
 		level--;
 		KMessageBox::sorry( this, "You have reached max game time\nTry again !", "Sorry");
 		order();
  	return;
  }
  if(e->timerId()==timerlevelid)
  {
  //	killTimer( timerlevelid );
   kdDebug()<<QString("e->timerId()==timerlevelid:emit sigChangeLevel")<<endl;
  //	emit(sigChangeLevel());
  }
  if(e->timerId()==displaytimerid)
  {
  	gametime.addSecs(30);
  	if((usetimer>0)&&(state==0))
  	{
  		display.setNum(usetimer-gametime.minute());
  		display+=i18n(" mins remaining");
  		statusbar->changeItem(display,3);
  		/*erase( width()-200, height()-35, 200,18);
  		drawText(width()-200, height()-20, display);*/
  	}
  	return;
  }
 if (state==stLoadingUncompleting) {
	  kdDebug()<<"In:LoadingUncompleting\n";
      state=stLoadingUncompleting;
      setState(stLoading, TRUE);
      timerid=startTimer(10);
  }  else if (state==stLoading) {
	  kdDebug()<<"In:Loading\n";
	  killTimer(timerid);
		for(int x=0;x<(int)(puz.size*puz.size);x++)
		{
			drawTile(x);
		}
      state=stMoving;
		setState(stInAction,FALSE);
      setCursor(5);
      puz.scrambling=false;
  }  else if ((e->timerId()==timerhintid)&&(isGivingHint())) {
  		if(currenthints==maxhints)
  		{
  			slotStopHint();
  			return;
  		}
  		currenthints++;
  		if(maxhints>-1)
  		{
	    	display.setNum(maxhints-currenthints);
	   	 	display+=i18n(" hints remaining");
   	 		statusbar->changeItem(display,2);
   	 	}
  		
  		QPoint p=tilelist.last();
  		tilelist.remove(tilelist.last());
  		tInd y=p.y();
		moveTile(y);
    if (tilelist.isEmpty()) {
      killTimer(timerid);
      killTimer(timerhintid);
      setState(stGivingHint, FALSE);
      if (solveCheck(FALSE))
        sigStoppedOrdering();
    }
  }  else  if (isUncompleting()) {
    tInd x, y;
    spiral(ani.delta--, x, y);
    bitBlt(this, ani.x + x, ani.y + y, ani.buf);
    if (ani.delta == ani.tVal) {
      puz.pos[puz.missingTile] = noTile;
      updateMovesTos();
      setState(stUncompleting | stComplete, FALSE);
      killTimer(timerid);
      if (puz.scrambling) {
        scramble(TRUE);
        timerid=startTimer(10);
      }
    }
  } else if (isMoving()) {
    if (ani.hor)
      bitBlt(this, ani.x + ani.delta, ani.y, ani.buf);
    else
      bitBlt(this, ani.x, ani.y + ani.delta, ani.buf);
    ani.delta += ani.step;
    if (ani.delta == ani.tVal) {
      killTimer(timerid);
      delete ani.buf;
      ani.buf = 0;
      updateMovesTos();
      setState(stMoving, FALSE);
      mouseMoveEvent(0);
      if (puz.scrambling)
        scramble(TRUE);
      else {
//        if (soundOn())
//          audio->play(QString(getFullSoundPath("sound1.wav")).data());
        solveCheck();
      }
    }
  } else if (isOrdering()) {
    tInd i, j;
    for (i = 0; i < maxTile(); i++) {
      if (puz.pos[i] != fixTile && i != puz.missingTile && puz.pos[i] != i) {
        for (j = lastTile(); puz.pos[j] != i; j--);
        swap(puz.pos[i], puz.pos[j]);
        drawTile(i);
        drawTile(j);
        break;
      }
    }
    if (i == maxTile()) {
      killTimer(timerid);
      setState(stOrdering, FALSE);
      if (solveCheck(FALSE))
        sigStoppedOrdering();
    }
  } else if (isCompleting()) {
    tInd x, y;
    spiral(ani.delta++, x, y);
    bitBlt(this, ani.x + x, ani.y + y, ani.buf, x, y, ani.step, ani.step,
      CopyROP, TRUE);
    if (ani.delta == ani.tVal) {
      killTimer(timerid);
      puz.pos[puz.missingTile] = puz.missingTile;
      setState(stCompleting, FALSE);
      setState(stComplete, TRUE);
      setCursor(cuNormal);
		kdDebug()<<"In timerevent, isCompleting(): Emit sigPuzzleSolved\n";
		emit(sigPuzzleSolved());
    }
  }
  if((maxmoves>0)&&(state==0))
  {
    	display.setNum(currentmoves);
    	display+=i18n(" moves remaining");
    	statusbar->changeItem(display,2);
  }
}

void KSlidePuzzle::mousePressEvent(QMouseEvent *event)
{
  if (isGivingHint() || isComplete() || event->button() != LeftButton)
    return;
  tInd x = (event->x() - scn.imgOffset) / tileSize();
  tInd y = (event->y() - scn.imgOffset) / tileSize();
  moveTile(coords2Index(x, y));
}

void KSlidePuzzle::mouseMoveEvent(QMouseEvent *event)
{
  if (event)  // event is 0 if called manually
    puz.prevMousePos = event->pos();
  if (isComplete() || isGivingHint() || !isIdle())
    return;
  unsigned char c = cuNormal;
  int x, y;
  if (event) {
    x = event->x() - scn.puzOffset - scn.imgOffset;
    y = event->y() - scn.puzOffset - scn.imgOffset;

  } else {
    x = puz.prevMousePos.x() - scn.puzOffset - scn.imgOffset;
    y = puz.prevMousePos.y() - scn.puzOffset - scn.imgOffset;
  }
  tInd ox = x / tileSize();
  tInd oy = y / tileSize();
  if (x >= 0 && y >= 0 && ox < puz.size && oy < puz.size) {
    c = cuNop;
    tInd z;
    if ((z = puz.movesTo[coords2Index(ox, oy)]) != noTile) {
      tInd nx, ny;
      index2Coords(z, nx, ny);
      if (abs(ox - nx) + abs(oy - ny) == 1)
        c = oy == ny ? (nx > ox ? cuRight : cuLeft) : (ny > oy ? cuDown : cuUp);
    }
  }
  if (c == puz.curCur)
    return;
  setCursor(c);
}

void KSlidePuzzle::scramble()
{
  if (!puz.scrambling)
  {
    tilelist.clear();
    emit(enableMenus(true));
    scramble((int)((gamelevel==1 ? 0.25 : gamelevel-1) * (puz.size+1) * (puz.size+1)));
  }
  else
    scramble(0);
}

void KSlidePuzzle::order()
{
  if (!isIdle())
    return;
  setState(stOrdering, TRUE);
  setCursor(cuWait);
  sigStartedOrdering();
  killTimer( gametimerid );
  timerid=startTimer(50);
}

bool KSlidePuzzle::loadImage(tOpt mode, QString fileName)
{
  if (!isIdle())
    return FALSE;
kdDebug ()<<QString("Loading Image:%1").arg(fileName)<<endl;
  if (!fileName.length()) {
    if (!puz.prevImage.length() || (mode & moLoadDefImage) == moLoadDefImage)
      fileName = getFullPicPath(defImageFileName);
    else
      fileName = puz.prevImage;
  }
  mode = (mode & moKeepMode) == moKeepMode ? puz.mode : mode & 0x0f;
  tInd i, size = mode < 3 ? mode+3 : 6;
  // let's preliminarily keep the original tiles...
  QPixmap *oldTiles[maxTiles * maxTiles];
  for (i = 0; i < maxTile(); i++)
    oldTiles[i] = puz.tiles[i];
  // load image, tile borders, tile border masks
  QPixmap *image = new QPixmap(fileName);
  QPixmap *tBorder = new QPixmap(getDataFileName(fnTiles));
  QBitmap *mask = new QBitmap(getDataFileName(fnTilesMask));
  if(image->size() == QSize(imageSize, imageSize) &&
    tBorder->size() == QSize(456, 160) && mask->size() == QSize(456, 160))
  {
    tBorder->setMask(*mask);
    QBitmap *tMask = new QBitmap(tileSize(size), tileSize(size));
    // calculate horizontal offset of the required tile border
    tInd left = 0;
    for (i = 3; i < size; i++)
      left += tileSize(i);
    bitBlt(tMask, 0, 0, mask, left, 0, tileSize(size), tileSize(size));
    // create tile mask from tile border mask
    QPainter *paint = new QPainter(tMask);
    paint->fillRect(2, 2, tileSize(size)-3, tileSize(size)-3, QBrush(color1));
    delete paint;
    // split image into tiles, glue a tile border to each and define a mask
    for (i = 0; i < size * size; i++) {
      puz.tiles[i] = new QPixmap(tileSize(size), tileSize(size));
      bitBlt(puz.tiles[i], 0, 0, image, (i % size) * tileSize(size),
        (i / size) * tileSize(size), tileSize(size), tileSize(size));
      if (isFixTile(i, mode)) {
        QImage *img = new
          QImage(puz.tiles[i]->convertToImage().convertDepth(8));
        for (int j = 0; j < img->numColors(); j++)
          img->setColor(j, qRgb(int((qGray(img->color(j)) * scn.fixCoeffs[0])),
            int((qGray(img->color(j)) * scn.fixCoeffs[1])),
            int((qGray(img->color(j)) * scn.fixCoeffs[2]))));
        puz.tiles[i]->convertFromImage(*img);
        delete img;
        puz.pos[i] = fixTile;
      } else {
        bitBlt(puz.tiles[i], 0, 0, tBorder, left, 0, tileSize(size),
          tileSize(size));
        puz.tiles[i]->setMask(*tMask);
        puz.pos[i] = i;
      }
    }
    tInd x, y;
    paint = new QPainter();
    if (mode > 3) {  // mode with fixed tiles?
      for (i = 0; i < size * size; i++) {
        if (puz.pos[i] == fixTile) {
          index2Coords(i, x, y);
          paint->begin(puz.tiles[i]);
          paint->setPen(QPen(scn.borderColor, 1));
          if (x > 0 && puz.pos[i-1] != fixTile)            // left border line?
            paint->drawLine(0, 0, 0, tileSize(size));
          if (x < 5 && puz.pos[i+1] != fixTile)                       // right?
            paint->drawLine(tileSize(size)-1, 0, tileSize(size)-1,
              tileSize(size));
          if (y > 0 && puz.pos[i-6] != fixTile)                         // top?

            paint->drawLine(0, 0, tileSize(size), 0);
          if (y < 5 && puz.pos[i+6] != fixTile)                      // bottom?
            paint->drawLine(0, tileSize(size)-1, tileSize(size),
              tileSize(size)-1);
          paint->end();
        }
      }
    }
    delete paint;
    delete mask;
    delete tBorder;
    delete image;
    // delete possibly previously allocated tiles
    for (i = 0; i < maxTile(); i++)
      delete oldTiles[i];
    puz.prevImage = fileName;
    puz.mode = mode;
    puz.size = size;
    setState(stComplete, TRUE);
    drawPuzzle();
    sigStoppedOrdering();
    return TRUE;
  }
  // something's gone wrong, so let's clean up...
  delete mask;
  delete tBorder;
  delete image;
  for (i = 0; i < maxTile(); i++)
    puz.tiles[i] = oldTiles[i];
  return FALSE;
}

unsigned int KSlidePuzzle::movesTo(const tInd index)
{
  tInd target = noTile;
  if (puz.pos[index] < noTile && index < maxTile()) {
    tInd x, y;
    index2Coords(index, x, y);
    if (y > 0 && puz.pos[coords2Index(x, y-1)] == noTile)
      target = coords2Index(x, y-1);                                 // upwards
    if (y < puz.size-1 && puz.pos[coords2Index(x, y+1)] == noTile)
      target = coords2Index(x, y+1);                               // downwards
    if (x > 0 && puz.pos[index-1] == noTile)                // towards the left
      target = index-1;
    if (x < puz.size-1 && puz.pos[index+1] == noTile)      // towards the right
      target = index+1;

  }
  return target;
}

void KSlidePuzzle::updateMovesTos()
{
  for (tInd i = 0; i < maxTile(); i++)
    puz.movesTo[i] = movesTo(i);
}

void KSlidePuzzle::drawTile(const tInd index)
{
  tInd x, y;
  index2Coords(index, x, y);
  if (puz.tiles[puz.pos[index]] && puz.pos[index] < noTile) {
    bitBlt(this, tileSize() * x + scn.imgOffset, tileSize() * y +
      scn.imgOffset, puz.tiles[puz.pos[index]]);
  } else if (puz.pos[index] == fixTile) {
    bitBlt(this, tileSize() * x + scn.imgOffset, tileSize() * y +
      scn.imgOffset, puz.tiles[index]);
  } else if (puz.pos[index] == noTile) {
    QPixmap *tile = new QPixmap(tileSize(), tileSize());
    tile->fill(backgroundColor());
    bitBlt(this, tileSize() * x + scn.imgOffset, tileSize() * y +
      scn.imgOffset, tile);
    delete tile;
  }
}

void KSlidePuzzle::moveTile(const tInd index)
{
  if (getState(stInAction) || index > lastTile())
    return;
  tInd target = puz.movesTo[index];
  if (target == noTile)
    return;
  if ((!puz.scrambling)&&(!isOrdering()))
  {
  	currentmoves--;
  	nbmoves++;
  }
  if(checkMaxMoves())
  	return;
  setState(stMoving, TRUE);
  setCursor(cuWait);
  tInd x, y;
  index2Coords(index, x, y);
  ani.x = x * tileSize() + scn.imgOffset;
  ani.y = y * tileSize() + scn.imgOffset;
  ani.step = 8;
  if (target < index)
    ani.step = -ani.step;
  ani.hor = abs(target - index) == 1;
  ani.delta = ani.step < 0 ? ani.step : 0;
  ani.buf = new QPixmap(tileSize() + abs(ani.step) * ani.hor, tileSize() +
    abs(ani.step) * !ani.hor);
  ani.buf->fill(backgroundColor());
  ani.tVal = ani.step < 0 ? ani.step - tileSize() : tileSize();
  int i = ani.step >= 0 ? ani.step : 0;
  bitBlt(ani.buf, i * ani.hor, i * !ani.hor, puz.tiles[puz.pos[index]]);
  ani.buf->setOptimization(QPixmap::BestOptim);
  swap(puz.pos[index], puz.pos[target]);
  timerid=startTimer(10);
}

void KSlidePuzzle::scramble(const tInd moves)
{
  if (!moves) {
    if (puz.scrambling)
      sigStoppedScrambling();
    puz.scrambling = 0;
    return;
  };

  if (getState(stInAction))
    return;

  if (!puz.scrambling) {
    puz.scrambling = moves;
    puz.prevScramble = noTile;
    setState(stOrdering, false);
    sigStartedScrambling();
    if (isComplete()) {
      setState(stUncompleting, true);
      setCursor(cuWait);
      chooseMissingTile();
      tInd x, y;
      index2Coords(puz.missingTile, x, y);
      ani.x = tileSize() * x + scn.imgOffset;
      ani.y = tileSize() * y + scn.imgOffset;

      ani.delta = 15;
      ani.tVal = -1;
      ani.buf = new QPixmap(tileSize()/4, tileSize()/4);
      ani.buf->fill(backgroundColor());
      timerid=startTimer(25);
      return;
    }
  }
  tInd moveable[4], targets[4];
  tInd i, j, k = 0;
  for (i = 0; i < maxTile(); i++)
    if ((j = puz.movesTo[i]) != noTile) {
      moveable[k] = i;
      targets[k++] = j;
    }
  for (i = rand() % k; moveable[i] == puz.prevScramble; i = rand() % k);
  puz.prevScramble = targets[i];
  moveTile(moveable[i]);
  if (!--puz.scrambling)
  {
    sigStoppedScrambling();
	if(usetimer>0)
	{
		gametimerid=startTimer(usetimer*60000);
		displaytimerid=startTimer(60000);
		gametime.setHMS(0,0,0);
	}
}
}

bool KSlidePuzzle::solveCheck(const bool honor)
{
  if (!isIdle()) return false;
  tInd i;
  for (i = 0; i < maxTile(); i++)
    if (puz.pos[i] != fixTile && i != puz.missingTile && puz.pos[i] != i)
      return false;
  tInd x, y;
  index2Coords(puz.missingTile, x, y);
  ani.x = tileSize() * x + scn.imgOffset;
  ani.y = tileSize() * y + scn.imgOffset;
  ani.delta = 0;
  ani.step = tileSize()/4;
  ani.tVal = 16;
  ani.buf = puz.tiles[puz.missingTile];
  timerid=startTimer(25);
//  if (soundOn() && honor)
//    audio->play(QString(getFullSoundPath("sound0.wav")).data());
  setState(stCompleting | stComplete, TRUE);
  setCursor(cuWait);
  killTimer(gametimerid);
  if (honor)
  {
    kdDebug()<<QString("In solveCheck: emit sigPuzzleSolved")<<endl;
    emit(sigPuzzleSolved());
  }
  return true;
}

void KSlidePuzzle::spiral(const tInd index, tInd &x, tInd &y)
{
  static const unsigned char coords[16][2] = {
    {0, 0}, {1, 0}, {2, 0}, {3, 0}, {3, 1}, {3, 2}, {3, 3}, {2, 3},
    {1, 3}, {0, 3}, {0, 2}, {0, 1}, {1, 1}, {2, 1}, {2, 2}, {1, 2}
  };
  tInd step = tileSize() / 4;
  x = coords[index][0] * step;
  y = coords[index][1] * step;
}

bool KSlidePuzzle::isFixTile(const tInd index, const tInd mode)
{
  tInd x, y;
  index2Coords(6, index, x, y);
  switch (mode) {
    case 4:                     // puzzle-piece-like moveable range ("corners")
      return ((y == 2 || y == 3) && (x == 0 || x == 5)) ||
        ((y == 0 || y == 5) && (x == 2 || x == 3));
    case 5:                                                          // a cross
      return (x < 2 || x > 3) && (y < 2 || y > 3);
    case 6:                           // diamond-shaped range of moveable tiles
      return ((y < 2 || y > 3) && (x == 0 || x == 5)) ||
        ((y == 0 || y == 5) && (x == 1 || x == 4));
    case 7:
      return (x == 1 || x == 4) && (y == 1 || y == 4);               // "isles"
    case 8:                                                         // triangle
      return x + y > 6;
  }
  return FALSE;
}

bool KSlidePuzzle::isCorner(const tInd index)
{
  if (index > lastTile() || puz.pos[index] >= noTile)
    return FALSE;
  tInd x, y;
  index2Coords(index, x, y);
  return ((x == 0 || x == puz.size-1) ||
    (x > 0 && puz.pos[index-1] == fixTile) ||
    (x < puz.size-1 && puz.pos[index+1] == fixTile)) &&
    ((y == 0 || y == puz.size-1) ||
    (y > 0 && puz.pos[index-puz.size] == fixTile) ||
    (y < puz.size-1 && puz.pos[index+puz.size] == fixTile));
}

void KSlidePuzzle::chooseMissingTile()
{
  if (randomMissingTile())
    for (puz.missingTile = noTile; !isCorner(puz.missingTile);
      puz.missingTile = rand() % (puz.size * puz.size));
  else
    for (puz.missingTile = lastTile(); !isCorner(puz.missingTile);
      puz.missingTile--);
}

bool KSlidePuzzle::toggleSound()
{
//  setOption(opSoundOn, bool(audio) && !soundOn());
  return soundOn();
}

bool KSlidePuzzle::toggleRandomMissingTile()
{
  setOption(opRandomMissingTile, !randomMissingTile());
  return randomMissingTile();
}

QString KSlidePuzzle::getFullPicPath(const char *file)
{
  tempFileName = (QString(puz.path) + "/pics/" + file);
  kdDebug()<<QString("getFullPicPath=%1").arg(tempFileName)<<endl;
  return tempFileName;
}

const char* KSlidePuzzle::getFullSoundPath(const char *file)
{
  delete tempFileName;
  return tempFileName = (QString(puz.path) + "/sounds/" + file).latin1();
}

const char* KSlidePuzzle::getDataFileName(const tOpt file)
{
  switch (file) {
    case fnFrame:
      return getFullPicPath(QString(scn.frameFileName) + dataFileExt);
    case fnFrameMask:
      return getFullPicPath(QString(scn.frameFileName) + 'm' + dataFileExt);
    case fnTiles:
      return getFullPicPath(QString(scn.tilesFileName) + dataFileExt);
    case fnTilesMask:
      return getFullPicPath(QString(scn.tilesFileName) + 'm' + dataFileExt);
    default:
      return 0;
  }
}

/** Save current game state */
void KSlidePuzzle::slotSaveGame(){
	QString saveGameFile;
	FILE* stream;

	saveGameFile=KFileDialog::getSaveFileName("", "*.sav" );
	if(saveGameFile.isEmpty())
		return;
	
	stream=fopen((const char*)saveGameFile, "w");

	fprintf( stream, "%i ",puz.missingTile );
	fprintf( stream, "%i ",puz.size );
	fprintf( stream, "%i ",level );
	fprintf( stream, "%i ",currentmoves );
	fprintf( stream, "%i ",maxmoves );
	fprintf( stream, "%i ",score );

    tInd i;
    for (i = 0; i < puz.size*puz.size; i++) {
/*      if (puz.pos[i] != fixTile && i != puz.missingTile && puz.pos[i] != i) {
        for (j = lastTile(); puz.pos[j] != i; j--);
        fprintf(stream, "%i %i ",puz.pos[i], puz.pos[j]);
      }*/
		if(puz.tiles[i] && puz.pos[i] != fixTile /*&& i != puz.missingTile*/ )
		{
			fprintf(stream, "%i %i ",puz.pos[i], puz.movesTo[i]);		
		}
    }
    fprintf(stream, "%i ",tilelist.count());
    for(i=0;i<tilelist.count();i++) {
    	QPoint p=*tilelist.at(i);
    	fprintf(stream, "%i %i", p.x(), p.y());
	}
	fclose(stream);
}

/** Load a game... */
void KSlidePuzzle::slotLoadGame(){
  if (!isIdle())
    return;

	QString loadGameFile;
	FILE* stream;
	tInd x,y;

	loadGameFile=KFileDialog::getOpenFileName("", "*.sav" );
	if(loadGameFile.isEmpty())
		return;
	
	stream=fopen((const char*)loadGameFile, "r");

	fscanf( stream, "%i", &puz.missingTile );
	fscanf( stream, "%i", &puz.size );
	fscanf( stream, "%i", &level );
	fscanf( stream, "%i", &currentmoves );
	fscanf( stream, "%i", &maxmoves );
	fscanf( stream, "%i", &score );
	
	QString imagename;
	imagename=imagename.sprintf("pics/puzzle%i.jpg",level);
	imagename=imagepath+imagename;
	loadImage(moKeepMode, imagename.data());

    tInd i, j;
    for (i = 0; i < puz.size*puz.size/*maxTile()*maxTile()*/; i++) {
/*      if (puz.pos[i] != fixTile && i != puz.missingTile && puz.pos[i] != i) {
        for (j = lastTile(); puz.pos[j] != i; j--);
        fscanf(stream, "%i %i ",&puz.pos[i], &puz.pos[j]);
      }*/
		if(puz.tiles[i] && puz.pos[i] != fixTile /*&& i != puz.missingTile*/ )
			fscanf(stream, "%i %i ",&puz.pos[i], &puz.movesTo[i]);
    }
    fscanf(stream, "%i ",&j);
    tilelist.clear();
    for(i=0;i<j;i++)
    {
		fscanf(stream, "%i %i ",&x, &y);

		tilelist.append(QPoint(x,y));
	}

	fclose(stream);

  state=stLoadingUncompleting;
  setCursor(cuWait);
  index2Coords(puz.missingTile, x, y);
  ani.x = tileSize() * x + scn.imgOffset;
  ani.y = tileSize() * y + scn.imgOffset;
  ani.delta = 15;
  ani.tVal = -1;
  ani.buf = new QPixmap(tileSize()/4, tileSize()/4);
  ani.buf->fill(backgroundColor());
  emit enableMenus(true);
  timerid=startTimer(25);
  return;
}

/** Start to order, giving a hint to the gamer */
void KSlidePuzzle::slotGiveHint(){
  if (!isIdle())
    return;
  setState(stGivingHint, TRUE);
//  setState(stOrdering, TRUE);
  setCursor(cuWait);
  emit(toggleHint(false));
//  sigStartedOrdering();
 counterGivingHint_j=lastTile();
  timerhintid=startTimer(1000);
}

void KSlidePuzzle::changeLevel()
{
  if (!isIdle())
    return;
//  repaint();
  kdDebug()<<QString("In: changeLevel")<<endl;
//	timerlevelid=startTimer(1000);
  slotChangeLevel();
}

void KSlidePuzzle::slotChangeLevel()
{
	QString imagename;
	level++;
	if(nbmoves>0)
		score+=300 * (puz.size+1) * (puz.size+1)/nbmoves;
	nbmoves=0;
	currenthints=0;
	if(gametimerid!=0)
	{
		killTimer(gametimerid);
		gametimerid=0;
	}
	if(displaytimerid!=0)
	{
		killTimer(displaytimerid);
		gametimerid=0;
	}
	if(level==maxlevel)
	{
		KMessageBox::information(this, i18n("You won ! Congratulations !!!"));
    KConfig *config = kapp->config();
    config->setGroup("PlayersScores");
  	QStringList names=config->readListEntry("Players");
  	QValueList<int> scores=config->readIntListEntry("Scores");
  	QStringList::Iterator itp=names.begin();
  	QValueList<int>::Iterator its=scores.begin();
  	while(itp!=names.end())
  	{
  		if(*its<score)
  			break;
  		itp++;
  		its++;
  	}	
  	names.insert(itp, playername);
  	scores.insert(its, score);
  	if(names.count()>10)
  	{
  		names.remove(names.at(10));
  		scores.remove(scores.at(10));
  	}
  	config->writeEntry("Players",names);
  	config->writeEntry("Scores",scores);
  	config->sync();
  	CScore dlg;
  	dlg.exec();
  	level=0;
	}
//  puz.prevMousePos = QCursor::pos();
  setCursor(5);
  emit(enableMenus(false));
//  tilelist.clear();
		imagename=imagename.sprintf("pics/puzzle%i.jpg",level);
	imagename=imagepath+imagename;
	loadImage(moKeepMode, imagename.data());
//  for (tInd i = 0; i < puz.size * puz.size; i++)
//    puz.tiles[i] = 0;
//  puz.size = puz.scrambling = 0;
  puz.missingTile=0;
  killTimer( timerlevelid );
}

/** Change the image's border */
void KSlidePuzzle::setMask(int mask){
	QString str,str1;
	QString imagename;
	str="frame"+QString::number(mask);
	scn. frameFileName=str.latin1();
	str1="tiles"+QString::number(mask);
	scn.tilesFileName=str1.latin1();

		imagename=imagename.sprintf("pics/puzzle%i.jpg",level);
	imagename=imagepath+imagename;
	loadImage(moKeepMode, imagename.data());
//	return;
	delete puz.frame;
  puz.frame = new QPixmap(getDataFileName(fnFrame));
  QBitmap *maskf = new QBitmap(getDataFileName(fnFrameMask));
  puz.frame->setMask(*maskf);
  delete maskf;
  return;
/*  repaint();
  return;
      state=stLoadingUncompleting;
      setCursor(cuWait);
*/      tInd x, y;
      index2Coords(puz.missingTile, x, y);
      ani.x = tileSize() * x + scn.imgOffset;
      ani.y = tileSize() * y + scn.imgOffset;
      ani.delta = 15;
      ani.tVal = -1;
      ani.buf = new QPixmap(tileSize()/4, tileSize()/4);
      ani.buf->fill(backgroundColor());
//      timerid=startTimer(25);
}

/** Check if the player has reached the max move number */
bool KSlidePuzzle::checkMaxMoves(){
	if(currentmoves==0)
	{
		currentmoves=maxmoves;
		level--;
		KMessageBox::sorry( this, i18n("You have reached max move number\nTry again !"));
		order();
		return true;
	}
	return false;
}

/** set the max moves number to moves */
void KSlidePuzzle::setMaxMoves( int moves ){
	maxmoves=moves;
	currentmoves=maxmoves;	
}

/** set the max moves number to moves */
void KSlidePuzzle::setMaxHints( int hints ){
	maxhints=hints;
	currenthints=0;	
}

void KSlidePuzzle::setMaxGameTime( int mins )
{
  QString display;
  usetimer=mins;
  if((mins>0)&&(state==0))
  {
    gametimerid=startTimer(mins*60000);
    displaytimerid=startTimer(60000);
    display.setNum(mins);
    display+=i18n(" mins remaining");
    statusbar->changeItem(display,3);
    gametime.setHMS(0,0,0);
  }
}

/** No descriptions */
void KSlidePuzzle::slotStopHint(){
  setState(stGivingHint, false);
  setCursor(5);
  emit(toggleHint(true));
  killTimer(timerhintid);//=startTimer(25);
}

/*Set a pointer to the statusbar so as to change its displayed text*/
void KSlidePuzzle::setStatusBar(KStatusBar* sb) {
	statusbar=sb;
}

/** change the game level */
void KSlidePuzzle::setGameLevel(int level){
	gamelevel=level+1;
}

/** Displays the Hall Of Fame (hiscores)*/
void KSlidePuzzle::slotViewHallOfFame(){
  CScore dlg;
  dlg.exec();
}

/* Set Player's name */
void KSlidePuzzle::setPlayername(QString name)
{
  playername=name;
}

//#include "puzzle.moc.cpp"
/** Set the global border skin */
void KSlidePuzzle::setFrameImage(QString str){
  if(QFile::exists(str))
  {
    QFileInfo fi(str);
    scn.frameFileName=fi.fileName();
    puz.frame = new QPixmap(str);
  }
}

/** Set the tiles skin */
void KSlidePuzzle::setTiledImage(QString str){
  QFileInfo fi(str);
  scn.tilesFileName=fi.fileName();
}
