function showEmail(email, name, shift) {
  var result = "";

  for(var index = 0; index < email.length; index++) {
    result += String.fromCharCode(email.charCodeAt(index) + shift);
  }

  document.write('<a href="mailto:' + result + '">' + name + '</a>');
}

// GLOBAL Variables
// robots & colors;
var empty = -1;
var red = 0;
var yellow = 1;
var green = 2;
var blue = 3;
var colors = new Array(5);
colors[0] = "red";
colors[1] = "yellow";
colors[2] = "green";
colors[3] = "blue";
colors[4] = "";
// sides
var left = 0;
var top = 1;
var up = 1;
var right = 2;
var bottom = 3;
var down = 3;
var directions = new Array(4);
directions[0] = "left";
directions[1] = "up";
directions[2] = "right";
directions[3] = "down";
//quadrants
var topright = 0;
var topleft = 1;
var bottomleft = 2;
var bottomright = 3;
//chips
var circle = 0;
var triangle = 1;
var square = 2;
var octagon = 3;
var shapes = new Array(5);
shapes[0] = "circle";
shapes[1] = "triangle";
shapes[2] = "square";
shapes[3] = "octagon";
shapes[4] = "whorl";
var start = 0;
var play = 1;
var finish = 2;
var charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz.0123456789";

function random(low, high) {
  spread = high - low + 1;
  return Math.floor(Math.random() * spread) + low;
}

function getShapeFromChip(chip) {
  return chip % 4;
}

function getColorFromChip(chip) {
  return Math.floor(chip / 4);
}

function getChip(shape, color) {
  return (color * 4) + shape;
}

// Class

function Cell(row, col) {
  this.row = row;
  this.col = col;
  this.robot = empty;
  this.walls = 0;
  this.chip = empty;

  this.addWall = Cell_AddWall;
  this.addChip = Cell_AddChip;
  this.addRobot = Cell_AddRobot;
  this.paint = Cell_Paint;
  this.addWhorl = Cell_AddWhorl;
}

function Cell_AddWhorl() {
  this.chip = 16;
}

function Cell_AddRobot(robot) {
  this.robot = robot;
}

function Cell_AddWall(wall) {
  this.walls |= (1 << wall);
}

function Cell_AddChip(chip) {
  this.chip = chip;
}

function Cell_Paint() {
  if((this.row == 8 || this.row == 9) && (this.col == 8 || this.col == 9)){
    return;
  }

  e = document.getElementById("r" + this.row + "c" + this.col);

  if(this.walls) {
    if(this.walls & (1 << left)) {
      e.style.borderLeft = "2px solid #000000";
    }
    if(this.walls & (1 << top)) {
      e.style.borderTop = "2px solid #000000";
    }
    if(this.walls & (1 << right)) {
      e.style.borderRight = "2px solid #000000";
    }
    if(this.walls & (1 << bottom)) {
      e.style.borderBottom = "2px solid #000000";
    }
  }

  if(this.chip != empty) {
    var shape, separator, color;
    if(this.chip == 16) {
      shape = 4;
      separator = '';
      color = 4;
    } else {
      shape = getShapeFromChip(this.chip);
      separator = '_';
      color = getColorFromChip(this.chip);
    }

    e.innerHTML = '<img src="/images/' + shapes[shape] + separator + colors[color] + '.gif" />';
  } else {
    e.innerHTML = '<img src="/images/cell.gif" />';
  }

  if(this.robot != empty) {
    e.innerHTML = '<img src="/images/robot_' + colors[this.robot] + '.gif" />';
  }
}

function Robot() {
  this.row;
  this.col;

  this.set = Robot_set;
}

function Robot_set(row, col) {
  this.row = row;
  this.col = col;
}

function Coordinate(row, col) {
  this.row = row;
  this.col = col;

  this.random = Coordinate_Random;
  this.bad = Coordinate_Bad;
}

function Coordinate_Bad() {
  if((this.row == 7 || this.row == 8) && (this.col == 7 || this.col == 8)) {
    return 1;
  } else {
    return 0;
  }
}

function Coordinate_Random() {
  this.row = 8;  this.col = 8;
  while((this.row == 7 || this.row == 8) && (this.col == 7 || this.col == 8)) {
    this.row = random(0,15);
    this.col = random(0,15);
  }
}

function Quadrant(position) {
  this.position = position;
  this.chips = new Array(4);
  this.chip_location = new Array(4);
  this.chip_count = 0;
  this.corners = new Array(4);
  this.corner_count = 0;

  this.addChip = Quadrant_AddChip;
}

function Quadrant_AddChip(chip) {
  this.chips[this.chip_count] = chip;

  var loc;

  occupied = 1;
  while(occupied) {
    var r, c;

    occupied = 0;

    switch(this.position) {
      case topright:
          r = random(9, 14);  c = random(9, 14);
        break;
      case topleft:
          r = random(9, 14);  c = random(1, 6);
        break;
      case bottomleft:
          r = random(1, 6);  c = random(1, 6);
        break;
      case bottomright:
          r = random(1, 6);  c = random(9, 14);
        break;
    }

    loc = new Coordinate(r, c);

    for(chip_index = 0; chip_index < this.chip_count; chip_index++) {
      if(this.chip_location[chip_index].row == loc.row &&
         this.chip_location[chip_index].col == loc.col) {
        occupied = 1;
      }
    }

    if(loc.bad()) {
      occupied = 1;
    }
  }

  this.chip_location[this.chip_count] = loc;
  this.chip_count++;

  return(loc);
}

function Move(color, direction, loc) {
  this.color = color;
  this.direction = direction;
  this.start = loc;
  this.stop = "";

  this.end = Move_End;
  this.print = Move_Print;
}

function Move_End(loc) {
  this.stop = loc;
  return(loc);
}

function Move_Print() {
  return '<span style="color: ' + colors[this.color] + ';">' + colors[this.color] + ' ' + directions[this.direction] + '</span><br />';
}

function Board(div) {
  this.selected;
  this.cells = new Array(16);
  for(row = 0; row < 16; row++) {
    this.cells[row] = new Array(16);
    for(col = 0; col < 16; col++) {
      this.cells[row][col] = new Cell(16 - row, col + 1);
    }
  }
  this.quadrants = new Array(4);
  for(quadrant = 0; quadrant < 4; quadrant++) {
    this.quadrants[quadrant] = new Quadrant(quadrant);
  }
  this.chips = new Array(17);
  this.robots = new Array(4);
  this.goal_robot;
  this.goal_chip;
  this.moves = new Array();
  this.div = div;
  this.mode = start;
  this.start_time;
  this.interval;

  this.addWalls = Board_AddWalls;
  this.addWall = Board_AddWall;
  this.addRandomChips = Board_AddRandomChips;
  this.addRandomRobots = Board_AddRandomRobots;
  this.paint = Board_Paint;
  this.random4 = Board_Random4;
  this.select = Board_Select;
  this.moveRobot = Board_MoveRobot;
  this.setGoal = Board_SetGoal;
  this.showMoves = Board_ShowMoves;
  this.undo = Board_Undo;
  this.print = Board_Print;
  this.mouse = Board_Mouse;
  this.randomize = Board_Randomize;
  this.getMode = Board_GetMode;
  this.setMode = Board_SetMode;
  this.showTime = Board_ShowTime;
  this.addRandomWhorl = Board_AddRandomWhorl;

  document.onkeyup = KeyCheck;
}

function Board_ShowTime() {
  var date = new Date();
  finish_time = date.getTime();

  duration = finish_time - board.start_time;

  minutes = parseInt(duration / 60000);
  seconds = parseInt((duration - (minutes * 60000)) / 1000);
  millis = duration % 1000;

  document.getElementById('curtime').innerHTML = minutes + ":" + seconds + "." + millis;
}

function Board_GetMode() {
  return this.mode;
}

function Board_SetMode(mode) {
  this.mode = mode;
}

function Board_Randomize() {
  this.addWalls(0, random(1, 6), right);
  this.addWalls(0, random(9, 14), left);
  this.addWalls(15, random(1, 6), right);
  this.addWalls(15, random(9, 14), left);
  this.addWalls(random(1, 6), 0, top);
  this.addWalls(random(9, 14), 0, bottom);
  this.addWalls(random(1, 6), 15, top);
  this.addWalls(random(9, 14), 15, bottom);

  this.addRandomChips();
  this.addRandomWhorl();
  this.addRandomRobots();
}

function Board_Mouse(row,col) {
  row = 15 - row;
  if(this.mode != play) {
      board.moves.length = 0;
      document.getElementById("moves").innerHTML = "";
      board.interval = window.setInterval(board.showTime, 50);
      board.setGoal();
      board.setMode(play);
      board.paint();
  } else {
    if(this.cells[row][col].robot != empty) {
      this.selected = this.cells[row][col].robot;
    } else {
      start_pos = this.robots[this.selected];
      if(row == start_pos.row) {
        if(col > start_pos.col) {
          board.moveRobot(right);
        } else {
          board.moveRobot(left);
        }
        board.showMoves();
      } else if(col == start_pos.col) {
        if(row > start_pos.row) {
          board.moveRobot(up);
        } else {
          board.moveRobot(down);
        }
        board.showMoves();
      }
    }
  }
}

function Board_Print() {
  output = '<div style="max-height:400px;"><table align="center" cellspacing="0">';
  for(r = 0; r < 16; r++) {
    output += '<tr>';
    if(r == 0) {
      output += '<td rowspan="16" style="padding-right:10px; width: 210px;" valign="top"><div id="docs"></div></td>';
    }
    for(c = 0; c < 16; c++) {
      if((r == 7 || r == 8) && (c == 7 || c == 8)) {
        if(r == 7 && c == 7) {
          output += '<td id="center" class="cell" align="center" valign="center" rowspan="2" colspan="2">&nbsp;</td>';
        }
      } else {
        output += '<td id="r' + (r + 1) + 'c' + (c + 1) + '" class="cell" align="center" valign="center" onClick="board.mouse(' + r + ',' + c + ')">&nbsp;</td>';
      }
    }
    if(r == 0) {
      output += '<td rowspan="16" style="padding-left:10px; width: 210px;" valign="top"><div id="time">Time: <span id="curtime"></span></div><div id="moves"></div></td>';
    }
    output += '</tr>';
  }
  output += '</table></div><div id="start" align="center"><span id="tostart" onClick="board.mouse(\'tostart\')">Press space or click mouse to begin</span></div><div style="display:none;"><img src="/images/cell.gif" /><img src="/images/robot_red.gif" /><img src="/images/robot_yellow.gif" /><img src="/images/robot_green.gif" /><img src="/images/robot_blue.gif" /><img src="/images/circle_red.gif" /><img src="/images/circle_yellow.gif" /><img src="/images/circle_green.gif" /><img src="/images/circle_blue.gif" /><img src="/images/triangle_red.gif" /><img src="/images/triangle_yellow.gif" /><img src="/images/triangle_green.gif" /><img src="/images/triangle_blue.gif" /><img src="/images/square_red.gif" /><img src="/images/square_yellow.gif" /><img src="/images/square_green.gif" /><img src="/images/square_blue.gif" /><img src="/images/octagon_red.gif" /><img src="/images/octagon_yellow.gif" /><img src="/images/octagon_green.gif" /><img src="/images/octagon_blue.gif" /><img src="/images/whorl.gif" /></div><div id="spacer" /></div>';
  document.getElementById(this.div).innerHTML = output;

  document.getElementById('docs').innerHTML = "<div id='trainer'>Trainer version 0.219</div><b>Using the keyboard:</b><br />Press:<br />'r' - select red robot<br />'y' - select yellow robot<br />'g' - select green robot<br />'b' - select blue robot<br />Cursor keys move the<br />&nbsp;&nbsp;selected robot<br />'u' - undo the last move<br />space - reset the board<p /><b>Using the mouse:</b><br />Click a robot<br />&nbsp;&nbsp;to select it<br />Click on the same row<br />&nbsp;&nbsp;or column to move";

  //Add edges
  for(index = 0; index < 16; index++) {
    this.cells[0][index].addWall(bottom);
    this.cells[15][index].addWall(top);
    this.cells[index][0].addWall(left);
    this.cells[index][15].addWall(right);
  }

  //Add center
  this.addWalls(7, 6, right);
  this.addWalls(8, 6, right);
  this.addWalls(7, 9, left);
  this.addWalls(8, 9, left);
  this.addWalls(6, 7, top);
  this.addWalls(6, 8, top);
  this.addWalls(9, 7, bottom);
  this.addWalls(9, 8, bottom);

}

function Board_Undo() {
  if(this.moves.length > 0) {
    move = this.moves[this.moves.length - 1];
    this.moves.length = this.moves.length - 1;
    from_loc = move.stop;
    to_loc = move.start;
    //alert("Moving from (" + from_loc.row + "," + from_loc.col + ") to (" + to_loc.row + "," + to_loc.col + ").");
    color = this.cells[from_loc.row][from_loc.col].robot;
    this.robots[color] = to_loc;
    this.selected = color;
    this.cells[to_loc.row][to_loc.col].robot = color;
    this.cells[from_loc.row][from_loc.col].robot = empty;
    this.cells[to_loc.row][to_loc.col].paint();
    this.cells[from_loc.row][from_loc.col].paint();
    this.showMoves();
  }
}

function Board_ShowMoves() {
  if(this.mode == play) {
    var output = "";
    for(move = 0; move < this.moves.length; move++) {
      output += (move + 1) + ': ' + this.moves[move].print();
    }

    document.getElementById('moves').innerHTML = output;
  }
}

function Board_SetGoal() {
  this.goal_chip = random(0, 16);
  if(this.goal_chip == 16) {
    this.goal_robot = random(0, 3);
    separator = '';
    document.getElementById("center").innerHTML = '<img src="/images/whorl.gif" />';
    this.selected = random(0,3);
  } else {
    this.goal_robot = Math.floor(this.goal_chip / 4);
    document.getElementById("center").innerHTML = '<img src="/images/robot_' + colors[this.goal_robot] + '.gif" /><br /><img src="/images/'+ shapes[getShapeFromChip(this.goal_chip)] + '_' + colors[getColorFromChip(this.goal_chip)] + '.gif" />';
    this.selected = this.goal_robot;
  }

}

function Board_MoveRobot(direction) {
  var r = this.robots[this.selected].row;
  var c = this.robots[this.selected].col;
  var dr, dc;

  // Check to see whether we're in the middle of a move...
  if(this.moves.length == 0 || this.moves[this.moves.length - 1].stop != "") {
    // No.  Let's create the entry for this move.
    this.moves[this.moves.length] = new Move(this.selected, direction, new Coordinate(r, c));
  }

  // Calculate the next cell in the move.
  switch(direction) {
    case left:
      dr = 0;  dc = -1;
      break;
    case up:
      dr = 1;  dc = 0;
      break;
    case right:
      dr = 0;  dc = 1;
      break;
    case down:
      dr = -1;  dc = 0;
      break;
  }
  new_r = r + dr;
  new_c = c + dc;

  // Are there any walls between the current cell and the next cell?
  if(this.cells[r][c].walls & (1 << direction)) {
    return(new Coordinate(r, c));
  }

  // Is there a robot in the new cell?
  if(this.cells[new_r][new_c].robot != empty) {
    return(new Coordinate(r, c));
  }

  // Must be a valid move
  // Update the new location of the selected robot
  this.robots[this.selected] = new Coordinate(new_r, new_c);
  // Clear the contents of the previous location and redraw the cell
  this.cells[r][c].robot = empty;
  this.cells[r][c].paint();
  // Update the contents of the new location and redraw the cell
  this.cells[new_r][new_c].robot = this.selected;
  this.cells[new_r][new_c].paint();

  robot_loc = this.robots[this.selected];
  goal_loc = this.chips[this.goal_chip];

  // If we've moved the goal color robot and the destination is the same as
  // the goal's destination, we're done!
  if((this.goal_chip == 16 || this.selected == getColorFromChip(this.goal_chip)) && robot_loc.row == goal_loc.row && robot_loc.col == goal_loc.col) {
    // Calculate and display the finish time.
    var date = new Date();
    var finish_time = date.getTime();
    duration = finish_time - this.start_time;
    this.showMoves();
    this.mode = finish;
    this.showTime();
    this.interval = clearInterval(this.interval);
    minutes = parseInt(duration / 60000);
    seconds = parseInt((duration - (minutes * 60000)) / 1000);
    millis = duration % 1000;
    document.getElementById('moves').innerHTML += '<b>Victory in ' + this.moves.length + ' moves!<br />Time: '+ minutes + ':' + seconds + '.' + millis + '</b>';
    return(goal_loc);
  } else {
    return(this.moves[this.moves.length - 1].end(this.moveRobot(direction)));
  }
}

function Board_Select(robot) {
  this.selected = robot;
}

function Board_Random4() {
  var order = new Array(4);

  order[0] = random(0, 3);
  for(ordinal = 1; ordinal < 4; ordinal++) {
    var next_position;
    used = 1;
    while(used) {
      used = 0;

      next_position = random(0, 3);
      for(position_check = 0; position_check <= ordinal; position_check++) {
        if(order[position_check] == next_position) {
          used = 1;
        }
      }
    }
    order[ordinal] = next_position;
  }

  return order;
}

function Board_AddRandomWhorl() {
  var loc;

  occupied = 0;
  while(! occupied) {
    loc = new Coordinate();
    loc.random();

    if((loc.row != 0 && loc.col != 0 && loc.row != 15 && loc.col != 15) || this.cells[loc.row][loc.col].walls) {
      occupied = 1;
    }
  }

  this.cells[loc.row][loc.col].addWhorl();
  this.chips[16] = loc;

  switch(random(0,3)) {
    case topright:
      this.addWalls(loc.row, loc.col, top, right);
      break;
    case topleft:
      this.addWalls(loc.row, loc.col, top, left);
      break;
    case bottomleft:
      this.addWalls(loc.row, loc.col, bottom, left);
      break;
    case bottomright:
      this.addWalls(loc.row, loc.col, bottom, right);
      break;
  }
}

function Board_AddRandomRobots() {
  for(robot = 0; robot < 4; robot++) {
    var loc;

    occupied = 0;
    while(! occupied) {
      loc = new Coordinate();
      loc.random();

      if(this.cells[loc.row][loc.col].robot) {
        occupied = 1;
      }
    }

    this.cells[loc.row][loc.col].addRobot(robot);
    this.robots[robot] = loc;
  }
}

function Board_AddRandomChips() {
  quadrant_order = this.random4();
  shape_order = this.random4();
  color_order = this.random4();
  direction_order = this.random4();

  for(quadrant = 0; quadrant < 4; quadrant++) {
    for(position = 0; position < 4; position++) {
      loc = this.quadrants[quadrant_order[quadrant]].addChip(getChip(shape_order[position], color_order[(quadrant + position) % 4]));
      chip = getChip(shape_order[position], color_order[(quadrant + position) % 4]);
      this.cells[loc.row][loc.col].addChip(chip);
      this.chips[chip] = loc;
      switch(direction_order[position]) {
        case topright:
          this.addWalls(loc.row, loc.col, top, right);
          break;
        case topleft:
          this.addWalls(loc.row, loc.col, top, left);
          break;
        case bottomleft:
          this.addWalls(loc.row, loc.col, bottom, left);
          break;
        case bottomright:
          this.addWalls(loc.row, loc.col, bottom, right);
          break;
      }
    }
  }
}

function Board_AddWall(row, col, wall) {
  var next_row, next_col;

  this.cells[row][col].addWall(wall);

  switch(wall) {
    case left:
      next_row = row;  next_col = col - 1;  wall = right;
      break;
    case top:
      next_row = row + 1;  next_col = col;  wall = bottom;
      break;
    case right:
      next_row = row;  next_col = col + 1;  wall = left;
      break;
    case bottom:
      next_row = row - 1;  next_col = col;  wall = top;
      break;
  }
  if(next_row >= 0 && next_row < 16 && next_col >= 0 && next_col < 16) {
    this.cells[next_row][next_col].addWall(wall);
  }
}

function Board_AddWalls(row, col, wall1, wall2) {
  this.addWall(row, col, wall1);

  if(typeof(wall2) != "undefined") {
    this.addWall(row, col, wall2);
  }
}

function Board_Paint() {
  for(row = 0; row < 16; row++) {
    for(col = 0; col < 16; col++) {
      this.cells[row][col].paint();
    }
  }
  document.getElementById("tostart").style.display = "none";
  var date = new Date();
  this.start_time = date.getTime();
}

function KeyCheck(e) {
  var KeyID = (window.event) ? event.keyCode : e.keyCode;

  mode = board.getMode();
  if(mode == play) {
    switch(KeyID) {
      case 32: // Space
        while(board.moves.length) {
          board.undo();
        }
        board.showmoves();
      case 37: // Cursor left
        board.moveRobot(left);
        board.showMoves();
        break;
      case 38: // Cursor up
        board.moveRobot(up);
        board.showMoves();
        break;
      case 39: // Cursor right
        board.moveRobot(right);
        board.showMoves();
        break;
      case 40: // Cursor down
        board.moveRobot(down);
        board.showMoves();
        break;
      case 82: // r
        board.select(red);
        break;
      case 89: // y
        board.select(yellow);
        break;
      case 71: // g
        board.select(green);
        break;
      case 66: // b
        board.select(blue);
        break;
      case 85: //u
        board.undo();
        break;
    }
  } else if(mode == start) {
    if(KeyID == 32) {
      board.interval = window.setInterval(board.showTime, 50);
      board.setGoal();
      board.setMode(play);
      board.paint();
    }
  } else if(mode == finish) {
    switch(KeyID) {
      case 85: //u
        board.interval = window.setInterval(board.showTime, 50);
        board.setMode(play);
        board.undo();
        board.showMoves();
        break;
      case 32:
        board.moves.length = 0;

        document.getElementById('moves').innerHTML = '';
        board.interval = window.setInterval(board.showTime, 50);
        board.setGoal();
        board.setMode(play);
        board.paint();
        break;;
    }
  }
}
