/*
 * Copyright (C) 2021 Thomas Van Acker
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 *
 * This project is hosted on https://git.bitscuit.be/bitscuit/Scrawl
 *
*/

// Imports
const http = require("http");
const url = require("url");
const fs = require("fs");
const util = require("util");

// Vars
const hostname = "localhost"; // Enter your local IP address here
const port = 5000; // Enter a port number here

const STATE_LOBBY = 0;
const STATE_MANUAL = 10;
const STATE_WRITE = 20;
const STATE_PICK = 30;
const STATE_RESULT = 40;
const STATE_END = 50;
var state = STATE_LOBBY;

var userSockets = [];
var userNames = [];
var userOK = [];

var lines = [];
var lineNr = 0; // Current line
var linesTotal = 10; // Total nr of lines before game ends
var submissions = [];

var userWrite = [];
var userPick = [];
var userPoints = [];



// Create HTTP server
const server = http.createServer((req, res) => {
	// Parse url
	const requrl = url.parse(req.url);
	var reqpath = requrl.pathname;
	if(reqpath == "/") reqpath = "/index.html";

	// Respond to requests
	try{
		// Return requested page
		res.statusCode = 200;
		res.write(fs.readFileSync("html"+reqpath));
		res.end();

	}catch(error){
		// 404 if page not found
		res.statusCode = 404;
		res.setHeader("Content-Type", "text/plain");
		res.end("404 - Page Not Found");
	}
});


// Create socket.io server
const io = require("socket.io")(server); // Can only be done when server is created

io.on("connection", (socket) => {
	console.log("New user connected!");
	
	// Init socket listeners
	
	// Disconnect
	socket.on("disconnect", () => {
		console.log("User disconnected!");

		// Remove from user lists
		if(userSockets.indexOf(socket) != -1){
			// When in users list
			name = userNames[userSockets.indexOf(socket)];

			userNames.splice(userSockets.indexOf(socket), 1);
			userSockets.splice(userSockets.indexOf(socket), 1);

			if(state == STATE_LOBBY){
				// Send new lobby
				userSockets.forEach(function(s){s.emit("LOBBY", userNames);});
			}else{
				// Send disconnect message
				userSockets.forEach(function(s){s.emit("DISCONNECT", name);});
			}
		}
	});

	// JOIN
	socket.on("JOIN", (username) => {
		console.log("JOIN request for "+username);

		// Check if can enter in lobby
		if(state == STATE_LOBBY){
			// Add user to lists
			userSockets.push(socket);
			userNames.push(username);
			console.log(userNames);

			// Send lobby data
			userSockets.forEach(function(s){s.emit("LOBBY", userNames);});

		}else{
			// When can't enter
			socket.emit("LOBBY_CLOSED");
		}
	});

	// START
	socket.on("START", () => {
		console.log("START request");

		// Send START to all users
		userSockets.forEach(function(s){s.emit("START");});
		
		// Set state
		state = STATE_MANUAL;
		
		// Reset vars
		lines = [];
		lineNr = 0;
		userPoints = [];
		for(i=0; i<userSockets.length; i++){
			userPoints.push(0);
		}

		// Send game data
		var dataUser = {usernames:userNames, linesTotal:linesTotal};
		for(i=0; i<userSockets.length; i++){
			userSockets[i].emit("START", dataUser);
		}

		console.log("Game data sent!");

		// Reset OK
		userOK = [];
		for(i=0; i<userSockets.length; i++){
			userOK[i] = false;
		}
	});

	// OK
	socket.on("OK", () => {
		ok(socket);
	});


	// WRITE
	socket.on("WRITE", (data) => {
		// When received write from player
		
		// Check if empty
		if(data.write == ""){
			socket.emit("WRITE_REPLY", {error:"Please enter some text!"});
			return;
		}

		// Add to list
		userWrite[userSockets.indexOf(socket)] = data.write;

		// Perform ok
		ok(socket);
	});


	// PICK
	socket.on("PICK", (id) => {
		// When received pick form player
		userPick[userSockets.indexOf(socket)] = id;

		// Perform ok
		ok(socket);
	});



});


// Start HTTP server
server.listen(port, hostname, () => {
	console.log("Server running at http://"+hostname+":"+port);
});



// Game methods
function next(){
	// Go to next step of game
	
	// Send LOADING
	userSockets.forEach(function(s){s.emit("LOADING");});
	
	
	if(state == STATE_MANUAL || state == STATE_RESULT){
		// Write
		state = STATE_WRITE;

		// Update vars
		lineNr++;
		userWrite = [];
		for(i=0; i<userSockets.length; i++){
			userWrite.push("");
		}
		userPick = [];
		for(i=0; i<userSockets.length; i++){
			userPick.push("");
		}

		// Stop if got all questions
		if(lineNr > linesTotal){
			// End
			state = STATE_END;

			// Gather data
			var sorted = [];
			for(i=0; i<userNames.length; i++){
				sorted.push({name:userNames[i], score:userPoints[i]});
			}
			sorted.sort((a,b) => b.score-a.score);
			console.log(sorted);

			var ranks = [];
			var r = 1;
			var ls = sorted[0].score;
			for(i=0; i<sorted.length; i++){
				if(sorted[i].score < ls){
					r = i+1;
					ls = sorted[i].score;
				}
				ranks.push({name:sorted[i].name, score:sorted[i].score, rank:r});
			}
			console.log(ranks);

			// Send data
			var data = {ranks:ranks, lineNr:lineNr, lines:lines};
			userSockets.forEach(function(s){s.emit("STOP", data);});

			// Reset vars
			state = STATE_LOBBY;
			userSockets = [];
			userNames = [];


		}else{
			// Load data for new line
			userSockets.forEach(function(s){s.emit("WRITE", {lineNr:lineNr, linesTotal:linesTotal, lines:lines});});
		}

		
	}else if(state == STATE_WRITE){
		// Pick
		state = STATE_PICK;
		console.log("Preparing PICK");

		// Send story and all writes to players
		submissions = [];
		for(i=0; i<userWrite.length; i++){
			submissions.splice(rand(0, submissions.length+1), 0, userWrite[i]);
		}
		console.log(submissions);

		// Send
		var data = {lineNr:lineNr, linesTotal:linesTotal, lines:lines, submissions:submissions};
		userSockets.forEach(function(s){s.emit("PICK", data);});
		console.log(data);

		

	}else if(state == STATE_PICK){
		// Answer
		state = STATE_RESULT;
		console.log("Preparing ANSWER");

		// Update points
		var pointsDiff = [];
		for(i=0; i<userSockets.length; i++){
			pointsDiff.push(0);
		}
		for(i=0; i<userSockets.length; i++){
			// Check writes
			for(j=0; j<userWrite.length; j++){
				if(submissions[userPick[i]].toUpperCase() == userWrite[j].toUpperCase() && i != j){
					userPoints[j] += 1;
					pointsDiff[j] += 1;
				}
			}
		}

		// Get line with most votes
		var bestLine = 0;
		var lineVotes = [];
		for(i=0; i<userSockets.length; i++){
			lineVotes.push(0);
		}
		for(i=0; i<userSockets.length; i++){
			lineVotes[userPick[i]] += 1;
		}
		for(i=0; i<userSockets.length; i++){
			if(lineVotes[i] > lineVotes[bestLine]){
				bestLine = i;
			}
		}

		// Add bestLine to lines
		lines.push(submissions[bestLine]);

		// Send data to players
		var data = {lineNr:lineNr, linesTotal:linesTotal, lines:lines, submissions:submissions, bestLine:bestLine, userPick:userPick, userWrite:userWrite, userPoints:userPoints, pointsDiff:pointsDiff, usernames:userNames};

		userSockets.forEach(function(s){s.emit("ANSWER", data);});
		console.log(data);

	}
}


function ok(socket){
	// Set OK for this player
	userOK[userSockets.indexOf(socket)] = true;
	
	// Check if everybody OK
	var OK = true;
	for(i=0; i<userOK.length; i++){
		OK = OK && userOK[i];
	}
	
	// Send OK data
	var waitingFor = [];
	for(i=0; i<userOK.length; i++){
		if(!userOK[i]) waitingFor.push(userNames[i]);
	}
	var dataOK = {users:waitingFor};
	userSockets.forEach(function(s){s.emit("OK_REPLY", dataOK)});
	
	// Perform next step if OK
	if(OK){

		// Reset OK
		userOK = [];
		for(i=0; i<userSockets.length; i++){
			userOK[i] = false;
		}

		// Next step
		next();
	}
}


// Helper methods
function rand(min, max){
	return Math.floor(Math.random()*(max-min) + min);
}




























