var speed_scrabble = function () {
  var server = "/speed_scrabble_server.php",

  grid = [],
  letter_size = 32,
  grid_width = 20,
  grid_height = 15,
  ROUNDS = 10,
  SCORES = 15,
  current_round,
  playing = false,
  my_id,
  last_message = '',
  games_timer = null,
  game_timer = null,
  consecutive = 0,
  chat = {},
  timer_ok = false,
  timer_check = null,
  start_game_options = { 'command': 'startgame', 'skill': 0, 'private': 0, 'wordlength': 0, 'lang': '' },
  language = { 'fr': 'French' },
  game_list,

  game_list_action_formatter = function (cell, rec, col, data) {
    if ( rec.getData("private") != 1 ) {
      cell.innerHTML = '<span class="yui-button"><span class="first-child"><button type="button" onclick="game.join_game(' + rec.getData('id') + ')">Join this game</button></span></span>';
    }
  },

  game_list_players_formatter = function (cell, rec, col, data) {
      var 
        players = rec.getData("players"),
        name = YAHOO.lang.escapeHTML( rec.getData("nickname") );
      if ( players <= 1 ) {
        cell.innerHTML = name;
      }
      else if ( players == 2 ) {
        cell.innerHTML = name + " and 1 other.";
      }
      else {
        cell.innerHTML = name + " and " + (players-1) + " others.";
      }
  },

  game_list_round_formatter = function (cell, rec, col, data) {
    var
      st = rec.getData("status");
    if ( st == 0 ) {
      cell.innerHTML = 'Not started';
    }
    else if ( st == 1 ) {
      cell.innerHTML = 'Round ' + rec.getData("current_round");
    }
    else {
      cell.innerHTML = "Finished";
    }
  },

  game_list_options_formatter = function (cell, rec, col, data) {
    var extra = '';
    rec.getData('common') == 1 && ( extra += ', Same letters' );
    rec.getData('private') == 1 && ( extra += ', Private' );
    rec.getData('word_length') > 0 && ( extra += ', ' + rec.getData('word_length') + ' letters+' );
    rec.getData('lang') == 'fr' && ( extra += ', French' );
    if ( extra === '' ) {
      cell.innerHTML = 'None';
    }
    else {
      cell.innerHTML = extra.slice(2);
    }
  },

  game_list_defs = [ 
    { key: 'round', label: 'Round', formatter: game_list_round_formatter }, 
    { key: 'players', label: 'Players', formatter: game_list_players_formatter }, 
    { key: 'options', label: 'Options', formatter: game_list_options_formatter  }, 
    { key: 'action', label: '', formatter: game_list_action_formatter } ],
  game_list_data = [],
  game_list_source = new YAHOO.util.LocalDataSource( game_list_data ),
  progress,
  container_pos,

  connect_failed = function() {
    show_status( "Error: Failed to connect to server." );
    // try again
    timer_ok = false;
    games_timer = setTimeout( reconnect, 10000 );
  },

  // --- check login
  check_login_response = function(response) {
    if ( response['status'] == 'ok' ) {
      show_status( "Logged in" );
      my_id = response['user_id'];
      document.getElementById( "login_detail" ).innerHTML = response['detail'] + '.&nbsp;&nbsp;<a href="/updateuser.php">Update details</a>&nbsp;&nbsp;<a href="/speed_scrabble_profile.php?id=' + my_id + '">View profile</a>&nbsp;&nbsp;';
      show( "logout" );
      if ( response['game_id'] ) {
        hide_lists();
        show_game();
        playing = true;
        show_status( "Playing game " + response['game_id'] );
        get_game_state()();
      }
      else {
        playing = false;
        if ( response['scores'] ) {
          update_scores( response['scores'] );
        }
        if ( response['trophies'] ) {
          update_trophies( response['trophies'], 'trophy_list' );
        }
        if ( response['fastest'] ) {
          update_trophies( response['fastest'], 'fast_list' );
        }
        if ( response['longest'] ) {
          update_trophies( response['longest'], 'long_list' );
        }
        games();
      }
    }
    else {
      show( "login" );
      show( "welcome" );
      show_status( "Please login" );
    }
  },
  
  // --- login
  login_response = function(response) {
    if ( response['status'] == 'ok' ) {
      show_status( "Logged in" );
      my_id = response['user_id'];
      document.getElementById( "login_detail" ).innerHTML = response['detail'] + '.&nbsp;&nbsp;<a href="/updateuser.php">Update details</a>&nbsp;&nbsp;<a href="/speed_scrabble_profile.php?id=' + my_id + '">View profile</a>&nbsp;&nbsp;';
      show( "logout" ); // show
      if ( response['game_id'] ) {
        playing = true;
        hide_lists();
        show_game();
        show_status( "Playing game " + response['game_id'] );
        get_game_state()();
      }
      else {
        playing = false;
        if ( response['scores'] ) {
          update_scores( response['scores'] );
        }
        if ( response['trophies'] ) {
          update_trophies( response['trophies'], 'trophy_list' );
        }
        if ( response['fastest'] ) {
          update_trophies( response['fastest'], 'fast_list' );
        }
        if ( response['longest'] ) {
          update_trophies( response['longest'], 'long_list' );
        }
        games();
      }
    }
    else if ( response['status'] == 'redirect' ) {
      location.href = response['detail'];
    }
    else {
      show( "login" );
      show( "welcome" );
      show_status( "Please try again - " + response["detail"] );
    }
  },
  
  // --- logout
  logout_response = function(response) {
    if ( response['status'] == 'ok' ) {
      show_status( "Logged out" );
      show( "login" );
      show( "welcome" );
      playing = false;
      hide_lists();
      hide( "game" ); // hide
    }
    else {
      show( "logout" );
      show_status( "Failed to logout: " + response["detail"] );
    }
  },
  
  // --- game list
  games_response = function(response) {
    if ( response['status'] == 'ok' ) {
      show_ss_progress( "Populating game list" );
      // remember expansion status
      var list, i, item, players, extra, others;
      // populate new games
      list = response['games'];
      game_list_data.length = 0;
      
      for ( i = 0; i < list.length; i++ ) {
        item = list[i];
        // add to datatable
        game_list_data[game_list_data.length] = { 
          'id': item['id'],
          'nickname': item['nickname'],
          'players': item['players'],
          'status': item['status'],
          'current_round': item['current_round'],
          'common': item['common'],
          'private': item['private'],
          'word_length': item['word_length'],
          'lang': item['lang']
        };
      }
      game_list.getDataSource().liveData = game_list_data;
      game_list.getDataSource().sendRequest( '', { success: game_list.onDataReturnInitializeTable, scope: game_list } );

      // user count
      if ( response['users'] && response['users'] > 1 ) {
        others = response['users'] - 1;
        document.getElementById( "games_status" ).innerHTML = "<img src='http://static.supernifty.com.au/images/group.png' width='16' height='16'/>&nbsp;" + others + " other user" + (others==1?"":"s") + " waiting to play.";
      }
      else {
        document.getElementById( "games_status" ).innerHTML = "";
      }
      if ( playing ) {
        show_status( "Ready" );
      }
      else {
        show_status( "Click a game to join it, or create a new game" );
        show_lists();
        // refresh again soon
        if ( consecutive < 100 ) { // about 8 minutes
          games_timer = setTimeout( games, 5000 );
          consecutive++;
        }
        else if ( consecutive < 1000 ) { // around 1.5 hours
          games_timer = setTimeout( games, 50000 ); // 50s
          consecutive++;
        }
        else {
          games_timer = setTimeout( games, 500000 ); // about 8 minutes
        }
      }
    }
    else {
      show_status( "Failed to get games list - " + response["detail"] );
    }
  },
  
  // --- newgame
  newgame_response = function(response) {
    if ( response['status'] == 'ok' ) {
      show_status( "New game added" );
      hide_lists();
      show_game();
      get_game_state()();
    }
    else {
      show_status( "Failed to add new game - " + response["detail"] );
    }
  },
  
  // --- join game
  joingame_response = function(response) {
    if ( response['status'] == 'ok' ) {
      show_status( "Joined game" );
      hide_lists();
      show_game();
      playing = true;
      get_game_state()();
    }
    else {
      show_status( "Failed to join game - " + response["detail"] );
    }
  },
  
  // --- leave game
  leavegame_response = function(response) {
    if ( response['status'] == 'ok' ) {
      show_status( "Left game" );
      playing = false;
      hide( "game" ); // hide
      if ( response['scores'] ) {
        update_scores( response['scores'] );
      }
      if ( response['trophies'] ) {
        update_trophies( response['trophies'], 'trophy_list' );
      }
      if ( response['fastest'] ) {
        update_trophies( response['fastest'], 'fast_list' );
      }
      if ( response['longest'] ) {
        update_trophies( response['longest'], 'long_list' );
      }
      games();
    }
    else {
      show_status( "Failed to leave game - " + response["detail"] );
    }
  },
  
  // --- give up
  giveup_response = function(response) {
    if ( response['status'] == 'ok' ) {
      show_status( "Gave up on round" );
      show_game_status(response);
    }
    else {
      show_status( "Failed to give up on round - " + response["detail"] );
    }
  },
  
  // --- play again
  again_response = function(response) {
    if ( response['status'] == 'ok' ) {
      show_status( "Joined new game." );
      show_game();
      show_game_status(response);
    }
    else {
      show_status( "Failed to join new game to play again - " + response["detail"] );
    }
  },
  
  // --- chat
  chat_response = function(response) {
    if ( response['status'] == 'ok' ) {
      show_status( "Sent." );
      show_game_status(response);
      document.getElementById( "chat_message" ).value = '';
    }
    else {
      show_status( "Failed to send chat message - " + response["detail"] );
    }
  },
  
  // --- chat report
  chat_report_response = function(response) {
    if ( response['status'] == 'ok' ) {
      show_status( response['detail'] );
    }
    else {
      show_status( "Failed to send chat report message - " + response["detail"] );
    }
  },
  
  // --- start game
  startgame_response = function(response) {
    if ( response['status'] == 'ok' ) {
      show_status( "Started game" + readable_options( response ) );
      hide( "start_game" ); // hide
      show_game_status(response);
    }
    else {
      show_status( "Failed to start game - " + response["detail"] );
    }
  },

  gamestate_response = function( response_message ) {
    return function(response) {
      if ( response_message ) {
        show_status( response_message );
      }
      if ( response['status'] == 'ok' ) {
        show_game_status(response);
        // get status again
        if ( playing ) {
          if ( response['game']['status'] == '2' ) {
            game_timer = setTimeout( get_game_state(), 3000 );
          }
          else {
            game_timer = setTimeout( get_game_state(), 2000 );
          }
          timer_ok = true;
        }
      }
      else {
        show_status( "Failed to get game state - " + response["detail"] );
      }
    }
  },
  
  grid_response = function(response) {
    if ( response['status'] == 'ok' ) {
      if ( response['accepted'] == 'ok') {
        // won the round
        show_good(response['message']);
        show_game_status(response);
      }
      else {
        show_bad(response['message']);
      }
    }
    else {
      show_status( "Failed to check grid - " + response["detail"] );
    }
  },

  games = function () {
    if ( !playing ) {
      send_command( { "command": "games" }, "Getting game list", games_response );
      // try comments
      if ( YAHOO.supernifty.comments.build ) {
        YAHOO.supernifty.comments.build();
      }
    }
  };

  function show_headline( value ) {
    var el = document.getElementById("status"), 
      anim = new YAHOO.util.ColorAnim(el, { backgroundColor:{to:"#ff9", from:"#f66"}, duration:1});
    el.innerHTML = value;
    anim.animate();
  }
  
  function show_status( value ) {
    show_headline( value );
  }
  
  function show_ss_progress( value ) {
    show_headline( value + " <img src='http://static.supernifty.com.au/images/progress.gif' style='vertical-align: middle' width='32' height='32'/>" );
  }
  
  function show_good( value ) {
    show_headline( "<img src='http://static.supernifty.com.au/images/accept.png' style='vertical-align: middle' width='16' height='16'/>&nbsp;" + value );
  }
  
  function show_bad( value ) {
    show_headline( "<img src='http://static.supernifty.com.au/images/exclamation.png' style='vertical-align: middle' width='16' height='16'/>&nbsp;" + value );
  }
  
  function hide( e ) {
    var box = document.getElementById(e);
    box.style.display = "none";
  }

  function show( e ) {
    var box = document.getElementById(e);
    box.style.display = "block";
  }
  
  function show_inline( e ) {
    var box = document.getElementById(e);
    box.style.display = "inline";
  }
  
  function hide_lists() {
    hide( "games" ); // hide
    hide( "scores" ); // hide
    hide( "trophies" ); // hide
    hide( "fastest" ); // hide
    hide( "longest" ); // hide
  }
  
  function show_lists() {
    show( "games" ); // show
    show( "scores" ); // show
    show( "trophies" ); // show
    show( "fastest" ); // show
    show( "longest" ); // show
  }
  
  function reconnect() {
    if ( !timer_ok ) {
      show_ss_progress( "Attempting to reconnect..." );
      if ( playing ) {
        get_game_state("Reconnected.")();
      }
      else {
        games();
      }
    }
  }
  
  function send_command( command, progress_message, callback ) {
    var sending = YAHOO.lang.JSON.stringify(command);
    if ( progress_message ) {
      show_ss_progress( progress_message );
    }
    return YAHOO.util.Connect.asyncRequest('POST', server, { success: build_response_handler( callback ), failure: connect_failed }, "json=" + sending ); 
  }
  
  function build_response_handler( callback ) {
    return function(o) {
      try {
        var parsed = YAHOO.lang.JSON.parse(o.responseText); 
        abortable = null;
        callback( parsed );
      }
      catch (e) {
        show_status( "Error: " + e.name + ": " + e.message );
        //alert( o.responseText );
      }
    }
  }
  
  function update_scores( scores ) {
    var html = '', i, j;
    for ( i = 0; i < scores.length; i++ ) {
      j = i+1;
      if ( scores[i]['id'] == my_id ) {
        if ( i == SCORES ) {
          html += '<li>...</li>';
          html += '<li class="li-you"><b><a href="/speed_scrabble_profile.php?id=' + my_id + '">You</a>: ' + scores[i]['score'] + '</b></li>';
        }
        else {
          html += '<li class="li-you"><b>' + j + '. <a href="/speed_scrabble_profile.php?id=' + my_id + '">You</a>: ' + scores[i]['score'] + '</b></li>';
        }
      }
      else {
        html += '<li class="li-user' + scores[i]['gender'] + '">' + j + '. <a href="/speed_scrabble_profile.php?id=' + scores[i]['id'] + '">' + scores[i]['nickname'] + '</a>: ' + scores[i]['score'] + '</li>';
      }
    } 
    document.getElementById( 'scores_list' ).innerHTML = html;
  }
  
  function update_trophies( trophies, target ) {
    var html = '', description, who, i;
    for ( i = 0; i < trophies.length; i++ ) {
      if ( trophies[i]['id'] == my_id ) {
        who = 'You';
      }
      else {
        who = trophies[i]['nickname'];
      }

      if ( trophies[i]['trophy'] == -1 ) { // language
        description = ' won a game in ' + language[ trophies[i]['data'] ]; 
        html += '<li class="achievement-' + trophies[i]['data'] + '"><a href="/speed_scrabble_profile.php?id=' + trophies[i]['id'] + '">' + who + '</a>' + description + '</li>';
      }
      else {
        if ( trophies[i]['trophy'] == 0 ) {
          description = ' won every round, in ' + trophies[i]['data'] + 's'; 
        }
        else if ( trophies[i]['trophy'] >= 100 ) {
          description = ' ' + ( who == 'You' ? 'have' : 'has' ) + ' a vocabulary of ' + trophies[i]['data'] + ' words'; 
        }
        else {
          description = ' won a round with ' + trophies[i]['data']; 
        }
        html += '<li class="achievement-' + trophies[i]['trophy'] + '"><a href="/speed_scrabble_profile.php?id=' + trophies[i]['id'] + '">' + who + '</a>' + description + '</li>';
      }
    } 
    document.getElementById( target ).innerHTML = html;
  }
  
  function show_game() {
    show( "game" );
    playing = true;
    container_pos = YAHOO.util.Dom.getXY("game_box");
    clear_grid();
    document.getElementById( "rounds_won" ).innerHTML = "";
    document.getElementById( "game_score" ).innerHTML = ""; 
    document.getElementById( "chat" ).innerHTML = ""; 
  }

  function readable_options( response ) {
    var message = '';
    if ( response['game']['common']  == '1' ) {
      message += ". Same letters";
    }
    if ( response['game']['word_length']  != '0' ) {
      message += ". Min " + response['game']['word_length'] + "-letter words";
    }
    if ( response['game']['private']  == '1' ) {
      message += ". Private";
    }
    if ( response['game']['lang']  == 'fr' ) {
      message += ". French";
    }
    return message;
  }
  
  // --- grid manipulation
  function add_letter(letter, idx) {
    // find a spare spot
    var x = 0,
      y = 0,
      letter_element, anim, container, handler, item, new_x, new_y;
    while ( letter_at( x, y ) != null ) {
      x += 1;
      if ( x >= grid_width ) {
        x = 0;
        y += 1;
      }
    }
    // add letter to box
    letter_element = document.createElement("div");
    letter_element.setAttribute( "id", "letter_" + idx );
    letter_element.setAttribute( "class", "letter" );
    letter_element.setAttribute( "className", "letter" ); // ie hack
    letter_element.innerHTML = letter;
    anim = new YAHOO.util.ColorAnim(letter_element, { backgroundColor:{to:"#ff9", from:"#f66"}, duration:1});
    anim.animate();
    container = document.getElementById( "game_box" );
    container.appendChild(letter_element);
    YAHOO.util.Dom.setXY( letter_element, [ x * letter_size + container_pos[0], y * letter_size + container_pos[1] ] );
    handler = new YAHOO.util.DD("letter_" + idx); 
    item = { x: x, y: y, letter: letter};
    handler.on( "endDragEvent", function(e) {
      position = YAHOO.util.Dom.getXY( letter_element );
      // snap
      new_x = Math.round( ( position[0] - container_pos[0] ) / 32.0 );
      new_y = Math.round( ( position[1] - container_pos[1] ) / 32.0 );
      if ( new_x >= 0 && new_x < grid_width && new_y >= 0 && new_y < grid_height && letter_at( new_x, new_y ) == null ) {
        item.x = new_x;
        item.y = new_y;
        YAHOO.util.Dom.setXY( letter_element, [ item.x * letter_size + container_pos[0], item.y * letter_size + container_pos[1] ] );
        check_grid();
      }
      else {
        // revert
        YAHOO.util.Dom.setXY( letter_element, [ item.x * letter_size + container_pos[0], item.y * letter_size + container_pos[1] ] );
      }
    }, handler, true );
    return item;
  }
  
  function clear_grid() {
    for ( var i = 0; i < grid.length; i++ ) {
      document.getElementById( "game_box" ).removeChild( document.getElementById( "letter_" + i ) );
    }
    grid = [];
  }
  
  function is_connected() {
    if ( grid.length == 0 ) {
      return false;
    }
    var connected = [ { x:grid[0].x, y:grid[0].y } ],
      done = 0,
      item;
    while ( connected.length > done && connected.length < grid.length ) {
      item = connected[done];
      if ( !contains(item.x-1, item.y, connected) && letter_at( item.x-1, item.y ) ) {
        connected[connected.length] = { x:item.x-1, y:item.y };
      }
      if ( !contains(item.x+1, item.y, connected) && letter_at( item.x+1, item.y ) ) {
        connected[connected.length] = { x:item.x+1, y:item.y };
      }
      if ( !contains(item.x, item.y-1, connected) && letter_at( item.x, item.y-1 ) ) {
        connected[connected.length] = { x:item.x, y:item.y-1 };
      }
      if ( !contains(item.x, item.y+1, connected) && letter_at( item.x, item.y+1 ) ) {
        connected[connected.length] = { x:item.x, y:item.y+1 };
      }
      done += 1;
    }
    
    return ( connected.length == grid.length );
  }
  
  function hashed_grid() {
    var hashed = {},
      i;
    for ( i = 0; i < grid.length; i++ ) {
      hashed[ grid[i].x + "," + grid[i].y ] = grid[i].letter;
    }
    return hashed;
  }
  
  function check_grid() {
    if ( is_connected() ) {
      // submit it
      show_ss_progress( "Checking..." );
      send_command( { "command": "grid", "grid": hashed_grid() }, false, grid_response );
    }
  }
  
  function contains( x, y, grid ) {
    for ( var i = 0; i < grid.length; i++ ) {
      if ( grid[i].x == x && grid[i].y == y ) {
        return true;
      }
    }
    return false;
  }
  
  function letter_at( x, y ) {
    for ( var i = 0; i < grid.length; i++ ) {
      if ( grid[i].x == x && grid[i].y == y ) {
        return grid[i].letter;
      }
    }
    return null;
  }
  
  function add_letters(letters) {
    for ( var i = grid.length; i < letters.length; i++ ) {
      grid[grid.length] = add_letter( letters.charAt(i), i );
    }
  }
  
  function add_scores( score, total, rank, next ) {
    document.getElementById( "game_score" ).innerHTML = 
      "You earnt <b>" + score + "</b> points.<br/>Your score is <b>" + total + "</b>.<br/>Your ranking is <b>" + rank + "</b>." + 
      ( next > score ? "<br/><b>" + (next-total) + "</b> point" + ( next-total==1 ? "" : "s" ) + " to go up a spot!" : "" );
  }
  
  // --- game state
  function show_game_status(response) {
    // show status
    var game_state = response['game'], 
      game_status = 'unknown',
      game_round = '',
      start_in, round_in, percent, players, player_html, i, j, round_html, scores_pos, items, chat_changed, element, anim;
    if ( game_state['status'] == '0' && game_state['is_creator'] ) {
      show( "start_game" );
    }
    else {
      hide( "start_game" );
    }
    if ( game_state['status'] == '0' ) {
      start_in = game_state['start_in'];
      game_status = "Not started - starting in " + start_in + "s";
      // progress bar
      percent = Math.min( 100, 100 * start_in / 60 );
      progress.set('value', percent );
  
      document.getElementById( "game_box" ).style.backgroundImage = "url('http://static.supernifty.com.au/images/grid-ready.png')";
      hide( "play_game" );
      hide( "play_again" );
    }
    else if (game_state['status'] == '1' ) {
      game_status = "Started" + readable_options( response );
      document.getElementById( "game_box" ).style.backgroundImage = "url('http://static.supernifty.com.au/images/grid-started.png')";
      show_inline( "play_game" );
      hide( "play_again" );
      if ( current_round != game_state['current_round'] ) {
        current_round = game_state['current_round'];
      }
      game_round = "Round " + current_round + " of " + ROUNDS + "<br/>"; 
      if ( current_round == ROUNDS ) {
        game_round += "Game over in ";
      }
      else {
        game_round += "Next round in ";
      }
      round_in = game_state['round_in'];
      game_round += round_in + "s";
      // progress bar
      percent = Math.min( 100, 100 * round_in / 60 );
      progress.set('value', percent );
      // letters
      if (response['game']['letters'].length > grid.length) {
        add_letters( response['game']['letters'] );
      }
    }
    else if (game_state['status'] == '2' ) {
      game_status = "Finished";
      progress.set('value', 0 );
      document.getElementById( "game_box" ).style.backgroundImage = "url('http://static.supernifty.com.au/images/grid-finished.png')";
      if ( current_round != game_state['current_round'] ) {
        current_round = game_state['current_round'];
      }
      hide( "play_game" );
      show_inline( "play_again" );
      // letters
      if (response['game']['letters'].length > grid.length) {
        add_letters( response['game']['letters'] );
      }
    }
    document.getElementById( 'game_status' ).innerHTML = game_status;
    document.getElementById( 'game_round' ).innerHTML = game_round;
    //document.getElementById( 'game_counter' ).innerHTML = game_counter;
    // message
    if ( game_state['last'] && game_state['last'] != last_message && game_state['last'] != '' ) {
      last_message = game_state['last'];
      show_status( last_message );
    }
    // players
    players = response['players'];
    player_html = '';
    for ( i = 0; i < players.length; i++ ) {
      if ( players[i]["given_up"] == '1' ) {
        player_html += '<li class="li-lose">' + players[i]["nickname"] + "</li>";
      }
      else {
        player_html += '<li class="li-win">' + players[i]["nickname"] + "</li>";
      }
    }
    document.getElementById("player_list").innerHTML = player_html;
    // rounds
    round_html = '';
    for ( i = 1; i <= current_round; i++ ) {
      for ( j = 0; j < players.length; j++ ) {
        if ( players[j]["extra"].indexOf( i + "." ) != -1 ) {
          if ( players[j]["user_id"] == my_id ) {
            round_html += '<li class="li-win">Round ' + i + "</li>";
          }
          else {
            round_html += '<li class="li-lose">Round ' + i + ': ' + players[j]['nickname'] + "</li>";
          }
        }
        else {
        }
        // scores
        if ( players[j]["user_id"] == my_id ) {
          scores_pos = players[j]["extra"].indexOf( "|" );
          if ( scores_pos != -1 ) {
            items = players[j]["extra"].substr( scores_pos + 1 ).split( "," );
            add_scores( items[0], items[1], items[2], items[3] );
          }
        }
      }
    }
    document.getElementById( 'rounds_won' ).innerHTML = round_html;
    // chat
    chat_changed = false;
    for ( j = 0; j < players.length; j++ ) {
      if ( players[j]["chat"] != '' && players[j]["chat"] != chat[players[j]["user_id"]] ) {
        chat[players[j]["user_id"]] = players[j]["chat"];
        document.getElementById( "chat" ).innerHTML = "<li><b>" + players[j]["nickname"] + "</b>: " + players[j]["chat"] + "</li>" + document.getElementById( "chat" ).innerHTML;
        chat_changed = true;
      }
    }
    // delete last if over 10 items
    if ( chat_changed ) {
      element = document.getElementById( "chat" );
      while ( element.childNodes.length > 10 ) {
        element.removeChild( element.lastChild );
      }
      anim = new YAHOO.util.ColorAnim(element, { backgroundColor:{to:"#fff", from:"#ff9"}, duration:1});
      anim.animate();
    }
  }

  function timer_check_expired() {
    if ( playing ) {
      if ( !timer_ok ) {
        show_ss_progress( "Reconnecting..." );
        get_game_state("Reconnected." )();
      }
      else {
        timer_ok = false; // to be turned true by game response
      }
    }
  }
  
  get_game_state = function(response_message) { return function() {
      send_command( { "command": "state" }, false, gamestate_response(response_message) );
    }
  }

  // support for touch
  function touchHandler(event) {
    var touches = event.changedTouches,
        first = touches[0],
        type = "",
        simulatedEvent;

    switch ( event.type ) {
        case "touchstart": type = "mousedown"; break;
        case "touchmove":  type="mousemove"; break;        
        case "touchend":   type="mouseup"; break;
        default: return;
    }

    simulatedEvent = document.createEvent("MouseEvent");
    simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, 0/*left*/, null);
    first.target.dispatchEvent(simulatedEvent);
    if ( event.type == 'touchmove' ) {
      event.preventDefault();
    }
  }

  function touchEnable() {
    var container = document.getElementById( "game_box" );
    if ( container.addEventListener ) { // ignore ie 
      container.addEventListener("touchstart", touchHandler, true);
      container.addEventListener("touchmove", touchHandler, true);
      container.addEventListener("touchend", touchHandler, true);
      container.addEventListener("touchcancel", touchHandler, true);    
    }
  }

  function do_check_login() {
      touchEnable(); // TODO check ua
      send_command( { "command": "authenticate" }, "Checking login", check_login_response );
      if ( timer_check == null ) {
        timer_check = setInterval( timer_check_expired, 20000 ); // communicate every 20s
        show_ss_progress( "Starting..." );
      }
  }

  function give_up() {
      send_command( { "command": "giveup" }, "Giving up on round.", giveup_response );
  }

  function logout() {
      hide( "logout" );
      send_command( { "command": "logout" }, "Logging out", logout_response );
  }
    
  function newgame() {
      consecutive = 0;
      send_command( { "command": "newgame", "name": "" }, "Adding new game", newgame_response );
  }
  
  function start_game() {
    send_command( start_game_options, "Starting game.", startgame_response );
  }
  
  function leave_game() {
    send_command( { "command": "leavegame" }, "Leaving game.", leavegame_response );
  }

  function play_again() {
    send_command( { "command": "again" }, "Joining new game to play again...", again_response );
  }
  
  function send_chat() {
    send_command( { "command": "chat", "message": document.getElementById("chat_message").value }, "Sending chat message...", chat_response );
  }
  
  return {
    init: function() {
      // game list
      game_list_source.responseType = YAHOO.util.DataSource.TYPE_JSARRAY; 
      game_list_source.responseSchema = { fields: [ "id", "status", "players", "common", "private", "word_length", "lang", "nickname", "current_round" ]  };
      game_list = new YAHOO.widget.DataTable("game_list", game_list_defs, game_list_source, { MSG_EMPTY: "No games in progress." } );
      // buttons
      new YAHOO.widget.Button("giveup-button", { type: "link", onclick: { 'fn': give_up  } });
      new YAHOO.widget.Button("logout-button", { type: "link", onclick: { 'fn': logout  } });
      new YAHOO.widget.Button("play-button", { type: "link", onclick: { 'fn': newgame  } });
      new YAHOO.widget.Button("start-button", { type: "link", onclick: { 'fn': start_game  } });
      new YAHOO.widget.Button("leave-button", { type: "link", onclick: { 'fn': leave_game  } });
      new YAHOO.widget.Button("again-button", { type: "link", onclick: { 'fn': play_again  } });
      new YAHOO.widget.Button("send-button", { type: "link", onclick: { 'fn': send_chat  } });
      new YAHOO.widget.Button("help-button");
      // progress bar
      progress = new YAHOO.widget.ProgressBar({id:'game_counter', value:100});
      progress.get('anim').duration = 0;
      progress.render('game_counter');
      do_check_login();
    },
  
    check_login: function() {
      do_check_login();
    },

    login: function() {
      hide( "login" );
      hide( "welcome" );
      send_command( { "command": "login", "email": encodeURIComponent( document.getElementById("login_email").value ), "password": encodeURIComponent( document.getElementById("login_password").value ) }, "Logging in", login_response );
    },
  
    join_game: function(id) {
      consecutive = 0;
      send_command( { "command": "joingame", "id": id }, "Joining game " + id, joingame_response );
    },
  
    report_chat: function() {
      send_command( { "command": "chat_report" }, "Reporting this conversation...", chat_report_response );
    },
  
    move_grid: function( delta_x, delta_y ) {
      var i, element;
      // check possible
      for ( i = 0; i < grid.length; i++ ) {
        if ( grid[i].y != 0 ) {
          candidate_x = grid[i].x + delta_x;
          candidate_y = grid[i].y + delta_y;
          if ( candidate_x < 0 || candidate_x >= grid_width || candidate_y <= 0 || candidate_y >= grid_height ) {
            return; // fail
          }
        }
      }
      // move
      for ( i = 0; i < grid.length; i++ ) {
        if ( grid[i].y != 0 ) {
          grid[i].x += delta_x;
          grid[i].y += delta_y;
          element = document.getElementById( "letter_" + i );
          YAHOO.util.Dom.setXY( element, [ grid[i].x * letter_size + container_pos[0], grid[i].y * letter_size + container_pos[1] ] );
        }
      }
    },
  
    toggle_skill: function() {
      if ( start_game_options['skill'] == 0 ) {
        start_game_options['skill'] = 1;
        document.getElementById( 'game_skill' ).innerHTML = 'Same letters';
      }
      else {
        start_game_options['skill'] = 0;
        document.getElementById( 'game_skill' ).innerHTML = 'Different letters';
      }
    },
    
    toggle_private: function() {
      if ( start_game_options['private'] == 0 ) {
        start_game_options['private'] = 1;
        document.getElementById( 'game_private' ).innerHTML = 'Private';
      }
      else {
        start_game_options['private'] = 0;
        document.getElementById( 'game_private' ).innerHTML = 'Allow others';
      }
    },
    
    toggle_word_length: function() {
      if ( start_game_options['wordlength'] == 0 ) {
        start_game_options['wordlength'] = 3;
        document.getElementById( 'game_word_length' ).innerHTML = '3 letters+';
      }
      else {
        start_game_options['wordlength'] = 0;
        document.getElementById( 'game_word_length' ).innerHTML = 'Any word length';
      }
    },
    
    toggle_lang: function() {
      if ( start_game_options['lang'] == '' ) {
        start_game_options['lang'] = 'fr';
        document.getElementById( 'lang' ).innerHTML = 'French';
      }
      else {
        start_game_options['lang'] = '';
        document.getElementById( 'lang' ).innerHTML = 'English';
      }
    },
    
    log_columns: [ 
      { key:'created_at', label:'When', width:80 },
      { key:'detail', label:'Detail', width:500 }
    ],

    populate_grid: function( grid ) {
      var container = document.getElementById( "game_box" ),
        idx = 0,
        item, letter_pos, x, y, letter_element,
        container_pos = YAHOO.util.Dom.getXY("game_box");
      container.style.backgroundImage = "url('http://static.supernifty.com.au/images/grid-started.png')";
      for ( item in grid ) {
        letter_pos = item.split(",");
        x = parseInt( letter_pos[0] );
        y = parseInt( letter_pos[1] );
        letter_element = document.createElement("div");
        letter_element.setAttribute( "id", "letter_" + idx );
        letter_element.setAttribute( "class", "letter" );
        letter_element.setAttribute( "className", "letter" ); // ie hack
        letter_element.innerHTML = grid[item];
        container.appendChild(letter_element);
        YAHOO.util.Dom.setXY( letter_element, [ x * letter_size + container_pos[0], y * letter_size + container_pos[1] ] );
        idx++;
      }
    }
  }
} // speed_scrabble ns

function openid_auth( who ) {
        var url = '',
          submit = false,
          manual = false,
          username = false;
        if ( who == 'google' ) {
          url = 'https://www.google.com/accounts/o8/id';
          submit = true;
        }
        else if ( who == 'yahoo' ) {
          url = 'http://yahoo.com/';
          submit = true;
        }
        else if ( who == 'openid' ) {
          url = 'http://';
          manual = true;
        }
        else if ( who == 'aol' ) {
          url = 'http://openid.aol.com/{username}';
          username = true;
        }
    
        if ( manual ) {
          document.getElementById( 'openid_manual' ).style.display = "block";
        }
        else {
          document.getElementById( 'openid_manual' ).style.display = "none";
        }
        if ( username ) {
          document.getElementById( 'openid_username_div' ).style.display = "block";
        }
        else {
          document.getElementById( 'openid_username_div' ).style.display = "none";
        }
        document.getElementById( 'openid_provider' ).value = url;
        if ( submit ) {
          document.getElementById( 'openid_form' ).submit();
        }
};

