window.onload = init;

//var pieces;
//var finalPieceIndex;

//var pieceSize = 136; // pixels
//var gutterWidth = 8; // pixels
//var puzzleBorder = 20; // pixels
//var slideStepTime = 50; // milliseconds
//var slideStepDistance = pieceAndGutterWidth / 6;

//function scrambleHook() {}
//function solveHook() {}

var pieceAndGutterWidth;
var finalPiece;

var puzzleOffsetTop;
var puzzleOffsetLeft;

var puzzle;
var correctlyPlaced;

var slidingPiece;
var timeoutID;
var fromTop;
var fromLeft;
var toTop;
var toLeft;

function init() {
  puzzle = document.getElementById('puzzle');

  pieceAndGutterWidth = pieceSize + gutterWidth;

  puzzleOffsetTop = puzzleBorder;
  puzzleOffsetLeft = puzzleBorder;
  for (var parent = puzzle; parent; parent = parent.offsetParent) {
    puzzleOffsetTop += parent.offsetTop;
    puzzleOffsetLeft += parent.offsetLeft;
  }

  for (var r = 0; r < 3; r++) {
    for (var c = 0; c < 3; c++) {
      var index = r*3 + c;
      installPiece(pieces[index], r, c, puzzle);
    }
  }

  finalPiece = pieces[finalPieceIndex];
  pieces.splice(finalPieceIndex, 1);

  scramble();
}

function scramble() {

  if (window.scrambleHook) { scrambleHook(); }

  correctlyPlaced = 0;
  hideFinalPiece();

  var shuffleIndex = [0,1,2,3,4,5,6,7,-1];
  shuffle(shuffleIndex);
  if (!verifyShuffle(shuffleIndex)) {
    var i = 0;
    while (shuffleIndex[i] == -1) { i++; }
    var j = i+1;
    while (shuffleIndex[j] == -1) { j++; }
    arraySwap(shuffleIndex,i,j);
  }

  for (var r = 0; r < 3; r++) {
    var top = r * pieceAndGutterWidth + puzzleOffsetTop;
    for (var c = 0; c < 3; c++) {
      var left = c * pieceAndGutterWidth + puzzleOffsetLeft;
      var index = shuffleIndex.pop();
      if (index == -1) {
        toTop = top;
        toLeft = left;
      } else {
        placePiece(pieces[index], top, left);
      }
    }
  }

  setOnclicks();
}

function solve() {
  correctlyPlaced = 0;
  hideFinalPiece();

  for (var i = 0; i < 8; i++) {
    placePieceAtHome(pieces[i]);
  }

  doneSolving();

  return false; // Keeps the "solve" button from overreacting
}

function shuffle(a) {
  for (var i = a.length-1; i > 0; i -= 1) {
    var j = Math.floor(Math.random() * (i+1));
    arraySwap(a,i,j);
  }
}

function arraySwap(a,i,j) {
  var t = a[i];
  a[i] = a[j];
  a[j] = t;
}

function verifyShuffle(a) {
  var ok = true;
  for (var i = a.length-1; i > 0; i-= 1) {
    if (a[i] != -1) {
      for (var j = i-1; j >= 0; j -= 1) {
        if (a[j] != -1) {
          if (a[i] < a[j]) {
            ok = !ok;
          }
        }
      }
    }
  }
  return ok;
}

function installPiece(piece, homeTop, homeLeft, parent) {
  piece.homeTop = homeTop * pieceAndGutterWidth + puzzleOffsetTop;
  piece.homeLeft = homeLeft * pieceAndGutterWidth + puzzleOffsetLeft;
  piece.style.visibility = 'hidden';
  parent.appendChild(piece);
}

function hideFinalPiece() {
  finalPiece.style.visibility = 'hidden';
}

function showFinalPiece() {
  placePiece(finalPiece, finalPiece.homeTop, finalPiece.homeLeft);
}

function placePieceAtHome(piece) {
  placePiece(piece, piece.homeTop, piece.homeLeft);
}

function placePiece(piece, curTop, curLeft) {
  piece.style.position = 'absolute';
  piece.style.top = curTop + 'px';
  piece.style.left = curLeft + 'px';
  piece.isHome = (piece.homeTop == curTop && piece.homeLeft == curLeft);
  if (piece.isHome) { incrementCorrect(); }
  piece.style.visibility = 'visible';
}

function setOnclicks() {
  for (var i = 0; i < pieces.length; i++) {
    var piece = pieces[i];
    if (Math.abs(piece.offsetTop - toTop) +
        Math.abs(piece.offsetLeft - toLeft) == pieceAndGutterWidth) {
      piece.onclick = handleClick;
    }
  }
}

function clearOnclicks() {
  for (var i = 0; i < pieces.length; i++) {
    pieces[i].onclick = noop;
  }
}

function noop() {}

function handleClick() {
  clearOnclicks();
  slidingPiece = this;
  fromTop = this.offsetTop;
  fromLeft = this.offsetLeft;
  if (this.isHome) {
    this.isHome = false;
    decrementCorrect();
  }
  slidePiece();
}

function slidePiece() {
  var curTop = slidingPiece.offsetTop;
  var curLeft = slidingPiece.offsetLeft;
  if (toLeft < curLeft) {
    slidingPiece.style.left = (curLeft - slideStepDistance) + 'px';
  } else if (toLeft > curLeft) {
    slidingPiece.style.left = (curLeft + slideStepDistance) + 'px';
  } else if (toTop < curTop) {
    slidingPiece.style.top = (curTop - slideStepDistance) + 'px';
  } else if (toTop > curTop) {
    slidingPiece.style.top = (curTop + slideStepDistance) + 'px';
  }

  if (toTop == slidingPiece.offsetTop
      && toLeft == slidingPiece.offsetLeft) {
    doneSliding();
  } else {
    timeoutID = setTimeout('slidePiece()', slideStepTime);
  }
}

function doneSliding() {
  clearTimeout(timeoutID);
  if (toTop == slidingPiece.homeTop && toLeft == slidingPiece.homeLeft) {
    slidingPiece.isHome = true;
    if (incrementCorrect()) {
      doneSolving();
      return;
    }
  }
  toTop = fromTop;
  toLeft = fromLeft;
  setOnclicks();
}

function doneSolving() {
  showFinalPiece();
  clearOnclicks();
  if (window.solveHook) { solveHook(); }
}

function incrementCorrect() {
  correctlyPlaced++;
  return (correctlyPlaced == 8);
}

function decrementCorrect() {
  correctlyPlaced -= 1;
}
