//Make sure canvasses are in the correct positionG
var maincanvas= document.querySelector("MainCanvas");

var MainCanvas = document.getElementById("MainCanvas");
var MainCanvas_ctx = MainCanvas.getContext("2d");
var maincanvasBackground="lightgrey";
var mouseIsDown=false;
var mouseDownLocation;
var mouseDidMovement=false;
var imageSize=40;
var small_button_size = 20;
var uiDeviceInfoLevel = 1; //What do we display when we look at the network
var uiActions = [];//a list of things on the screen we can click on or process.
var ui_highlightRect = null;
var ui_HadHighlight = false;

//The user interface mode.  0=network, 1=network information, 2=puzzle-selection menu
var uiMode=1;


const imageCollection = loadImages(
	["ArrowUp", "ArrowDown", "animations",
		"burnmark", "cellphone", "circle",
		"copier", "firewall", "fluorescent",
		"hub", "ip_phone", "laptop",
		"link", "microwave", "pc", "printer",
		"router", "select", "server",
		"shapes", "square", "switch",
		"tablet", "tree", "vidimage",
		"wap", "wbridge", "wrepeater",
		"wrouter", "x", "info", "menu",
		"eye", "queryuser",
	],
	["img/ArrowUp.png", "img/ArrowDown.png", "img/Animations.png",
		"img/BurnMark.png", "img/cellphone.png", "img/Circle.png",
		"img/Copier.png", "img/firewall.png", "img/fluorescent.png",
		"img/Hub.png", "img/ip_phone.png", "img/Laptop.png",
		"img/link.png", "img/microwave.png", "img/PC.png",
		"img/Printer.png", "img/Router.png", "img/select.png",
		"img/Server.png", "img/Shapes.png", "img/Square.png",
		"img/Switch.png", "img/tablet.png", "img/tree.png",
		"img/VidImage.png", "img/WAP.png", "img/WBridge.png",
		"img/WRepeater.png", "img/WRouter.png", "img/X.png",
		"img/info.png", "img/menu.png", "img/eye.png",
		"img/menu.png",
	],
    InitializeGameMenu  // this is called when all images have loaded.
);

var menuItemSize=50;


function loadImages(names, files, onAllLoaded) {
    var i = 0, numLoading = names.length;
    const onload = () => --numLoading === 0 && onAllLoaded();
    const images = {};
    while (i < names.length) {
        const img = images[names[i]] = new Image;
        img.src = files[i++];
        img.onload = onload;
    }
    return images;
}

function imageFromName(name)
{
    return imageCollection[name];
}

function InitializeGameMenu()
{
    console.log("Initializing");
    MainCanvas.addEventListener("touchstart", handleTouchStart);
    MainCanvas.addEventListener("touchend", handleTouchEnd);
    MainCanvas.addEventListener("touchcancel", handleTouchCancel);
    MainCanvas.addEventListener("touchmove", handleTouchMove);
    MainCanvas.addEventListener('mousedown',handleMouseDown);
    MainCanvas.addEventListener('mouseup',handleMouseUp);
    MainCanvas.addEventListener('mousemove',handleMouseMove);

    //MainCanvas_ctx.drawImage(imageCollection['router'],100,100,50,50);
    //MainCanvas_ctx.drawImage(imageCollection['firewall'],150,150,50,50);

	InitializeSelectMenu();
    //It should be printed
    PrintScreen();
}

//Print the screen.  Figure out what needs to be printed based on the mode
function PrintScreen(WhatPassedIn=-1)
{
//allow us to override what is printed
    var what=uiMode;
    if(WhatPassedIn >=0) what=WhatPassedIn;

	//Clear out any old ActionStructs.  They will get filled in as we print the screen.
	clearActionStructs();

	console.log("PrintingScreen for mode: " + what);
	var rect;

    if(what == 0)
    {
		//The network drawing mode.  Print the network
		//Clear the old screen
		MainCanvas_ctx.fillStyle = maincanvasBackground;
		MainCanvas_ctx.fillRect(0, 0, MainCanvas.width, MainCanvas.height);

		//Do any highlight we need to
		if (ui_highlightRect !== null) {
			//console.log("trying to highlight something: " + JSON.stringify(ui_highlightRect));
			MainCanvas_ctx.fillStyle = "white";
			MainCanvas_ctx.globalAlpha = 0.3; //mostly transparent
			if (ui_highlightRect.shapeText == "square")
				MainCanvas_ctx.fillRect(ui_highlightRect.sx, ui_highlightRect.sy, ui_highlightRect.deltax, ui_highlightRect.deltay);
			else if (ui_highlightRect.shapeText == "line") {
				var oldWidth = MainCanvas_ctx.lineWidth;
				MainCanvas_ctx.lineWidth += 4;
				MainCanvas_ctx.strokeStyle = "white";
				MainCanvas_ctx.beginPath();
				MainCanvas_ctx.moveTo(ui_highlightRect.sx, ui_highlightRect.sy);
				MainCanvas_ctx.lineTo(ui_highlightRect.dx, ui_highlightRect.dy);
				MainCanvas_ctx.stroke();
				MainCanvas_ctx.lineWidth = oldWidth;
				MainCanvas_ctx.strokeStyle = "black";
            }
			MainCanvas_ctx.globalAlpha = 1.0; //reset
			MainCanvas_ctx.fillStyle = "black"; //reset
			ui_HadHighlight = true;
		}
		else ui_HadHighlight = false;

		//Draw the puzzle-select button
		//Put the X there so we can click on it
		rect = makeRectangle(MainCanvas.width - small_button_size, 0, small_button_size, small_button_size);
		MainCanvas_ctx.drawImage(imageFromName("menu"),rect.sx,rect.sy,rect.deltax,rect.deltay);
		registerActionStruct("square", rect, null, ui_PuzzleChoiceMenuLeftClick, null, generic_mouseoverHighlight);

		//Draw the info button
		rect = makeRectangle(MainCanvas.width - small_button_size, small_button_size, small_button_size, small_button_size);
		MainCanvas_ctx.drawImage(imageFromName("info"), rect.sx, rect.sy, rect.deltax, rect.deltay);
		registerActionStruct("square", rect, null, ui_InfoLeftClick, null, generic_mouseoverHighlight);

		//Draw the eye button
		rect = makeRectangle(MainCanvas.width - small_button_size, small_button_size * 2, small_button_size, small_button_size);
		MainCanvas_ctx.drawImage(imageFromName("eye"), rect.sx, rect.sy, rect.deltax, rect.deltay);
		registerActionStruct("square", rect, null, ui_eyeLeftClick, null, generic_mouseoverHighlight);

		drawSelectMenu();
		PrintAllNetworkLinks();
		PrintAllNetworkDevices();
	}
	else if(what == 1) //PuzzleDescription/Info
	{
	//Display the text about the puzzle
	textMenuPrint(puzzle.en_message);
	}
	else if(what == 2) //PuzzleSelect
	{
	//TextMenuSelection
	PrintPuzzleSelectMenu(0);
	}
}

function handleTouchStart(evt)
{
    handleMouseDown(copyLocation(evt));
}
function handleTouchEnd(evt)
{
    handleMouseUp(copyLocation(evt));
}
function handleTouchCancel(evt)
{
    evt.preventDefault();
    console.log("canceling touch");
}
function handleTouchMove(evt)
{
    //evt.preventDefault();
    if(evt.touches.length > 0)
    {
    handleMouseMove(copyLocation(evt.touches[0]));
    } else{
    //console.log("not enough touches");
    }
    //console.log("moving touch");
}
function handleMouseDown(evt)
{
    mouseDownLocation = copyLocation(evt);
    mouseIsDown=true;
    //console.log("mousedown");
	if(uiMode==0) SelectMenu_handleMouseDown(mouseDownLocation);
}

function CheckForActions(actionPoint, action) {
	if (uiActions.length >= 0) {
		var checkit = false;
		var inside = false;
		for (var index = 0; index < uiActions.length; index++) {
			if (action == "leftclick" && uiActions[index].funcLeftClick !== null) checkit = true;
			if (action == "rightclick" && uiActions[index].funcRightClick !== null) checkit = true;
			if (action == "mouseover" && uiActions[index].funcMouseover !== null) checkit = true;
			checklocation = uiActions[index];
			var point = newPointFromPair(actionPoint.pageX - checklocation.shapePoints.offsetx, actionPoint.pageY - checklocation.shapePoints.offsety);
			if (checkit) {
				//See if the click is inside the shape
				if (checklocation.shapeText == "square") {
					if (pointInRect(point, checklocation.shapePoints))
						inside = true;
				}
				if (checklocation.shapeText == "line") {
					if (pointInRect(point, checklocation.shapePoints)) {
						//console.log("inside line square");
						//We are inside the box.  Now determine if we are on the line...
						var d1 = distance(checklocation.shapePoints.sx, checklocation.shapePoints.sy, actionPoint.pageX, actionPoint.pageY);
						d1 += distance(checklocation.shapePoints.dx, checklocation.shapePoints.dy, actionPoint.pageX, actionPoint.pageY);
						var d2 = distance(checklocation.shapePoints.sx, checklocation.shapePoints.sy, checklocation.shapePoints.dx, checklocation.shapePoints.dy);
						if (Math.abs(d1 - d2) < 3) {
							inside = true;
							//console.log("on a line!");
						}
					}
				}

			}
			if (inside) {
				//console.log("Is inside");
				switch (action) {
					case "leftclick":
						if (checklocation.funcLeftClick != null) {
							checklocation.funcLeftClick(actionPoint, checklocation);
							//console.log("Successfully did a UI action");
							return true;
						}
						break;
					case "mouseover":
						if (checklocation.funcMouseover != null) {
							checklocation.funcMouseover(actionPoint, checklocation);
							//console.log("Successfully did a UI action");
							return true;
						}
						break;
				}
			}
		}
	}
	return false;
}

function pointInRect(point, rectangle) {
	if (point.x  >= Math.min(rectangle.sx,rectangle.dx)) {
		if (point.x <= Math.max(rectangle.sx, rectangle.dx)) {
			if (point.y >= Math.min(rectangle.sy, rectangle.dy)) {
				if (point.y <= Math.max(rectangle.sy, rectangle.dy)) {
					return true;
				}
			}
		}
	}
	return false;
}

//return the distance between two points
function distance(x1, y1, x2, y2) {
	return Math.hypot(x2 - x1, y2 - y1);
}

function handleMouseUp(evt)
{
    //evt.preventDefault();
    mouseIsDown=false;
    //See if we have a mouse click
    let deltaX = Math.abs(evt.pageX - mouseDownLocation.pageX);
    let deltaY = Math.abs(evt.pageY - mouseDownLocation.pageY);
    //console.log("delta " + deltaX + "," + deltaY);
    if(deltaY < 3 && deltaX <3)
    {
		//We did not move much.  Assume click
		if(evt.pageX <= menuItemSize && !mouseDidMovement)
		{
			if(uiMode==0) SelectMenu_handleMouseUp(evt);

		 //We are in the item select menu.
		}
		if(!mouseDidMovement)
		{ //If we are not dragging, it is a click
			var myevt = copyLocation(evt);
			//console.log("evt:" + JSON.stringify(myevt));
			if (CheckForActions(myevt, "leftclick")) return; //If we did this, do not do anything else.
			if(uiMode==1) TextWindow_handleMouseUp(evt);
			else if(uiMode==2) TextWindow_handleMouseUp(evt);
		}
		
    }
    mouseDidMovement=false; //reset it after we raise the button
}

function ui_PuzzleChoiceMenuLeftClick(evt) {
	//We clicked on the puzzle-select menu
	console.log("PuzzleSelect pressed in action");
	uiMode = 2;
	PrintScreen();
}

function ui_eyeLeftClick(evt) {
	console.log("Selected 'eye' button in action");
	//It is the eye button
	uiDeviceInfoLevel++;
	if (uiDeviceInfoLevel > 3) uiDeviceInfoLevel = 0;
	PrintScreen();
}

function ui_InfoLeftClick(evt) {
	console.log("Selected info button in action");
	//It is the info button
	uiMode = 1;
	PrintScreen();
}

function handleMouseMove(evt)
{
    //evt.preventDefault();
    if(mouseIsDown)
    {
		let deltaX = evt.pageX - mouseDownLocation.pageX;
		let deltaY = evt.pageY - mouseDownLocation.pageY;
		if(isNaN(deltaY)) deltaY=0;
		if(isNaN(deltaX)) deltaX=0;
		//we are dragging
		//console.log('mousemove ' + evt.pageX + " " + evt.pageY + "  delta " + deltaY );
		if(uiMode == 0)
		{
			SelectMenu_handleMouseMove(evt);
		}
		
		mouseDidMovement=true;
	}
	else //Mouse is not down.  Not dragging
	{
		var needrefresh = false;
		var oldRect = structuredClone(ui_highlightRect);
		if (!CheckForActions(evt, "mouseover")) {
			//We did not find anything
			if (JSON.stringify(ui_highlightRect) === JSON.stringify(oldRect)) {

			}
			else {
				//console.log("Rects are not equal:" + JSON.stringify(ui_highlightRect) + " - " + JSON.stringify(oldRect) )
				needrefresh = true;
			}
			ui_highlightRect = null; //nothing to highlight
			if (ui_HadHighlight) { needrefresh = true; }
			if (needrefresh) {
				PrintScreen();
			}
        }
		if(uiMode==2)
		{
			textMenu_HandleMouseMove(evt);
		}

	}
	return;
}

function copyLocation({ pageX, pageY }) {
  return { pageX, pageY };
}

function PrintNetworkLink(linkToPrint)
{
    //We should have passed in a working link, make sure it exists
    if(linkToPrint !== null)
    {
		if(linkToPrint.SrcNic !== null && linkToPrint.DestNic !== null)
		{
			//console.log("printing link from " + linkToPrint.SrcNic.hostname);
			sdevice = deviceFromID(linkToPrint.SrcNic.hostid);
			ddevice = deviceFromID(linkToPrint.DstNic.hostid);
			if(sdevice !== null && ddevice !== null)
			{
				//We have an existing link with two devices.  Find the device locations and print
				var spoint = convertXYPointToActual(newPointFromString(sdevice.location));
				var dpoint = convertXYPointToActual(newPointFromString(ddevice.location));

				//Make an actionstruct
				var actionLine = makeLine(spoint.x, spoint.y, dpoint.x, dpoint.y);
				registerActionStruct("line", actionLine, linkToPrint, null, null, generic_mouseoverHighlight);

				var old
				//Now we draw a line between them
				MainCanvas_ctx.beginPath();
				MainCanvas_ctx.moveTo(spoint.x,spoint.y);
				MainCanvas_ctx.lineTo(dpoint.x,dpoint.y);
				MainCanvas_ctx.stroke();
			}
		}
	}

}

function PrintAllNetworkLinks()
{
    if (puzzle == null) return; //If the puzzle has not been set, exit

    let index=0;
	if(puzzle.link !== null && typeof(puzzle.link) === "object")
	{
		while (index < puzzle.link.length) {
		PrintNetworkLink(puzzle.link[index]);
			index++;
		}
	}
}

function PrintNetworkDevice(ToPrint)
{
    //We should have passed in a working device, make sure it exists
    if(ToPrint !== null)
    {
		var rect = deviceRectangle(ToPrint);
		var actionrect = makeRectangle(rect.spoint.x, rect.spoint.y, rect.width, rect.height);

		var dname = ToPrint.mytype;
		if(dname=="net_switch") dname="switch";	
		if(dname=="net_hub") dname="hub";	
		//console.log("printing device " + dname);
		MainCanvas_ctx.drawImage(imageFromName(dname), rect.spoint.x, rect.spoint.y, rect.width, rect.height);
		registerActionStruct("square", actionrect, ToPrint, null, null, generic_mouseoverHighlight);

		//Now, we see if we need to print the name, or a list of IPs..
		var xpoint = rect.center.x;
		var ystart = rect.epoint.y; //the bottom-most Y point
		var gap = 3;
		var delta;
		var ipaddresses = ipsFromDevice(ToPrint);
		//console.log("addresses: " + JSON.stringify(ipaddresses));
		switch (uiDeviceInfoLevel) {
			case 0:
				//Do not print anything
				break;
			case 1:
				//Print the name
				printCenteredText(MainCanvas_ctx, ToPrint.hostname, xpoint, ystart);
				break;
			case 2:
			case 3:
				//console.log("printing device " + ToPrint.hostname);
				//Print both the name and the IP addresses
				if (uiDeviceInfoLevel == 2) {
					delta = printCenteredText(MainCanvas_ctx, ToPrint.hostname, xpoint, ystart) + gap;
					ystart += delta / 2;
				}
				//print the ip addresses
				for (var x = 0; x < ipaddresses.length; x++) {
					//Print the IP address if the type is correct.
					//console.log(JSON.stringify(ipaddresses[x]));
					switch (ipaddresses[x].nictype) {
						case "eth":
						case "management_interface":
							//console.log("Found a " + ipaddresses[x].nictype)
							let mystring = ipaddresses[x].cidrip;
							delta = printCenteredText(MainCanvas_ctx, mystring, xpoint, ystart);
							ystart += (delta / 2);
							break;
                    }
                }
				break;
        }
    }
}

function PrintAllNetworkDevices()
{
    if (puzzle == null) return; //If the puzzle has not been set, exit

    let index=0;
    while (index < puzzle.device.length) {
	PrintNetworkDevice(puzzle.device[index]);
        index++;
    }
}

//print centered text.  We use y as the top-most y position.  But we center around the x position
function printCenteredText(canvas_context, text, centerx, top_y, font = "15px serif", textcolor="black") {
	var metrics = canvas_context.measureText(text);
	var yHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent + tmTextYGap;
	var xWidth = metrics.width;

	var oldfill = canvas_context.fillStyle;
	var oldstroke = canvas_context.strokeStyle;

	canvas_context.font = font;
	canvas_context.fillStyle = textcolor;
	canvas_context.strokeStyle = textcolor;

	canvas_context.fillText(text, centerx - (xWidth / 2), top_y + (yHeight / 3));

	//reset stuff when done
	canvas_context.fillStyle = oldfill;
	canvas_context.strokeStyle = oldstroke;

	return yHeight; //report back how much space we used.  Just in case they want it.
}

function convertXYPointToActual(point)
{
    //We have an x and y coordinate which needs to be converted to the canvas size
    var deltax = (MainCanvas.width - menuItemSize) / puzzle.width;
    var deltay = MainCanvas.height / puzzle.height;
    
   return newPointFromPair((point.x * deltax) + menuItemSize, point.y * deltay);
}

function convertXYpairToActual(x,y)
{
    return convertXYPointToActual(newPointFromPair(x,y));
}

function newPointFromPair(x,y)
{
    var point = { 
	'x' : Math.floor(x),
	'y' : Math.floor(y)
	}
    return point;
}

function newPointFromString(pointasstring)
{
    if(typeof(pointasstring) == "string")
    {
	var tarray=pointasstring.split(",");
	return newPointFromPair(Number(tarray[0]),Number(tarray[1]));
    }
}

//return a rectangle for the device
function deviceRectangle(theDevice)
{
    var centerpoint = convertXYPointToActual(newPointFromString(theDevice.location));
    var delta = imageSize / 2;

    var rect = { 
		spoint : newPointFromPair(centerpoint.x-delta, centerpoint.y-delta),
		height : imageSize,
		width : imageSize,
		epoint : newPointFromPair(centerpoint.x + delta, centerpoint.y + delta),
		center: centerpoint,
	}
    return rect;
}

function makeRectangle(x1, y1, deltax, deltay, offsetx = 0, offsety = 0) {
	//The offset is for when we are drawing on a cached surface.  It adds x or y to the point we are looking at
	var struct = {
		sx: x1,
		sy: y1,
		dx: x1 + deltax,
		dy: y1 + deltay,
		deltay: deltay,
		deltax: deltax,
		offsetx: offsetx,
		offsety: offsety,
	}
	return struct;
}

function makeLine(x1, y1, x2, y2, offsetx = 0, offsety = 0) {
	var linestruct = makeRectangle(x1, y1, x2 - x1, y2 - y1, offsetx, offsety);
	//console.log("Creating a line: " + JSON.stringify(linestruct));
	return linestruct;
}

//Make a structure to hold all our data
function actionStruct(shapeText, shapePoints, theObject=null, funcLeftClick=null, funcRightClick=null, funcMouseover=null) {
	var struct = {
		shapeText: shapeText,
		shapePoints: structuredClone(shapePoints),
		theObject: theObject,
		funcLeftClick: funcLeftClick,
		funcRightClick: funcRightClick,
		funcMouseover: funcMouseover,
	}
	shapePoints.shapeText = shapeText;
	return struct;
}

function registerActionStruct(shapeText, shapePoints, theObject=null, funcLeftClick=null, funcRightClick=null, funcMouseover=null) {
	//Make an object with all the data
	var what = actionStruct(shapeText, shapePoints, theObject, funcLeftClick, funcRightClick, funcMouseover);
	//console.log("Pushing an action: " + shapeText);
	//Push it onto the uiActions list
	uiActions.unshift(what); //Put it at the beginning of the list
	//console.log("ActionList: " + JSON.stringify(uiActions));
}

function clearActionStructs() {
	uiActions = [];
}

//This takes generic information for us to highlight the background
function generic_mouseoverHighlight(point, actionrec) {
	//console.log("Found highlight " + JSON.stringify(actionrec));
	//The point is the place where the mouse is, but the actionrec.shapePoints is the rectangle (or shape) we want to highlight
	var oldrec = structuredClone(ui_highlightRect);
	if (actionrec.shapeText == "square") {
		ui_highlightRect = structuredClone(actionrec.shapePoints);
		ui_highlightRect.shapeText = "square";
		//console.log("setting highlights to:" + JSON.stringify(ui_highlightRect));
	}
	if (actionrec.shapeText == "line") {
		ui_highlightRect = structuredClone(actionrec.shapePoints);
		ui_highlightRect.shapeText = "line";
		//console.log("setting highlights to:" + JSON.stringify(ui_highlightRect));
	}
	if (JSON.stringify(ui_highlightRect) === JSON.stringify(oldrec)) {
		//they are the same.  Nothing to do
	}
	else
		PrintScreen();  //two different areas.  Need to print the screen
}