const fs = require("fs");
const express = require('express');
const path = require('path');
const WebSocket = require('ws'); // new

function noop() {}
function heartbeat() {
    this.isAlive = true;
}


const app = express();// express code
const url = require('url');
const User = require('./user');
const moment = require('moment');
const timeFormat = 'h:mm a [PDT]'
const threshold = 1;
const playerThreshold = 2;
var playerTurn = false;
var turnIndex = false;
var nextPlayer = false;
var cardsDir = "cards/";
var cardsFile = cardsDir + moment().format("YYYY-MM-DD") + ".cards";
var cards = []; /*
    {
	"title": "Test Card 1",
	"image": "/img/profiles/6.jpg",
	"content": "Angelica approves.",
	"revealed": false,
	"author": "admin"
    },
    {
	"title": "19.jpg",
	"image": "/img/profiles/19.jpg",
	"content": "Taking work calls like",
	"revealed": false,
	"author": "admin"
    },
    {
	"title": "YUGE",
	"image": "/img/profiles/12.jpg",
	"content": ""
	"revealed": false,
	"author": "admin"
    }
    ]*/
var lastCard = false;
var graveyard = [];
var incompleteCard = false;

fs.readFile(cardsFile, 'utf-8', (err, data) => {
    if (err) console.log(err);
    console.log("Opened %s", cardsFile);
//    cards = JSON.parse(data);
});


const messages = [
    {'username': 'ADMIN',
     'pId': 0,
     'message': 'Welcome to Blank White Cards!',
     'timestamp': moment().format(timeFormat)
    }
];

var players = [];
var playerProfilesMap = {
    'panda':{
	'img': '/img/profiles/6.jpg',
	'pId': 1000
    },
    'hodor':{
	'img': '/img/profiles/12.jpg',
	'pId': 2000
    }
};
var pId = 0;
var username = "";
var profilepic = "";

var mode = "draw"; // can also be "play" or "postgame"

app.get('/game', (req, res) => {
    const querystring = url.parse(req.url,true).query;
    username = querystring.username;
    res.sendFile(path.join(__dirname, 'game.html'));
    
});

const port = process.env.PORT || 8765;
app.listen(port, () => {
    console.log(`listening http://localhost:${port}`);
});

const wss = new WebSocket.Server({port: 3030});

const updateClients = (wss, data) => {
    wss.clients.forEach(function each(client) {
	if (client.readyState === WebSocket.OPEN) {
	    data.timestamp = moment(data.timestamp).format(timeFormat);
	    client.send(JSON.stringify(data));
	}
    });
};

wss.on('connection', function connection(ws) {

    ws.isAlive = true;
    ws.on('pong', heartbeat);
    ws.ping(null, null, false);ws._socket.setKeepAlive(true,100);
    const packet = {
	"type": "init",
	"players": players,
	"mode": mode,
	"playerTurn": playerTurn,
	"messages": messages,
	"cards": cards.length,
	"incompleteCard": incompleteCard
    }
    var existingPlayer = false; 	var status = "entered";
    pId = players.length + 1;
    console.log("Trying to join: " + username + " (Player " + pId + ")");
/*    players.forEach(function each(player) {
	if(player.username == username){
	    packet.mode = "error";
	    packet.error_msg = "That username is taken. Try another.";
	    ws.send(JSON.stringify(packet));
	    console.log("Kicked out due to duplicate username: " + username);
	    ws.close();
	    existingPlayer = true;
	}
    });   
*/
    if(existingPlayer){
	pId = pId - 1;
	return "Fuckoff";
    }
    else{	
	if(playerProfilesMap[username]){
	    pId = playerProfilesMap[username].pId;
	    console.log("Found '" + username + "' in the archives (Player "+pId+").");
	    status = "rejoined";
	    profilepic = playerProfilesMap[username].img;
	}
	else{
	    profilepic = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjQsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMjUwcHgiIGhlaWdodD0iMjUwcHgiIHZpZXdCb3g9IjAgMCAyNTAgMjUwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyNTAgMjUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxyZWN0IGZpbGw9IiNGNUY1RjUiIHdpZHRoPSIyNTAiIGhlaWdodD0iMjUwIi8+DQo8Zz4NCgk8cGF0aCBmaWxsPSIjRDhEOEQ4IiBkPSJNMjI3LjgxOCwyMDcuMjQ1Yy0xLjA0NS01Ljg0Ny0yLjM2OS0xMS4yNTktMy45NjUtMTYuMjUyYy0xLjU5OC00Ljk5NC0zLjc0Mi05Ljg1OS02LjQ0MS0xNC42MDYNCgkJYy0yLjY5Ny00Ljc0LTUuNzg5LTguNzg1LTkuMjgzLTEyLjEzMWMtMy41MDEtMy4zNDMtNy43NjgtNi4wMTUtMTIuODA5LTguMDEyYy01LjA0NS0xLjk5Ni0xMC42MTEtMi45OTUtMTYuNy0yLjk5NQ0KCQljLTAuODk3LDAtMi45OTQsMS4wNzQtNi4yOTEsMy4yMTljLTMuMjk1LDIuMTUtNy4wMTcsNC41NDYtMTEuMTU4LDcuMTg4Yy00LjE0NiwyLjY0Ni05LjUzNyw1LjA0NC0xNi4xNzYsNy4xODkNCgkJYy02LjY0MiwyLjE0OC0xMy4zMDgsMy4yMjEtMTkuOTk1LDMuMjIxYy02LjY4OSwwLTEzLjM1NC0xLjA3MS0xOS45OTUtMy4yMjFjLTYuNjQzLTIuMTQ2LTEyLjAzNi00LjU0My0xNi4xNzYtNy4xODkNCgkJYy00LjE0OC0yLjY0My03Ljg2My01LjAzNy0xMS4xNTgtNy4xODhjLTMuMjk1LTIuMTQ1LTUuMzkxLTMuMjE5LTYuMjkxLTMuMjE5Yy02LjA5NSwwLTExLjY2MSwwLjk5OS0xNi43MDEsMi45OTUNCgkJYy01LjA0MSwxLjk5Ny05LjMxMyw0LjY2OS0xMi44MDMsOC4wMTJjLTMuNTAxLDMuMzQ2LTYuNTkyLDcuMzkxLTkuMjg3LDEyLjEzMWMtMi42OTYsNC43NDctNC44NDcsOS42MTItNi40NDEsMTQuNjA2DQoJCWMtMS41OTcsNC45OTMtMi45MjIsMTAuNDA1LTMuOTcxLDE2LjI1MmMtMS4wNDYsNS44MzktMS43NDgsMTEuMjgtMi4wOTYsMTYuMzIzYy0wLjM0OSw1LjA0My0wLjUyNCwxMC4yMTMtMC41MjQsMTUuNQ0KCQljMCwzLjkyNSwwLjQzMiw3LjU1LDEuMjExLDEwLjkzMmgyMDguNDY0YzAuNzgxLTMuMzgyLDEuMjEzLTcuMDA3LDEuMjEzLTEwLjkzMmMwLTUuMjg3LTAuMTc2LTEwLjQ1Ny0wLjUyNi0xNS41DQoJCUMyMjkuNTY2LDIxOC41MjUsMjI4Ljg2OSwyMTMuMDg0LDIyNy44MTgsMjA3LjI0NXoiLz4NCgk8cGF0aCBmaWxsPSIjRDhEOEQ4IiBkPSJNMTI1LDE2Mi44MzRjMTUuODc1LDAsMjkuNDMtNS42MTcsNDAuNjY2LTE2Ljg1YzExLjIzMi0xMS4yMzUsMTYuODUtMjQuNzg5LDE2Ljg1LTQwLjY2Nw0KCQljMC0xNS44NzctNS42MTctMjkuNDI5LTE2Ljg1LTQwLjY2M0MxNTQuNDMsNTMuNDIyLDE0MC44NzUsNDcuODA0LDEyNSw0Ny44MDRzLTI5LjQzNCw1LjYxOS00MC42NjQsMTYuODUyDQoJCUM3My4xLDc1Ljg5LDY3LjQ4NCw4OS40NDEsNjcuNDg0LDEwNS4zMThjMCwxNS44NzgsNS42MTUsMjkuNDMxLDE2Ljg1Miw0MC42NjdDOTUuNTY2LDE1Ny4yMTcsMTA5LjEyNSwxNjIuODM0LDEyNSwxNjIuODM0eiIvPg0KPC9nPg0KPC9zdmc+DQo=";
	}

	newPlayer = {
	    "pId": pId,
	    "username": username,
	    "profilepic": profilepic
	}
	players.push(newPlayer);
	newData = {
	    'pId': newPlayer.pId,
	    'img': newPlayer.profilepic		
	}
	playerProfilesMap[newPlayer.username] = newData;

	console.log("'" + username + "' has "+status+" the game.");
	
	if(newPlayer.username == playerTurn.username){console.log(">> It's "+newPlayer.username+"'s turn.");}

	packet.id = pId;
	ws.pId = pId;
	ws.send(JSON.stringify(packet));
	
	const newPlayerInfo = {
	    'type': 'newPlayer',
	    'player': newPlayer
	}
	updateClients(wss, newPlayerInfo);
    }
    ws.on('message', function incoming(data) {
	data = JSON.parse(data);
	response = {
	    "type": "error",
	    "content": "Unknown server error."
	}
	switch(data.type){
	case "startPlayMode":
	    if((mode != "play") && (cards.length + 1 > threshold) && (players.length + 1 > playerThreshold)){
		mode = "play";
		console.log("'" + data.player + "' started the game. (%s cards)", cards.length);		
		console.log(">> GAME MODE IS NOW: " + mode);
		type = "changeMode";
		nextPlayerMove(type);
	    }
	    else{		
		if(cards.length < threshold){
		    console.log("'" + data.player + "' tried to start the game. (Error: Not enough cards)");
		    response = {
			"type": "error",
			"content": "The game won't start until we have at least "+threshold+" cards."
		    }
		    ws.send(JSON.stringify(response));		    
		}
		if(players.length < playerThreshold){
		    console.log("'" + data.player + "' tried to start the game. (Error: Not enough players)");
		    response = {
			"type": "error",
			"content": "The game won't start until we have at least "+playerThreshold+" players."
		    }
		    ws.send(JSON.stringify(response));		    
		}
	    }
	    break;
	case "message":
	    console.log("Received message from '%s': '%s'", data.username, data.message);
	    messages.push(data);
	    updateClients(wss,data);
	    
	    break;
	case "vote":
	    console.log("Caught a vote for Player "+data.pId);
	    updateClients(wss,data);
	    break;
	case "changeProfilePic":
	    console.log("'" + data.player.username + "' has changed their profile picture.");
	    players.map(function(p){
		if(p.pId == data.player.pId){
		    p.profilepic = data.href;
		}
	    });
	    newData = {
		'pId': data.player.pId,
		'img': data.href		
	    }
	    
	    playerProfilesMap[data.player.username] = newData;
	    updateClients(wss,data);	    
	    break;
	case "changeUsername":
	    response.type = "notification";
	    if(username == data.username){
		response.type = "error";
		response.content = "Already your name.";
	    }
	    else{
		var someoneElse = false;
		players.forEach(function(p){
		    if(p.username == data.username){
			someoneElse = true;
		    }
		});
		if(someoneElse){ // if there's a current player with this name
		    response.type = "error";
		    response.content = "Already someone's name.";
		}
		else{ // Not already your name, and not someone else's name
		    if(playerProfilesMap[data.username]){
			// We've seen this username before
			if(playerProfilesMap[data.username].pId == pId){
			    // Same pId, good to go
			}
			else{
			    // Someone else's profile, don't touch it
			    response.content = "This looks like someone else's name.";
			    response.type = "error";			
			}
		    }
		    else {
			// Never seen this before
			username = data.username;
			players.forEach(function(p){
			    if(p.pId == pId){
				// This is us within the players array; update it
				p.username = username;
			    }
			});
			
			playerProfilesMap[username] = {
			    'pId': pId,
			    'img': profilepic
			}
		    }
		}
		response.content = "Changed your name to '"+username+"'.";
		
		console.log("Changed player %s to '%s'.", pId, username);		
	    };
	    updateClients(wss,data);
	    ws.send(JSON.stringify(response));
		break;
		
	case "pullCard":

	    if(incompleteCard){
		// There's a card in memory that hasn't been played yet
		pulledCard = incompleteCard;
		
	    }else{
		mycard = true;
		pulledCardIndex = Math.floor(Math.random() * cards.length);
/*		while(mycard){

		    if(cards[pulledCardIndex].author != username){
			mycard = false
		    }
		}		*/
		pulledCard = cards.splice(pulledCardIndex,1)[0];
		graveyard.push(pulledCard); // Keep track of it for later viewing/saving/reloading
		incompleteCard = pulledCard; // Make sure this card remains active until the player does something with it
		
		console.log("Pulled card ("+cards.length+" remaining).");
		console.log(graveyard);
	    
	    
		if(cards.length == 0){
		    console.log("[[ LAST CARD! ]]");
		    lastCard = true;
		}
	    }
	    
	    response = {
		'type': 'pulledCard',
		'card': pulledCard,
		'last': lastCard
	    }
	    
	    ws.send(JSON.stringify(response));
	    
	    break;
	case "revealCard":
	    console.log(">> '"+playerTurn.username+"' has revealed their card.");
	    data.card.revealed = true;
	    incompleteCard.revealed = true;
	    updateClients(wss,data);
	    break;
	case "endTurn":
	    console.log(">> '"+playerTurn.username+"' has ended their turn.");
	    incompleteCard = false;
	    if(lastCard){
		// Game over
		gameOver();
		
	    }else{
		nextPlayerMove("nextTurn");
	    }
	    break;
	case "card":
	    var dupe = false;
	    const card = data.card;
	    const author = data.card.author;
	    console.log("'%s' submitted a card.", author);

	    cards.forEach(function each(c){
		if(c.content == card.content){
		    console.log("Duplicate card");
		    dupe = true;
		}
	    });

	    if(dupe){
		response = {
		    'type':'error',
		    'content': 'This looks like a duplicate of a card we already have.'
		}
	    }
	    else{
		cards.push(card);
		response = {
		    'type':'updateCardsList',
		    'author': author
		}
	    }
	    
	    updateClients(wss, response);
	    break;
	default:
	    console.log("Unknown client request:\n" + data);
	}
    });
    ws.on('close', function close() {
	console.log('Client disconnected');
	players = players.filter(function(p){
	    return p.pId != ws.pId;
	});
	pId = pId - 1;
    });      
});

const gameOver = () => {
    console.log("Game is over");
    notice = {
	'type': "gameOver",
	"content": "Thanks for playing - the game's over!"
    }
    updateClients(wss, notice);
    updateClients(wss, {
	'type': 'message',
	'pId': 0,
	'username': "ADMIN",
	'message': "Thanks for playing - the game's over!"
    });

    var saveFile = cardsDir + moment().format("YYYY-MM-DD-hh-mm") + ".cards";
    fs.writeFile(saveFile, JSON.stringify(graveyard), (err) => {
	if (err) console.log(err);
    });
    
    
}

const nextPlayerMove = (type) => {
    if(type == "changeMode"){
	// First move of the game
	console.log("First move; choosing random player");
	turnIndex = Math.floor(Math.random() * players.length); // For now, just random	
    }
    else{
	// Mid-game
	console.log(players[turnIndex].username + " just finished. Incrementing..");
	turnIndex = turnIndex + 1;
	if(!players[turnIndex]){turnIndex = 0}	
    }
    console.log(players[turnIndex].username + " is up now.");
    turnIndex = 1;

    playerTurn = players[turnIndex];      

    response = {
	"type": type,
	"mode": "play",
	"playerTurn": playerTurn,
	"message": null
    }
    updateClients(wss,response);
    

    /*       
    var order = "Next";
    var firstLine = playerTurn.username + ' has ended their turn.';
    var name = nextPlayer.username;
    
    if(type == "changeMode"){
	name = playerTurn.username;
	order = "First";
	firstLine = 'The game has started.';
    }    
    message = { 
	'username': 'ADMIN',
	'pId': 0,
	'message': firstLine + '<br/>'+order+' up: <b>'+name+'</b>',
	'timestamp': moment().format(timeFormat)
    }
*/    
}

const interval = setInterval(function ping() {
    wss.clients.forEach(function each(ws) {
	if (ws.isAlive === false) return ws.terminate();

	ws.isAlive = false;
	ws.ping(noop);
    });
}, 30000);


wss.on('close', function close() {
    clearInterval(interval);
});
