const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const url = require('url');
const objectsModule = require('./objects');

const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({
  server
});

const connections = new Set();
var playerCount = 0;
var players = []

const NAME_MAXLEN = 25;

app.get('/', (req, res) => {
  res.send('Grand Tabernacle Auto VI');
});

function norm(x1, x2, y1, y2) {
  return Math.sqrt((x1 - x2) ** 2 + (y1 + y2) ** 2);
}

function circleCollide(x, y) {
  let colliding = false;
  objectsModule.objects.circles.forEach((circle) => {
    if (norm(x, circle.x, y, circle.y) <= circle.r) {
      colliding = true;
    }
  })
  return colliding;
}

function squareCollide(x, y) {
  let colliding = false;
  objectsModule.objects.squares.forEach((square) => {
    if (square.x <= x && x <= square.x + square.w && square.y <= y && y <= square.y + square.h) {
      colliding = true;
    }
  })
  return colliding;
}

function generatePosition() {
  let mapWidth = 1000;
  let mapHeight = 1000;
  let count = 0;

  let x = Math.floor(Math.random() * mapWidth);
  let y = Math.floor(Math.random() * mapHeight);

  while ((circleCollide(x, y) || squareCollide(x, y))) {
    x = Math.floor(Math.random() * mapWidth);
    y = Math.floor(Math.random() * mapHeight);
    count++;
    if (count >= 1000) {
      break;
    }
  }
  return [x, y];
}

function createNewPlayer(socket, name) {
  let pos = generatePosition();
  var obj = {
    id: playerCount,
    x: pos[0],
    y: pos[1],
    dir: 0,
    visibleDir: 0,
    name: name
  }
  players.push(obj);
  playerCount++;

  socket.send(JSON.stringify({
    type: "connect",
    data: {
      playerId: obj.id,
      players: players
    }
  }))

  broadcast(JSON.stringify({
    type: "newplayer",
    data: obj
  }), obj.id);

  socket.id = obj.id;
  connections.add(socket);
}

wss.on('connection', (socket, req) => {
  //create new player, send informations to new player and broadcast new player for all
  let username = url.parse(req.url, true).query.name;
  if (username === undefined || username == null || username == "null") {
    username = "Soldat Inconnu"
  }
  if (username.length > NAME_MAXLEN) {
    username = username.substring(0, NAME_MAXLEN);
  }
  createNewPlayer(socket, username);

  socket.on('message', (message) => {
    message = JSON.parse(message);
    switch (message.type) {
      case 'ping':
        socket.send("pong");
        break;

      case "update":
        for (var i = players.length - 1; i >= 0; i--) {
          if (players[i].id == message.data.id) {
            players[i] = message.data;
          }
        }
        broadcast(JSON.stringify(message), socket.id);
        break;

      case "newBullet":
        broadcast(JSON.stringify(message), socket.id);
        break;

      case "died":
        broadcast(JSON.stringify(message), socket.id);
        for (var i = players.length - 1; i >= 0; i--) {

          if (players[i].id == message.data.id) {
            let pos = generatePosition();
            players[i].x = pos[0];
            players[i].y = pos[1];

            broadcast(JSON.stringify({
              type: "update",
              data: players[i]
            }))
          }
        }
        break;

      default:
        break;
    }
  });

  socket.on('close', () => {
    for (var i = players.length - 1; i >= 0; i--) {
      broadcast(JSON.stringify({
        type: "removePlayer",
        data: {
          id: socket.id
        }
      }));
      if (players[i].id == socket.id) {
        players.splice(i, 1);
      }
    }
    connections.delete(socket);
  });
});

function broadcast(message, exceptId = -1) {
  connections.forEach((socket) => {
    if (socket.readyState === WebSocket.OPEN && socket.id != exceptId) {
      socket.send(message);
    }
  });
}

const PORT = 8080;

server.listen(PORT, () => {
  console.log(`Serveur WebSocket écoutant sur le port ${PORT}`);
});