
// Twisteroo puzzle game

// Global appearance config
var unfinished_color = "#ffcccc";
var finished_color = "#88ff88";
var gridded_color = "#ccccff";
var iphone_selected_color = "#ff8888";
var cell_size = 50;
var grid_cell_size_x = 51;
var grid_cell_size_y = 50;

// Globals
var puzzle_words;
var grid_top_left;
var grid_bottom_right;
var grid_letters;
var last_cell = [-1, -1];
var words_completed = 0;
var grid_table;
var instr_element;
var changepuzzle_element;

var ops_clear_on = 0;

var instructions = "<center><u><b>How to play</b></u></center><br>Your goal is to drag tiles into the grid until the grid contains every word from the quotation below.   Words can twist in any direction -- up, down, left, right, diagonally -- as long as all the letters in a word are touching.   A tile can be re-used many times, even within the same word.    When a word is complete, it will light up green at the top of the screen.  When all the words are green, you win!";

var exclamations = ["Awesome", "Great", "Well done", "Nice", "Excellent", "Sweet"];

var agent=navigator.userAgent.toLowerCase();
var is_iphone = ((agent.indexOf('safari')) != -1);

function start_lb() {
  grid_table = document.getElementById('puzzle_grid');
  instr_element = document.getElementById('instructions');
  changepuzzle_element = document.getElementById('changepuzzle');

  puzzle_words = puzzle_string.split(" ");
  make_title();
  set_quote_info();
  make_grid();
  set_status_bar("Pick up a tile to move.");
  set_operations_bar(-1);
  var current_wins = get_current_wins();
  if ((current_wins == null) && (puzzle_cat == "quotes1")) {
    toggle_instructions();
  }  
};

function set_quote_info() {
  var quote_span = document.getElementById("quote_info");
  if (quote_author != "Unknown") {
	  quote_span.innerHTML = '(Quote source: <a href="' + quote_link + '" target=new>' + quote_author + '</a>)';
  }
}

function set_status_bar(str) {
  var status_bar = document.getElementById("status_bar");
  status_bar.innerHTML = str;
}

function dismiss_instructions() {
  instr_element.innerHTML = '';
}

function toggle_changepuzzle() {
  var dismiss_str = '<a href="#" onClick=\"dismiss_changepuzzle();\">Cancel</a>';
  var current_wins = get_current_wins();
  var str = "";
  if (changepuzzle_element.innerHTML == '') {

    if (puzzle_cat != 'daily') {
      str = str + "Puzzles in the current category (<b>" + puzzle_cat_name + "</b>):<br><blockquote><font size=8>";
    var start = 0;
    // if (puzzle_cat == "quotes1") start = -1;
    for (var i=start; i<puzzle_cat_total; i++) {
      var item = '<a href="/?cat=' + puzzle_cat + '&pnum=' + (i+1) + '">' + (i+1) + '</a>';
      if ((current_wins & (1 << i)) > 0) { 
	item = "<span style=\"{background-color:88ff88;}\">" + item + "</span>";
      }
      str = str + item + ' ';
    }
    str = str + '</font></blockquote>Other categories:<br><blockquote><font size=2>';
    }

    if (puzzle_cat == 'daily') str = str + '<a href="/?cat=daily&l=list">Puzzle of the Day archive</a><br>';
    
    for (var i=0; i<puzzle_categories.length; i++) {
      var catid_catname = puzzle_categories[i];
      var cat_id = catid_catname[0];
      var cat_name = catid_catname[1];
      if ((puzzle_cat == cat_id) || ('end' == cat_id)) continue;
      str = str + '<a href="/?cat=' + cat_id + '">' + cat_name + '</a>';
      if ('daily' == cat_id) str = str + ' (<a href="/?cat=daily&l=list">Archive</a>)';
      str = str + '<br>';
    }
    str = str + '</blockquote></font>';

    changepuzzle_element.innerHTML = '<table class=instr width=400><td>' + str + '<br><center>' + dismiss_str + '</center></td></table><br>';
  } else {
    changepuzzle_element.innerHTML = '';
  }
}

function dismiss_changepuzzle() {
  changepuzzle_element.innerHTML = '';
}

function toggle_instructions() {
  var dismiss_str = '<a href="#" onClick=\"dismiss_instructions();\">Continue playing</a><br><br><i>Game by Doug Beeferman,<br> in loving memory of Bonnie Beeferman</i>';
  if (instr_element.innerHTML == '') {
    instr_element.innerHTML = '<table class=instr width=400><td>' + instructions + '<br><br><center>' + dismiss_str + '<br></td></table><br>';
  } else {
    instr_element.innerHTML = '';
  }
}


function set_operations_bar(set_clear) {
  var ops_bar = document.getElementById("operations_bar");
  var str = "";
  if (set_clear != -1) ops_clear_on = set_clear;
  if (ops_clear_on) {
    str = str + "<a href=\"#\" onClick=\"clear_grid();\">Clear grid</a>";
  }
  ops_bar.innerHTML = str;

  var print_bar = document.getElementById("print");
  if ((window.print != null) && (print_bar.innerHTML == "")) print_bar.innerHTML = "&nbsp;&nbsp;<a href=\"#\" onClick=\"window.print();\"><img align=top src=\"prints.gif\" border=0></a>";
}

function wedge(event){ Event.stop(event); return false; } 

function make_draggable(id) {
  if (Prototype.Browser.IE) {
    return new Draggable(id, {onStart:function() { if (document.all) { Event.observe(document.body, "drag", wedge, false);  Event.observe(document.body, "selectstart", wedge, false);  pickup_tile(); }}, onEnd:end_tile_drag, revert:function(){ if (document.all){ Event.stopObserving(document.body, "drag", wedge, false); Event.stopObserving(document.body, "selectstart", wedge, false);} }, ghosting:true, snap:tile_snap, endeffect:wedge});
  } else {
    return new Draggable(id, {onStart:pickup_tile, onEnd:end_tile_drag, ghosting:true, snap:tile_snap});
  }
}

function make_title() {
  var i;
  var row = 0;
  var num_chars = 0;
  var num_squares = 0;

  add_title_square('puzzle_title_' + row, "[]", 0, "&ldquo;", 0);  // Begin quote
  num_chars = num_chars + 1;

  for (i = 0; i < puzzle_words.length; i++) {
    if (num_chars > 12) {
      row = row + 1;
      num_chars = 0;
    }
    if ((num_chars != 0) && (!((row == 0) && (num_chars == 1)))) {
      add_title_square('puzzle_title_' + row, 
		       "[]", 0, "", 0);
      num_chars = num_chars + 1;
    }
    var word = puzzle_words[i].toUpperCase();
    var j;
    for (j = 0; j < word.length; j++) {
      var square_id = 'title_' + num_squares;
      num_squares++;
      var ch = word.charAt(j);
      add_title_square('puzzle_title_' + row, 
		       square_id, 0, ch, 1);
      make_draggable(square_id);
    }
    num_chars = num_chars + word.length;
  }

  add_title_square('puzzle_title_' + row, "[]", 0, "&rdquo;", 0);  // End quote
  num_chars = num_chars + 1;
}

function clear_grid() {
  var i, j;
  for (i=0; i<puzzle_height; i++) {
    for (j=0; j<puzzle_width; j++) {
      var cell_id = 'grid_' + i + '_' + j;
      var cell_element = document.getElementById(cell_id + "_outer");
      cell_element.innerHTML = tile_html(" ", "white", cell_id);
      grid_letters[i][j] = "";
    }
  }
  check_complete();
}

function grid_cell_of(x, y) {
  grid_top_left = [grid_table.offsetLeft, grid_table.offsetTop];
  grid_bottom_right = [grid_table.offsetLeft + (cell_size+2) * puzzle_width, grid_table.offsetTop + (cell_size+2) * puzzle_height];

  if (((x >= grid_top_left[0]) && (x < grid_bottom_right[0])) && 
      ((y >= grid_top_left[1]) && (y < grid_bottom_right[1]))) {
    var cell = [Math.round((y - grid_top_left[1]) / grid_cell_size_y),
		Math.round((x - grid_top_left[0]) / grid_cell_size_x)];
		
    if ((cell[1] < puzzle_width) && (cell[0] < puzzle_height)) {

      if ((grid_letters[cell[0]][cell[1]]) != "") {  
	//	return [-2, -2];  // Occupied
	return cell;
      } else {
	return cell;
      }
    }
  }
  return [-1, -1]; // Out of bounds
}

function tile_snap(x, y) {
  var x_center = x;
  var y_center = y;
  
  var cell = grid_cell_of(x_center, y_center);
  if ((last_cell != cell) && (last_cell[0] >= 0)) {
    if (cell[0] == -1) {
      set_status_bar("Drop the tile outside the grid to make it go away.  If you need it again, pull it from the quotation.");
    } else if (ops_clear_on) {
      set_status_bar("Set the tile on an empty space, or on another tile to swap it.");
    } else {
      set_status_bar("Set the tile on an empty space.");
    }
  }
  last_cell = cell;
  if (cell[0] < 0) {
    return [x, y];
  } else {
    return [4 + grid_top_left[0] + cell[1] * grid_cell_size_x,
	    3 + grid_top_left[1] + cell[0] * grid_cell_size_y];
  }
}

function check_complete() {
  var w,i,j,start_index=0,words_completed_now=0;
  for (w = 0; w < puzzle_words.length; w++) {
    var word_complete = 0;
    var word = puzzle_words[w].toUpperCase();
    for (i=0; i<puzzle_height; i++) {
      for (j=0; j<puzzle_width; j++) 
	if (word_at(word, i, j)) {
	  word_complete = 1;
	  break;
	}
      if (word_complete == 1) break;
    }
    var color = unfinished_color;
    if (word_complete == 1) {
      color = finished_color;
      words_completed_now++;
    }
    for (i=start_index; i<start_index + word.length; i++) {
      var square_id = "title_" + i;
      var tile_element = document.getElementById(square_id + "_outer");
      var letter_span = document.getElementById(square_id + "_span");
      var letter = letter_span.innerHTML;
       
      tile_element.innerHTML = tile_html(letter, color, square_id);
      make_draggable(square_id);
    }
    start_index = start_index + word.length;
  }
  return words_completed_now;
}

function word_at(word, y, x) {
  if (grid_letters[y][x] == word.charAt(0)) {
    if (word.length == 1) return 1;
    var new_word = word.substring(1);
    if (y>0) {
      if ((word_at(new_word, y-1, x)) ||
	  ((x>0) && (word_at(new_word, y-1, x-1))) ||
	  ((x<puzzle_width-1) && (word_at(new_word, y-1, x+1)))) return 1;
    }
    if (y<puzzle_height-1) {
      if ((word_at(new_word, y+1, x)) ||
	  ((x>0) && (word_at(new_word, y+1, x-1))) ||
	  ((x<puzzle_width-1) && (word_at(new_word, y+1, x+1)))) return 1;
    }
    if (x>0) {
      if (word_at(new_word, y, x-1)) return 1;
    }
    if (x<puzzle_width-1) {
      if (word_at(new_word, y, x+1)) return 1;      
    }
  }
  return 0;
}

function pickup_tile(draggable_obj, mouse_event) {
  set_status_bar("Move the tile into the grid.");

  // Fix for internet explorer
  if (document.all) { 
    Event.observe(document.body, "drag", wedge, false); 
    Event.observe(document.body, "selectstart", wedge, false); 
  }
  return;
}

function end_tile_drag(draggable_obj, mouse_event) {
  var tile_id = draggable_obj.element.getAttribute("id");
  var letter_span = document.getElementById(tile_id + "_span");
  var letter = letter_span.innerHTML;
  var is_titlebar = 0;
  var displaced_letter = " ";

  if ((tile_id.substring(0,5) == "title")) is_titlebar = 1; 

  // If dragged from the title to the grid:  copy onto grid, update state
  // If dragged from the grid to anywhere to outside the grid:  delete from grid
  // If dragged from the grid to an invalid location within the grid:  revert
  // If dragged from the grid to a valid spot on the grid:  move, update state

  if (last_cell[0] == -1) {
    // Out of bounds.
    if (!is_titlebar) {
      draggable_obj.destroy();
      delete draggable_obj;
    }
  } else if (last_cell[0] == -2) {
    // invalid location on grid
  } else {
    var cell_id = "grid_" + last_cell[0] + "_" + last_cell[1];
    var cell_element = document.getElementById(cell_id + "_outer");
    var old_letter_span = document.getElementById(cell_id + "_span");
    displaced_letter = old_letter_span.innerHTML;

    cell_element.innerHTML = tile_html(letter, gridded_color, cell_id);
    grid_letters[last_cell[0]][last_cell[1]] = letter;
    make_draggable(cell_id);
  }
  // If it's from the grid;  swap with letter at original cell
  if ((tile_id.substring(0,4) == "grid") && (last_cell[0] >= -1)) {
    var old_cell_element = document.getElementById(tile_id + "_outer");
    if (displaced_letter == " ") {
      old_cell_element.innerHTML = tile_html(" ", "white", tile_id);
    } else {
      old_cell_element.innerHTML = tile_html(displaced_letter, gridded_color, tile_id);
      make_draggable(tile_id);
    }
    var part_array = tile_id.split("_");
    grid_letters[parseInt(part_array[1])][parseInt(part_array[2])] = displaced_letter;
  }

  var words_completed_now = check_complete();
  if (words_completed_now > words_completed) {
    var new_words_str = "<b>" + words_completed_now + "</b> word";
    if (words_completed_now > 1) new_words_str += "s";
    new_words_str += " of <b>" + puzzle_words.length + "</b>";

    var exclamation = exclamations[Math.floor(Math.random()*exclamations.length)];
    set_status_bar(exclamation + "! You've completed " + new_words_str + ".");
  } else {
    set_status_bar("Pick up a tile to move.");
  }
  words_completed = words_completed_now;
  if (words_completed == puzzle_words.length) {
    if (puzzle_cat == 'daily') {
	alert("Congratulations!  See you tomorrow!  :-)");
    } else {
      set_win_bit();
    }
    if (puzzle_id < puzzle_cat_total - 1) {
      var next_puzzle_id = puzzle_id + 2;
      var next_puzzle_url = "/?cat=" + puzzle_cat + "&pnum=" + next_puzzle_id;
      if (puzzle_cat == 'daily') {
      } else {
	alert("Congratulations!  Onward to puzzle " + next_puzzle_id + "!");
	document.location = next_puzzle_url;
      }
    } else {
      set_status_bar("<font size=10 face=courier><b>Congratulations!</b></font><br>  Click on a new category above.");
      if (changepuzzle_element.innerHTML == '') {
	toggle_changepuzzle();
      }
    }
    Effect.Puff('puzzle_grid', { duration: 3 });
  }
  last_cell = [-1, -1];
  set_operations_bar(1);
}

function get_current_wins() {
  var cookie_val = get_cookie( 'wins_' + puzzle_cat);
  if ((cookie_val & (1 << puzzle_id)) > 0) {
    var solved_bar = document.getElementById("puzzle_havesolved");
    solved_bar.innerHTML = "<font color=green><b>You have solved this puzzle already!</font><br>";
  }
  return cookie_val;
}

function set_win_bit() {
  var current_win_vector = 0;
  var cookie_val = get_cookie( 'wins_' + puzzle_cat);
  if (cookie_val != null) {
    current_win_vector = parseInt(cookie_val);
  }
  current_win_vector |= (1 << (puzzle_id));
  set_cookie( 'wins_' + puzzle_cat, current_win_vector, 1000, '/', '', '' );
}

function make_grid() {
  var i, j;
  grid_letters = new Array(puzzle_height);
  for (i=0; i<puzzle_height; i++) {
    grid_letters[i] = new Array(puzzle_width);
    grid_table.insertRow(i);
    for (j=0; j<puzzle_width; j++) {
      var cell_id = 'grid_' + i + '_' + j;
      add_title_square('puzzle_grid', cell_id, i, " ", 1);
      grid_letters[i][j] = "";
    }
  }
  grid_top_left = [grid_table.offsetLeft, grid_table.offsetTop];
  grid_bottom_right = [grid_table.offsetLeft + (cell_size+2) * puzzle_width, grid_table.offsetTop + (cell_size+2) * puzzle_height];
}

function tile_html(letter, bgcolor, tile_id) {
  var border = 1;
  var height = 38;
  var face = "courier";
  var align = "center";

  if (bgcolor == "white") {
    border = 0;
    height = 39;
  }

  if (letter == "&ldquo;") {
    align="right";
    face="times";
  } else if (letter == "&rdquo;") {
    align="left";
    face="times";
  }

  var ret = "<b><table id=" + tile_id + " border=" + border + "\"><td width=40 bgcolor=" + bgcolor + " height=" + height + " align=" + align + "><font size=5 face=" + face + "><span id=" + tile_id + "_span>" + letter + "</span></font></td></table></b>";
  if (Prototype.Browser.IE) {
    ret = '<span style="cursor:move">' + ret + '</span>';
  }
  return ret;
}


function add_title_square(table_id, square_id, row, letter, is_real) {
  var table = document.getElementById(table_id);

  var new_cell = document.createElement('td');
  new_cell.setAttribute("width", cell_size);
  new_cell.setAttribute("height", cell_size);
  new_cell.setAttribute("align", "center");
  if (is_real) {
    new_cell.setAttribute("onmouseover", "this.style.cursor='move'");
  } else {
    new_cell.setAttribute("onmouseover", "this.style.cursor='default'");
  }
  new_cell.setAttribute("align", "center");
  new_cell.setAttribute("id", square_id + "_outer");
  table.rows[row].appendChild(new_cell);
  if ((letter == " ") || (letter == "&ldquo;") || (letter == "&rdquo;")) {  // initial grid tile
    new_cell.innerHTML = tile_html(letter, "white", square_id);
  } else if (letter != "") {
    new_cell.innerHTML = tile_html(letter, unfinished_color, square_id);
  }
}


// Pinched from Effect.Shake;  made it a vertical shake and tweaked the times
Effect.EndShake = function(element) {
  element = $(element);
  var options = Object.extend({
    distance: 20,
    duration: 0.5
  }, arguments[1] || {});
  var distance = parseFloat(options.distance);
  var split = parseFloat(options.duration) / 10.0;
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left') };
    return new Effect.Move(element,
      { y:  distance, x: 0, duration: split, afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { y: -distance*2, x: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { y:  distance*2, x: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { y: -distance*2, x: 0, duration: split,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { y:  distance*2, x: 0, duration: split,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { y: -distance, x: 0, duration: split, afterFinishInternal: function(effect) {
        effect.element.undoPositioned().setStyle(oldStyle);
  }}) }}) }}) }}) }}) }});
};



function set_cookie( name, value, expires, path, domain, secure ) 
{
  var today = new Date();
  today.setTime( today.getTime() );
  if ( expires ) expires = expires * 1000 * 60 * 60 * 24;
  var expires_date = new Date( today.getTime() + (expires) );
  document.cookie = name + "=" +escape( value ) +
    ( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) + 
    ( ( path ) ? ";path=" + path : "" ) + 
    ( ( domain ) ? ";domain=" + domain : "" ) +
    ( ( secure ) ? ";secure" : "" );
}



function get_cookie( check_name ) {
  var a_all_cookies = document.cookie.split( ';' );
  var a_temp_cookie = '';
  var cookie_name = '';
  var cookie_value = '';
  var b_cookie_found = false; // set boolean t/f default f
  
  for ( i = 0; i < a_all_cookies.length; i++ )
    {
      // now we'll split apart each name=value pair
      a_temp_cookie = a_all_cookies[i].split( '=' );
      // and trim left/right whitespace while we're at it
      cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');
      // if the extracted name matches passed check_name
      if ( cookie_name == check_name )
	{
	  b_cookie_found = true;
	  // we need to handle case where cookie has no value but exists (no = sign, that is):
	  if ( a_temp_cookie.length > 1 )
	    {
	      cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
	    }
	  // note that in cases where cookie is initialized but no value, null is returned
	  return cookie_value;
	  break;
	}
      a_temp_cookie = null;
      cookie_name = '';
    }
  if ( !b_cookie_found )
    {
      return null;
    }
}
