var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res){
  res.sendFile(__dirname + '/index.html');
});

/** Parameters **/
var clients = [],  // List of all conected clients
    players = [],  // Store Player 1 and Player 2 in Array {Player1 == players[0]}
    gameState = 0, // Game-Controler Variable
    width, height, // Final Width + Height of Game-Window
    startinterval, // Interval for Counting down (cancelable)
    calcPos,
    score = [0,0];

/** Game-Config **/
var cube_width = 50,
    cube_height = 200,
    cube_speed = 7; // Depending on 'tickrate' [cube_speed*tickrate == acutal Speed]

/** Networking-Config **/
var tickrate = 20;

/** Ball-Config **/
var ball_size = 15,
    ball_speed = 15,
    ballX = cube_width+ball_size,
    ballY = cube_height/2,
    ball_owner = 1,
    ball_VX = 0,
    ball_VY = 0,
    ball_maxAngle = 5*Math.PI/12;

/** ########## **/
/** CONNECTION **/
/** ########## **/
io.on('connection', function(socket){
  /** Set ID for User **/
  var id = clients.length + 1;
  if(clients.length == 1)
    if(clients[0].id == 2) id = 1;
  var clientkey = clients.push(new Client(id, socket)) - 1;
  console.log('Client connected. ID=' + id);

  /** ######### **/
  /** HANDSHAKE **/
  /** ######### **/
  socket.emit('handshake', id, gameState, cube_width, cube_height, cube_speed, score, function(w, h){ // Callback function for 1st Client to set screen size //
    clients[clientkey].width = w;
    clients[clientkey].height = h;
    if(clients.length > 1 && gameState == 0) setUp(socket);
  });

if(gameState > 0) socket.emit('size', width, height);
  /** #### **/
  /** Ping **/
  /** ### **/
  socket.on('appping', function(fn){
    fn(Date.now());
  });

  /** ######### **/
  /** Ballshoot **/
  /** ######### **/
  socket.on('ballshoot', function(y){
    console.log('### Ball shoot event ###');
    if(ball_owner == 1){
      ball_VX = ball_speed;
      console.log('owner: left  - at:' + players[0].y);

    }
    else if(ball_owner == 2){
      ball_VX = -ball_speed;
      console.log('owner: right  - at:' + players[1].y);
    }
    ball_owner = 0;
  });

  /** ################ **/
  /** Paddel V-Change **/
  /** ############## **/
  socket.on('vchange', function(pos, val, y, timestamp){
    var timediff = Date.now()-timestamp;



    // console.log('INTERPOLATING...');

    // var diff = ((players[pos-1].speed/tickrate)*timediff);
    // if(players[pos-1].vY == 0){ // Lag from Sender (accel)
    //   if(val > 0)
    //     players[pos-1].y -= diff;
    //   if(val < 0)
    //     players[pos-1].y += diff;
    // }
    // if(players[pos-1].vY == 1){
    //   if(val == 0)
    //     players[pos-1].y += diff;
    // }
    // if(players[pos-1].vY == -1){
    //   if(val == 0)
    //     players[pos-1].y -= diff;
    // }

    players[pos-1].vY = val;

    // console.log('Diff:' + diff);
    // console.log('Got: ' + y + ' @ ' + timestamp);
    // console.log('Expected:' + players[pos-1].y + ' @ ' + Date.now());
    // socket.emit('changeV', pos, val, players[pos-1].y);
    // socket.broadcast.emit('changeV', pos, val, players[pos-1].y);
  });

  /** ########## **/
  /** DISCONNECT **/
  /** ########## **/
  socket.on('disconnect', function(){
    var index;
    clients.forEach(function(sock, ind){
      if (sock.socket == socket){
          index = ind;
          return;
      }
    });
    var oldid = clients[index].id;
    clients.splice(index, 1);
    console.log('Client disconnected. ID=' + (oldid+1));
    if(clients.length == 1){
      clients[0].socket.emit('debug', 'player_left'); //TODO win-event
      console.log('Only one Player left. Sending win-event');
      // Debug Restart:
      gameState = 0;
      clearInterval(startinterval);
      broadcastGameState(socket);
      return;
    }
    else if(clients.length == 0){
      console.log('All Players left. Reseting lobby...');
      gameState = 0;
      id = 0;
      return;
    }

    var key = 0;
    if(clients[0].id == 1 || clients[0].id == 2) key = 1;
    switch(oldid){
      case 1 : clients[key].id = 1; clients[key].socket.emit('handshake', 1); console.log('New Player 1 selected.'); setUp(); break;
      case 2 : clients[key].id = 2; clients[key].socket.emit('handshake', 2); console.log('New Player 2 selected.'); setUp(); break;
      default : console.log('Spectator left. Nothing need to select new Players.'); break;

    }
  });
});


http.listen(3000, function(){
  console.log('listening on *:3000');
});


/** Sync all settings to client and start the game **/
function setUp(socket){
  gameState = 1;
  broadcastGameState(socket);
  console.log('Starting SetUp...');
  /** Select smalest screensize and set for all clients **/
  clients.forEach(function(sock){
    broadcastGameState(socket);
    if(sock.id == 1){
      players[0] = new Player(sock.id, sock.socket);
      clients.forEach(function(sock2){
        if(sock2.id == 2){
          players[1] = new Player(sock2.id, sock2.socket);
          if(sock.width < sock2.width)
            width = sock.width;
          else
            width = sock2.width;
          if(sock.height < sock2.height)
            height = sock.height;
          else
            height = sock2.height;
          }
      });
    }
  });
  if(calcPos == null) // Start tick Server only once
    tickServer();
  socket.emit('size', width, height);
  socket.broadcast.emit('size', width, height);
  var i = 5;
  startinterval = setInterval(function(){
    i--;
    socket.emit('countdown', i);
    socket.broadcast.emit('countdown', i);
    if(i == 0){
      resetPlayers();
      clearInterval(startinterval);
      gameState = 2;
      broadcastGameState(socket);
      socket.emit('gamestate');
      socket.broadcast.emit('gamestate');
    }
  }, 1000);
}

/** Broadcast GameState to all clients **/
function broadcastGameState(socket){
  socket.emit('gamestate', gameState);
  socket.broadcast.emit('gamestate', gameState);
}

function tickServer(){
  calcPos = setInterval(function(){
    calculatePlayerPosition();
    calculateBallPosition();
  }, tickrate);
}

function calculatePlayerPosition(){
  players.forEach(function(player){
    player.y -= player.vY*player.speed;
    if(player.y > height-player.height || player.y < 0) player.vY = 0;
    if(player.y < 0) player.y = 0;
    if(player.y > height-player.height) player.y = height-player.height;
    player.socket.emit('paddlepos', player.id, player.vY, player.y);
    player.socket.broadcast.emit('paddlepos', player.id, player.vY, player.y);
  });
}

function calculateBallPosition(){

  /** Move Ball with Paddle if owned by owner (@roundstart) **/
  if(ball_owner == 1){
    ballX = cube_width+ball_size,
    ballY = players[0].y+cube_height/2;
  }
  else if(ball_owner == 2){
    ballX = width-cube_width-ball_size,
    ballY = players[1].y+cube_height/2;
  }

  /** ############ **/
  /** Ball Physics **/
  /** ############ **/

  ballX += ball_VX;
  ballY += ball_VY;

  /** Collide with right Paddle  or get point and respawn **/
  if(ballX > width - cube_width){
    if(ballY < players[1].y || ballY > players[1].y+cube_height){
      console.log('Point for Left');
      changeScore(1, score[0]+1);
      ball_VX = 0;
      ball_VY = 0;
      ball_owner = 1;
    } else {
      var intersect = ((players[1].y+cube_height/2)-ballY)/(cube_height/2);
      console.log('Intersect with right at:');
      console.log(intersect);
      console.log('###########################');
      ball_VX = -ball_speed*Math.cos(intersect*ball_maxAngle);
      ball_VY = -ball_speed*Math.sin(intersect*ball_maxAngle);
    }
  }

  /** Collide with left Paddle  or get point and respawn **/
  if(ballX < cube_width){
    if(ballY < players[0].y || ballY > players[0].y+cube_height){
      console.log('Point for Right');
      changeScore(2, score[1]+1);
      ball_VX = 0;
      ball_VY = 0;
      ball_owner = 2;
    } else {
      var intersect = ((players[0].y+cube_height/2)-ballY)/(cube_height/2);
      console.log('Intersect with left at:');
      console.log(intersect);
      console.log('###########################');
      ball_VX = ball_speed*Math.cos(intersect*ball_maxAngle);
      ball_VY = ball_speed*Math.sin(intersect*ball_maxAngle);
    }
  }

  /** Collide with walls **/
  if(ballY <= 0 || ballY >= height-ball_size){
    ball_VY = -ball_VY;
    console.log(ballX + ' : ' + ballY);
  }

  var data = {
    x     : ballX,
    y     : ballY,
    vx    : ball_VX,
    vy    : ball_VY,
    speed : ball_speed,
    owner : ball_owner
  };
  players[0].socket.emit('ballpos', data);
  players[0].socket.broadcast.emit('ballpos', data);
}

function changeScore(id, val){
  score[id-1] = val;
  players[0].socket.emit('score', score);
  players[0].socket.broadcast.emit('score', score);
}

function resetPlayers(){
  players.forEach(function(player){
    player.speed = cube_speed;
    player.width = cube_width;
    player.height = cube_height;
    player.y = 0;
    player.vY = 0;
    changeScore(player.id-1, 0);
  });
}

function resetServer(){
  // TODO
}

function Client(id, socket){
  this.id = id;
  this.socket = socket;
  this.width = 0;
  this.height = 0;
}

function Player(id, socket){
  this.id = id;
  this.socket = socket;
  this.speed = cube_speed;
  this.width = cube_width;
  this.height = cube_height;
  this.y = 0;
  this.vY = 0;
}
