text: var io = require('socket.io-client');
  var ChatClient = require('./chat-client');
  var Canvas = require('./canvas');
  var global = require('./global');
  
  var playerNameInput = document.getElementById('playerNameInput');
  var socket;
  var reason;
  
  var debug = function(args) {
      if (console && console.log) {
          console.log(args);
      }
  };
  
  if ( /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) ) {
      global.mobile = true;
  }
  
  function startGame(type) {
      global.playerName = playerNameInput.value.replace(/(<([^>]+)>)/ig, '').substring(0,25);
      global.playerType = type;
  
      global.screenWidth = window.innerWidth;
      global.screenHeight = window.innerHeight;
  
      document.getElementById('startMenuWrapper').style.maxHeight = '0px';
      document.getElementById('gameAreaWrapper').style.opacity = 1;
      if (!socket) {
          socket = io({query:"type=" + type});
          setupSocket(socket);
      }
      if (!global.animLoopHandle)
          animloop();
      socket.emit('respawn');
      window.chat.socket = socket;
      window.chat.registerFunctions();
      window.canvas.socket = socket;
      global.socket = socket;
  }
  
  // Checks if the nick chosen contains valid alphanumeric characters (and underscores).
  function validNick() {
      var regex = /^\w*$/;
      debug('Regex Test', regex.exec(playerNameInput.value));
      return regex.exec(playerNameInput.value) !== null;
  }
  
  window.onload = function() {
  
      var btn = document.getElementById('startButton'),
          btnS = document.getElementById('spectateButton'),
          nickErrorText = document.querySelector('#startMenu .input-error');
  
      btnS.onclick = function () {
          startGame('spectate');
      };
  
      btn.onclick = function () {
  
          // Checks if the nick is valid.
          if (validNick()) {
              nickErrorText.style.opacity = 0;
              startGame('player');
          } else {
              nickErrorText.style.opacity = 1;
          }
      };
  
      var settingsMenu = document.getElementById('settingsButton');
      var settings = document.getElementById('settings');
      var instructions = document.getElementById('instructions');
  
      settingsMenu.onclick = function () {
          if (settings.style.maxHeight == '300px') {
              settings.style.maxHeight = '0px';
          } else {
              settings.style.maxHeight = '300px';
          }
      };
  
      playerNameInput.addEventListener('keypress', function (e) {
          var key = e.which || e.keyCode;
  
          if (key === global.KEY_ENTER) {
              if (validNick()) {
                  nickErrorText.style.opacity = 0;
                  startGame('player');
              } else {
                  nickErrorText.style.opacity = 1;
              }
          }
      });
  };
  
  // TODO: Break out into GameControls.
  
  var foodConfig = {
      border: 0,
  };
  
  var playerConfig = {
      border: 6,
      textColor: '#FFFFFF',
      textBorder: '#000000',
      textBorderSize: 3,
      defaultSize: 30
  };
  
  var player = {
      id: -1,
      x: global.screenWidth / 2,
      y: global.screenHeight / 2,
      screenWidth: global.screenWidth,
      screenHeight: global.screenHeight,
      target: {x: global.screenWidth / 2, y: global.screenHeight / 2}
  };
  global.player = player;
  
  var foods = [];
  var viruses = [];
  var fireFood = [];
  var users = [];
  var leaderboard = [];
  var target = {x: player.x, y: player.y};
  global.target = target;
  
  window.canvas = new Canvas();
  window.chat = new ChatClient();
  
  var visibleBorderSetting = document.getElementById('visBord');
  visibleBorderSetting.onchange = settings.toggleBorder;
  
  var showMassSetting = document.getElementById('showMass');
  showMassSetting.onchange = settings.toggleMass;
  
  var continuitySetting = document.getElementById('continuity');
  continuitySetting.onchange = settings.toggleContinuity;
  
  var roundFoodSetting = document.getElementById('roundFood');
  roundFoodSetting.onchange = settings.toggleRoundFood;
  
  var c = window.canvas.cv;
  var graph = c.getContext('2d');
  
  $( "#feed" ).click(function() {
      socket.emit('1');
      window.canvas.reenviar = false;
  });
  
  $( "#split" ).click(function() {
      socket.emit('2');
      window.canvas.reenviar = false;
  });
  
  // socket stuff.
  function setupSocket(socket) {
      // Handle ping.
      socket.on('pongcheck', function () {
          var latency = Date.now() - global.startPingTime;
          debug('Latency: ' + latency + 'ms');
          window.chat.addSystemLine('Ping: ' + latency + 'ms');
      });
  
      // Handle error.
      socket.on('connect_failed', function () {
          socket.close();
          global.disconnected = true;
      });
  
      socket.on('disconnect', function () {
          socket.close();
          global.disconnected = true;
      });
  
      // Handle connection.
      socket.on('welcome', function (playerSettings) {
          player = playerSettings;
          player.name = global.playerName;
          player.screenWidth = global.screenWidth;
          player.screenHeight = global.screenHeight;
          player.target = window.canvas.target;
          global.player = player;
          window.chat.player = player;
          socket.emit('gotit', player);
          global.gameStart = true;
          debug('Game started at: ' + global.gameStart);
          window.chat.addSystemLine('Conectado al juego!');
          window.chat.addSystemLine('çescribe <b>-help</b> para listar los comandos.');
          if (global.mobile) {
              document.getElementById('gameAreaWrapper').removeChild(document.getElementById('chatbox'));
          }
  		c.focus();
      });
  
      socket.on('gameSetup', function(data) {
          global.gameWidth = data.gameWidth;
          global.gameHeight = data.gameHeight;
          resize();
      });
  
      socket.on('playerDied', function (data) {
          window.chat.addSystemLine('{Jugador} - <b>' + (data.name.length < 1 ? 'Sin nombre' : data.name) + '</b> ha sido comido');
      });
  
      socket.on('playerDisconnect', function (data) {
          window.chat.addSystemLine('{Jugador} - <b>' + (data.name.length < 1 ? 'Sin nombre' : data.name) + '</b> se ha desconectado.');
      });
  
      socket.on('playerJoin', function (data) {
          window.chat.addSystemLine('{Jugador} - <b>' + (data.name.length < 1 ? 'Sin nombre' : data.name) + '</b> ha ingresado.');
      });
  
      socket.on('leaderboard', function (data) {
          leaderboard = data.leaderboard;
          var status = '<span class="title">Leaderboard</span>';
          for (var i = 0; i < leaderboard.length; i++) {
              status += '<br />';
              if (leaderboard[i].id == player.id){
                  if(leaderboard[i].name.length !== 0)
                      status += '<span class="me">' + (i + 1) + '. ' + leaderboard[i].name + "</span>";
                  else
                      status += '<span class="me">' + (i + 1) + ". Sin nombre</span>";
              } else {
                  if(leaderboard[i].name.length !== 0)
                      status += (i + 1) + '. ' + leaderboard[i].name;
                  else
                      status += (i + 1) + '. Sin nombre';
              }
          }
          //status += '<br />Players: ' + data.players;
          document.getElementById('status').innerHTML = status;
      });
  
      socket.on('serverMSG', function (data) {
          window.chat.addSystemLine(data);
      });
  
      // Chat.
      socket.on('serverSendPlayerChat', function (data) {
          window.chat.addChatLine(data.sender, data.message, false);
      });
  
      // Handle movement.
      socket.on('serverTellPlayerMove', function (userData, foodsList, massList, virusList) {
          var playerData;
          for(var i =0; i< userData.length; i++) {
              if(typeof(userData[i].id) == "undefined") {
                  playerData = userData[i];
                  i = userData.length;
              }
          }
          if(global.playerType == 'player') {
              var xoffset = player.x - playerData.x;
              var yoffset = player.y - playerData.y;
  
              player.x = playerData.x;
              player.y = playerData.y;
              player.hue = playerData.hue;
              player.massTotal = playerData.massTotal;
              player.cells = playerData.cells;
              player.xoffset = isNaN(xoffset) ? 0 : xoffset;
              player.yoffset = isNaN(yoffset) ? 0 : yoffset;
          }
          users = userData;
          foods = foodsList;
          viruses = virusList;
          fireFood = massList;
      });
  
      // Death.
      socket.on('RIP', function () {
          global.gameStart = false;
          global.died = true;
          window.setTimeout(function() {
              document.getElementById('gameAreaWrapper').style.opacity = 0;
              document.getElementById('startMenuWrapper').style.maxHeight = '1000px';
              global.died = false;
              if (global.animLoopHandle) {
                  window.cancelAnimationFrame(global.animLoopHandle);
                  global.animLoopHandle = undefined;
              }
          }, 2500);
      });
  
      socket.on('kick', function (data) {
          global.gameStart = false;
          reason = data;
          global.kicked = true;
          socket.close();
      });
  
      socket.on('virusSplit', function (virusCell) {
          socket.emit('2', virusCell);
          reenviar = false;
      });
  }
  
  function drawCircle(centerX, centerY, radius, sides) {
      var theta = 0;
      var x = 0;
      var y = 0;
  
      graph.beginPath();
  
      for (var i = 0; i < sides; i++) {
          theta = (i / sides) * 2 * Math.PI;
          x = centerX + radius * Math.sin(theta);
          y = centerY + radius * Math.cos(theta);
          graph.lineTo(x, y);
      }
  
      graph.closePath();
      graph.stroke();
      graph.fill();
  }
  
  function drawFood(food) {
      graph.strokeStyle = 'hsl(' + food.hue + ', 100%, 45%)';
      graph.fillStyle = 'hsl(' + food.hue + ', 100%, 50%)';
      graph.lineWidth = foodConfig.border;
      drawCircle(food.x - player.x + global.screenWidth / 2,
                 food.y - player.y + global.screenHeight / 2,
                 food.radius, global.foodSides);
  }
  
  function drawVirus(virus) {
      graph.strokeStyle = virus.stroke;
      graph.fillStyle = virus.fill;
      graph.lineWidth = virus.strokeWidth;
      drawCircle(virus.x - player.x + global.screenWidth / 2,
                 virus.y - player.y + global.screenHeight / 2,
                 virus.radius, global.virusSides);
  }
  
  function drawFireFood(mass) {
      graph.strokeStyle = 'hsl(' + mass.hue + ', 100%, 45%)';
      graph.fillStyle = 'hsl(' + mass.hue + ', 100%, 50%)';
      graph.lineWidth = playerConfig.border+10;
      drawCircle(mass.x - player.x + global.screenWidth / 2,
                 mass.y - player.y + global.screenHeight / 2,
                 mass.radius-5, 18 + (~~(mass.masa/5)));
  }
  
  function drawPlayers(order) {
      var start = {
          x: player.x - (global.screenWidth / 2),
          y: player.y - (global.screenHeight / 2)
      };
  
      for(var z=0; z<order.length; z++)
      {
          var userCurrent = users[order[z].nCell];
          var cellCurrent = users[order[z].nCell].cells[order[z].nDiv];
  
          var x=0;
          var y=0;
  
          var points = 30 + ~~(cellCurrent.mass/5);
          var increase = Math.PI * 2 / points;
  
          graph.strokeStyle = 'hsl(' + userCurrent.hue + ', 100%, 45%)';
          graph.fillStyle = 'hsl(' + userCurrent.hue + ', 100%, 50%)';
          graph.lineWidth = playerConfig.border;
  
          var xstore = [];
          var ystore = [];
  
          global.spin += 0.0;
  
          var circle = {
              x: cellCurrent.x - start.x,
              y: cellCurrent.y - start.y
          };
  
          for (var i = 0; i < points; i++) {
  
              x = cellCurrent.radius * Math.cos(global.spin) + circle.x;
              y = cellCurrent.radius * Math.sin(global.spin) + circle.y;
              if(typeof(userCurrent.id) == "undefined") {
                  x = valueInRange(-userCurrent.x + global.screenWidth / 2,
                                   global.gameWidth - userCurrent.x + global.screenWidth / 2, x);
                  y = valueInRange(-userCurrent.y + global.screenHeight / 2,
                                   global.gameHeight - userCurrent.y + global.screenHeight / 2, y);
              } else {
                  x = valueInRange(-cellCurrent.x - player.x + global.screenWidth / 2 + (cellCurrent.radius/3),
                                   global.gameWidth - cellCurrent.x + global.gameWidth - player.x + global.screenWidth / 2 - (cellCurrent.radius/3), x);
                  y = valueInRange(-cellCurrent.y - player.y + global.screenHeight / 2 + (cellCurrent.radius/3),
                                   global.gameHeight - cellCurrent.y + global.gameHeight - player.y + global.screenHeight / 2 - (cellCurrent.radius/3) , y);
              }
              global.spin += increase;
              xstore[i] = x;
              ystore[i] = y;
          }
          /*if (wiggle >= player.radius/ 3) inc = -1;
          *if (wiggle <= player.radius / -3) inc = +1;
          *wiggle += inc;
          */
          for (i = 0; i < points; ++i) {
              if (i === 0) {
                  graph.beginPath();
                  graph.moveTo(xstore[i], ystore[i]);
              } else if (i > 0 && i < points - 1) {
                  graph.lineTo(xstore[i], ystore[i]);
              } else {
                  graph.lineTo(xstore[i], ystore[i]);
                  graph.lineTo(xstore[0], ystore[0]);
              }
  
          }
          graph.lineJoin = 'round';
          graph.lineCap = 'round';
          graph.fill();
          graph.stroke();
          var nameCell = "";
          if(typeof(userCurrent.id) == "undefined")
              nameCell = player.name;
          else
              nameCell = userCurrent.name;
  
          var fontSize = Math.max(cellCurrent.radius / 3, 12);
          graph.lineWidth = playerConfig.textBorderSize;
          graph.fillStyle = playerConfig.textColor;
          graph.strokeStyle = playerConfig.textBorder;
          graph.miterLimit = 1;
          graph.lineJoin = 'round';
          graph.textAlign = 'center';
          graph.textBaseline = 'middle';
          graph.font = 'bold ' + fontSize + 'px sans-serif';
  
          if (global.toggleMassState === 0) {
              graph.strokeText(nameCell, circle.x, circle.y);
              graph.fillText(nameCell, circle.x, circle.y);
          } else {
              graph.strokeText(nameCell, circle.x, circle.y);
              graph.fillText(nameCell, circle.x, circle.y);
              graph.font = 'bold ' + Math.max(fontSize / 3 * 2, 10) + 'px sans-serif';
              if(nameCell.length === 0) fontSize = 0;
              graph.strokeText(Math.round(cellCurrent.mass), circle.x, circle.y+fontSize);
              graph.fillText(Math.round(cellCurrent.mass), circle.x, circle.y+fontSize);
          }
      }
  }
  
  function valueInRange(min, max, value) {
      return Math.min(max, Math.max(min, value));
  }
  
  function drawgrid() {
       graph.lineWidth = 1;
       graph.strokeStyle = global.lineColor;
       graph.globalAlpha = 0.15;
       graph.beginPath();
  
      for (var x = global.xoffset - player.x; x < global.screenWidth; x += global.screenHeight / 18) {
          graph.moveTo(x, 0);
          graph.lineTo(x, global.screenHeight);
      }
  
      for (var y = global.yoffset - player.y ; y < global.screenHeight; y += global.screenHeight / 18) {
          graph.moveTo(0, y);
          graph.lineTo(global.screenWidth, y);
      }
  
      graph.stroke();
      graph.globalAlpha = 1;
  }
  
  function drawborder() {
      graph.lineWidth = 1;
      graph.strokeStyle = playerConfig.borderColor;
  
      // Left-vertical.
      if (player.x <= global.screenWidth/2) {
          graph.beginPath();
          graph.moveTo(global.screenWidth/2 - player.x, 0 ? player.y > global.screenHeight/2 : global.screenHeight/2 - player.y);
          graph.lineTo(global.screenWidth/2 - player.x, global.gameHeight + global.screenHeight/2 - player.y);
          graph.strokeStyle = global.lineColor;
          graph.stroke();
      }
  
      // Top-horizontal.
      if (player.y <= global.screenHeight/2) {
          graph.beginPath();
          graph.moveTo(0 ? player.x > global.screenWidth/2 : global.screenWidth/2 - player.x, global.screenHeight/2 - player.y);
          graph.lineTo(global.gameWidth + global.screenWidth/2 - player.x, global.screenHeight/2 - player.y);
          graph.strokeStyle = global.lineColor;
          graph.stroke();
      }
  
      // Right-vertical.
      if (global.gameWidth - player.x <= global.screenWidth/2) {
          graph.beginPath();
          graph.moveTo(global.gameWidth + global.screenWidth/2 - player.x,
                       global.screenHeight/2 - player.y);
          graph.lineTo(global.gameWidth + global.screenWidth/2 - player.x,
                       global.gameHeight + global.screenHeight/2 - player.y);
          graph.strokeStyle = global.lineColor;
          graph.stroke();
      }
  
      // Bottom-horizontal.
      if (global.gameHeight - player.y <= global.screenHeight/2) {
          graph.beginPath();
          graph.moveTo(global.gameWidth + global.screenWidth/2 - player.x,
                       global.gameHeight + global.screenHeight/2 - player.y);
          graph.lineTo(global.screenWidth/2 - player.x,
                       global.gameHeight + global.screenHeight/2 - player.y);
          graph.strokeStyle = global.lineColor;
          graph.stroke();
      }
  }
  
  window.requestAnimFrame = (function() {
      return  window.requestAnimationFrame       ||
              window.webkitRequestAnimationFrame ||
              window.mozRequestAnimationFrame    ||
              window.msRequestAnimationFrame     ||
              function( callback ) {
                  window.setTimeout(callback, 1000 / 60);
              };
  })();
  
  window.cancelAnimFrame = (function(handle) {
      return  window.cancelAnimationFrame     ||
              window.mozCancelAnimationFrame;
  })();
  
  function animloop() {
      global.animLoopHandle = window.requestAnimFrame(animloop);
      gameLoop();
  }
  
  function gameLoop() {
      if (global.died) {
          graph.fillStyle = '#333333';
          graph.fillRect(0, 0, global.screenWidth, global.screenHeight);
  
          graph.textAlign = 'center';
          graph.fillStyle = '#FFFFFF';
          graph.font = 'bold 30px sans-serif';
          graph.fillText('You died!', global.screenWidth / 2, global.screenHeight / 2);
      }
      else if (!global.disconnected) {
          if (global.gameStart) {
              graph.fillStyle = global.backgroundColor;
              graph.fillRect(0, 0, global.screenWidth, global.screenHeight);
  
              drawgrid();
              foods.forEach(drawFood);
              fireFood.forEach(drawFireFood);
              viruses.forEach(drawVirus);
  
              if (global.borderDraw) {
                  drawborder();
              }
              var orderMass = [];
              for(var i=0; i<users.length; i++) {
                  for(var j=0; j<users[i].cells.length; j++) {
                      orderMass.push({
                          nCell: i,
                          nDiv: j,
                          mass: users[i].cells[j].mass
                      });
                  }
              }
              orderMass.sort(function(obj1, obj2) {
                  return obj1.mass - obj2.mass;
              });
  
              drawPlayers(orderMass);
              socket.emit('0', window.canvas.target); // playerSendTarget "Heartbeat".
  
          } else {
              graph.fillStyle = '#333333';
              graph.fillRect(0, 0, global.screenWidth, global.screenHeight);
  
              graph.textAlign = 'center';
              graph.fillStyle = '#FFFFFF';
              graph.font = 'bold 30px sans-serif';
              graph.fillText('Game Over!', global.screenWidth / 2, global.screenHeight / 2);
          }
      } else {
          graph.fillStyle = '#333333';
          graph.fillRect(0, 0, global.screenWidth, global.screenHeight);
  
          graph.textAlign = 'center';
          graph.fillStyle = '#FFFFFF';
          graph.font = 'bold 30px sans-serif';
          if (global.kicked) {
              if (reason !== '') {
                  graph.fillText('Fuiste expulsado por:', global.screenWidth / 2, global.screenHeight / 2 - 20);
                  graph.fillText(reason, global.screenWidth / 2, global.screenHeight / 2 + 20);
              }
              else {
                  graph.fillText('Fuiste expulsado!', global.screenWidth / 2, global.screenHeight / 2);
              }
          }
          else {
                graph.fillText('Descpmectadp!', global.screenWidth / 2, global.screenHeight / 2);
          }
      }
  }
  
  window.addEventListener('resize', resize);
  
  function resize() {
      if (!socket) return;
  
      player.screenWidth = c.width = global.screenWidth = global.playerType == 'player' ? window.innerWidth : global.gameWidth;
      player.screenHeight = c.height = global.screenHeight = global.playerType == 'player' ? window.innerHeight : global.gameHeight;
  
      if (global.playerType == 'spectate') {
          player.x = global.gameWidth / 2;
          player.y = global.gameHeight / 2;
      }
  
      socket.emit('windowResized', { screenWidth: global.screenWidth, screenHeight: global.screenHeight });
  }
  