// times i've wanted to jump off a bridge while writing this counter:
// 5

let stage_data, mappool, teams, stage_updated = false;
(async () => {
	$.ajaxSetup({ cache: false });
	stage_data = await $.getJSON('../_data/current_round.json');
	mappool = await $.getJSON('../_data/beatmaps.json');
	teams = await $.getJSON('../_data/teams.json');
})();

let socket = new ReconnectingWebSocket('ws://' + location.host + '/ws');

let map_container = document.getElementById('map-container');
let title = document.getElementById('title');
let diff = document.getElementById('diff');
let artist = document.getElementById('artist');
let nowplaying = document.getElementById('mod');

let stage = document.getElementById('stage');
let red_name = document.getElementById('red-name');
let red_points = document.getElementById('red-points');
let red_score = document.getElementById('red-score');
let red_flag = document.getElementById('red-flag');

let blue_name = document.getElementById('blue-name');
let blue_points = document.getElementById('blue-points');
let blue_score = document.getElementById('blue-score');
let blue_flag = document.getElementById('blue-flag');

let lead_bar = document.getElementById('lead-bar');
let chat_container = document.getElementById('chat-container');
let chat = document.getElementById('chat');

socket.onopen = () => { console.log('Successfully Connected'); };

let animation = {
	red_score: new CountUp('red-score', 0, 0, 0, .3, { useEasing: true, useGrouping: true, separator: ',', decimal: '.' }),
	blue_score: new CountUp('blue-score', 0, 0, 0, .3, { useEasing: true, useGrouping: true, separator: ',', decimal: '.' }),
}

socket.onclose = event => {
	console.log('Socket Closed Connection: ', event);
	socket.send('Client Closed!');
};

socket.onerror = error => { console.log('Socket Error: ', error); };

let tempImg;
let tempMapName;
let tempMapDiff;
let tempMapArtist;
let ar;
let od;
let cs;
let sr;
let bpm;
let len;
let id;
let md5;

let chatLen = 0;
let tempClass = 'unknown';

let bestOf;
let firstTo;
let scoreVisible;
let starsVisible;
let starsRed;
let starsBlue;
let scoreBlue;
let scoreRed;
let nameBlue;
let nameRed;

socket.onmessage = event => {
	let data = JSON.parse(event.data);

	if (stage && mappool && stage_data && (id !== data.menu.bm.id || !stage_updated)) {
		id = data.menu.bm.id;
		//let map = mappool.find(m => m.beatmap_id == id);
		stage.innerHTML = stage_data.long_name.toUpperCase();
		stage_updated = true;
	}

	if (scoreVisible !== data.tourney.manager.bools.scoreVisible) {
		scoreVisible = data.tourney.manager.bools.scoreVisible;
		if (scoreVisible) {
			chat_container.style.opacity = 0;
			blue_score.style.opacity = 1;
			red_score.style.opacity = 1;
			lead_bar.style.opacity = 1;
		} else {
			chat_container.style.opacity = 1;
			blue_score.style.opacity = 0;
			red_score.style.opacity = 0;
			lead_bar.style.opacity = 0;
		}
	}
	if (starsVisible !== data.tourney.manager.bools.starsVisible) {
		starsVisible = data.tourney.manager.bools.starsVisible;
		if (starsVisible) {
			blue_points.style.opacity = 1;
			red_points.style.opacity = 1;

		} else {
			blue_points.style.opacity = 0;
			red_points.style.opacity = 0;

		}
	}
	if (tempImg !== data.menu.bm.path.full) {
		tempImg = data.menu.bm.path.full;
		data.menu.bm.path.full = data.menu.bm.path.full.replace(/#/g, '%23').replace(/%/g, '%25').replace(/\\/g, '/');
		map_container.style.backgroundImage = `url('http://` + location.host + `/Songs/${data.menu.bm.path.full}')`;
	}
	if (tempMapName !== data.menu.bm.metadata.title) {
		tempMapName = data.menu.bm.metadata.title;
		title.innerHTML = tempMapName;
	}
	if (tempMapArtist !== data.menu.bm.metadata.artist) {
		tempMapArtist = data.menu.bm.metadata.artist;
		artist.innerHTML = tempMapArtist;
	}
	if (tempMapDiff !== `set by ${data.menu.bm.metadata.mapper} – [${data.menu.bm.metadata.difficulty}]`) {
		tempMapDiff = `set by ${data.menu.bm.metadata.mapper} – [${data.menu.bm.metadata.difficulty}]`;
		diff.innerHTML = tempMapDiff;
	}

	if (mappool && md5 !== data.menu.bm.md5 || len !== data.menu.bm.time.full - data.menu.bm.time.firstObj) {
		let map = mappool.find(m => m.beatmap_id == data.menu.bm.id) || { id: data.menu.bm.id, mods: 'NM', identifier: '' };
		let mod_ = map.mods;
		let stats = getModStats(data.menu.bm.stats.CS, data.menu.bm.stats.AR, data.menu.bm.stats.OD, data.menu.bm.stats.BPM.max, mod_);
		let singlestat = mod_ != 'FM';

		md5 = data.menu.bm.md5;
		ar = singlestat ? stats.ar : `${data.menu.bm.stats.AR}<i>▴${stats.ar}</i>`;
		cs = singlestat ? stats.cs : `${data.menu.bm.stats.CS}<i>▴${stats.cs}</i>`;
		od = singlestat ? stats.od : `${data.menu.bm.stats.OD}<i>▴${stats.od}</i>`;
		sr = data.menu.bm.stats.fullSR;
		let srup = mod_.includes('HR') || mod_.includes('DT') || mod_.includes('FM') ? '▴' : '';
		bpm = stats.bpm;

		len = data.menu.bm.time.full - data.menu.bm.time.firstObj;
		let mins = Math.trunc((len / stats.speed) / 1000 / 60);
		let secs = Math.trunc((len / stats.speed) / 1000 % 60);
		let songlength = `${mins}:${secs.toString().padStart(2, '0')}`;

		document.getElementById('stats-1').innerHTML = `CS <b>${cs}</b> / AR <b>${ar}</b> / OD <b>${od}</b>`;
		document.getElementById('stats-2').innerHTML = `BPM <b>${bpm}</b> / LENGTH <b>${songlength}</b> / SR <b>${sr}★${srup}</b>`;
		nowplaying.innerHTML = `NOW PLAYING${map.identifier ? `: ${map.identifier}` : ''}`
	}

	if (bestOf !== data.tourney.manager.bestOF) {
		let newmax = Math.ceil(data.tourney.manager.bestOF / 2);
		if (bestOf === undefined) {
			for (let i = 1; i <= newmax; i++) {
				let nodeBlue = document.createElement('div');
				let nodeRed = document.createElement('div');
				nodeBlue.className = 'star-b';
				nodeRed.className = 'star-r';
				nodeBlue.id = 'blue' + i.toString();
				nodeRed.id = 'red' + i.toString();
				document.getElementById('blue-points').appendChild(nodeBlue);
				document.getElementById('red-points').appendChild(nodeRed);
			}

		}
		if (bestOf < data.tourney.manager.bestOF) {
			for (let i = firstTo + 1; i <= newmax; i++) {
				let nodeBlue = document.createElement('div');
				let nodeRed = document.createElement('div');
				nodeBlue.className = 'star';
				nodeRed.className = 'star';
				nodeBlue.id = 'blue' + i.toString();
				nodeRed.id = 'red' + i.toString();
				document.getElementById('blue-points').appendChild(nodeBlue);
				document.getElementById('red-points').appendChild(nodeRed);
			}
		} else {
			for (let i = firstTo; i > newmax; i--) {
				let nodeBlue = document.getElementById('blue' + i.toString());
				let nodeRed = document.getElementById('red' + i.toString());
				document.getElementById('blue-points').removeChild(nodeBlue);
				document.getElementById('red-points').removeChild(nodeRed);
			}
		}
		bestOf = data.tourney.manager.bestOF;
		firstTo = newmax;
	}

	if (starsRed !== data.tourney.manager.stars.left) {
		starsRed = data.tourney.manager.stars.left;
		for (let i = 1; i <= starsRed; i++) { document.getElementById('red' + i.toString()).style.backgroundColor = '#222222'; }
		for (let i = starsRed + 1; i <= firstTo; i++) { document.getElementById('red' + i.toString()).style.backgroundColor = 'none'; }
	}
	if (starsBlue !== data.tourney.manager.stars.right) {
		starsBlue = data.tourney.manager.stars.right;
		for (let i = 1; i <= starsBlue; i++) { document.getElementById('blue' + i.toString()).style.backgroundColor = '#222222'; }
		for (let i = starsBlue + 1; i <= firstTo; i++) { document.getElementById('blue' + i.toString()).style.backgroundColor = 'none'; }
	}

	if (nameBlue !== data.tourney.manager.teamName.right && data.tourney.manager.teamName.right && teams) {
		nameBlue = data.tourney.manager.teamName.right || 'Blue';
		let team = teams.find(t => t.name == nameBlue);

		blue_flag.src = team.flag_url;
		blue_flag.style.visibility = 'visible';
		blue_name.innerHTML = nameBlue.toUpperCase();
	}
	if (nameRed !== data.tourney.manager.teamName.left && data.tourney.manager.teamName.left && teams) {
		nameRed = data.tourney.manager.teamName.left || 'Red';
		let team = teams.find(t => t.name == nameRed);

		red_flag.src = team.flag_url;
		red_flag.style.visibility = 'visible';
		red_name.innerHTML = nameRed.toUpperCase();
	}

	if (!scoreVisible && (scoreRed > 0 || scoreBlue > 0)) {
		scoreRed = 0;
		scoreBlue = 0;
		animation.red_score.update(scoreRed);
		animation.blue_score.update(scoreBlue);
	}

	if (scoreVisible) {
		// scoreRed = data.tourney.manager.gameplay.score.left + 551453;
		// scoreBlue = data.tourney.manager.gameplay.score.right + 178456;

		// if (modsRed.includes('EZ')) scoreRed = Math.round(scoreRed * 1.75);
		// if (modsBlue.includes('EZ')) scoreBlue = Math.round(scoreBlue * 1.75);

		let scores = [];
		for (let i = 0; i < 6; i++) {
			let score = data.tourney.ipcClients[i].gameplay.score;
			let mods = data.tourney.ipcClients[i].gameplay.mods.str;
			if (mods.includes('EZ')) score *= 1.75;
			scores.push({ id: i, score });
		}

		scoreRed = scores.filter(s => s.id == 0 || s.id == 1 || s.id == 2).map(s => s.score).reduce((a, b) => a + b);
		scoreBlue = scores.filter(s => s.id == 3 || s.id == 4 || s.id == 5).map(s => s.score).reduce((a, b) => a + b);

		animation.red_score.update(scoreRed);
		animation.blue_score.update(scoreBlue);

		if (scoreBlue > scoreRed) {
			blue_score.style.fontFamily = 'Mont-Bold';
			blue_score.style.fontSize = '54px';
			red_score.style.fontFamily = 'Mont-Regular';
			red_score.style.fontSize = '44px';

			lead_bar.style.width = 360 * (Math.min(0.5, Math.pow((scoreBlue - scoreRed) / 1000000, 0.7)) * 2) + 'px';
			lead_bar.style.right = 'unset';
			lead_bar.style.left = '960px';
		}
		else {
			blue_score.style.fontFamily = 'Mont-Regular';
			blue_score.style.fontSize = '44px';
			red_score.style.fontFamily = 'Mont-Bold';
			red_score.style.fontSize = '54px';

			lead_bar.style.width = 360 * (Math.min(0.5, Math.pow((scoreRed - scoreBlue) / 1000000, 0.7)) * 2) + 'px';
			lead_bar.style.right = '960px';
			lead_bar.style.left = 'unset';
		}
	}
	if (!scoreVisible) {
		if (chatLen != data.tourney.manager.chat.length) {
			// There's new chats that haven't been updated

			if (chatLen == 0 || (chatLen > 0 && chatLen > data.tourney.manager.chat.length)) {
				// Starts from bottom
				chat.innerHTML = '';
				chatLen = 0;
			}

			// Add the chats
			for (var i = chatLen; i < data.tourney.manager.chat.length; i++) {
				tempClass = data.tourney.manager.chat[i].team;

				let text = data.tourney.manager.chat[i].messageBody;
				if (data.tourney.manager.chat[i].name == 'BanchoBot' && text.startsWith('Match history')) {
					continue;
				}

				// Chat variables
				let chatParent = document.createElement('div');
				chatParent.setAttribute('class', 'chat');

				let chatTime = document.createElement('div');
				chatTime.setAttribute('class', 'chatTime');

				let chatName = document.createElement('div');
				chatName.setAttribute('class', 'chatName');

				let chatText = document.createElement('div');
				chatText.setAttribute('class', 'chatText');

				chatTime.innerText = data.tourney.manager.chat[i].time;
				chatName.innerText = data.tourney.manager.chat[i].name + ':\xa0';
				chatText.innerText = text;

				chatName.classList.add(tempClass);

				chatParent.append(chatTime);
				chatParent.append(chatName);
				chatParent.append(chatText);
				chat.append(chatParent);

			}

			// Update the Length of chat
			chatLen = data.tourney.manager.chat.length;

			// Update the scroll so it's sticks at the bottom by default
			chat.scrollTop = chat.scrollHeight;
		}
	}
}

const getModStats = (cs_raw, ar_raw, od_raw, bpm_raw, mods) => {
	mods = mods.replace('NC', 'DT');
	mods = mods.replace('FM', 'HR');

	let speed = mods.includes('DT') ? 1.5 : mods.includes('HT') ? 0.75 : 1;
	let ar = mods.includes('HR') ? ar_raw * 1.4 : mods.includes('EZ') ? ar_raw * 0.5 : ar_raw;

	let ar_ms = Math.max(Math.min(ar <= 5 ? 1800 - 120 * ar : 1200 - 150 * (ar - 5), 1800), 450) / speed;
	ar = ar <= 5 ? (1800 - ar_ms) / 120 : 5 + (1200 - ar_ms) / 150;

	let cs = Math.round(Math.min(mods.includes('HR') ? cs_raw * 1.3 : mods.includes('EZ') ? cs_raw * 0.5 : cs_raw, 10) * 10) / 10;

	let od = mods.includes('HR') ? od_raw * 1.4 : mods.includes('EZ') ? od_raw * 0.5 : od_raw;
	od = Math.round(Math.min((79.5 - Math.min(79.5, Math.max(19.5, 79.5 - Math.ceil(6 * od))) / speed) / 6, 11) * 10) / 10;

	return {
		cs: Math.round(cs * 10) / 10,
		ar: Math.round(ar * 10) / 10,
		od: Math.round(od * 10) / 10,
		bpm: Math.round(bpm_raw * speed * 10) / 10,
		speed
	}
}