/*
	Protractor - A Screen Protractor
	Copyright © 2008-2019 Harry Whitfield

	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 2 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, write to the Free Software Foundation, Inc.,
	51 Franklin St, Fifth Floor, Boston, MA	 02110-1301	 USA

	Protractor - browser version 1.2
	28 April 2019
	Copyright © 2008-2019 Harry Whitfield
	mailto:g6auc@arrl.net
*/

/*jslint browser, devel */

/*property
    PI, altKey, atan2, checked, clientX, clientY, filter, floor, forEach,
    getElementById, height, innerHTML, isNaN, onchange, onclick, ondblclick,
    onmousedown, onmousemove, onmouseup, ontouchend, ontouchmove, ontouchstart,
    opacity, open, preventDefault, rotation, round, selectedIndex, setAttribute,
    setValue, src, style, targetTouches, title, toFixed, value, width
*/

/*global newCanvas, newCanvasImage, newDiv, newInput, drawImage, drawText, newImage,
	newText, setPreference, Slider, left, vtop, scale */

////////////////////////////////////// Preferences ///////////////////////////////////////

// Preferences initialised in webstorage.js

var scalePref;
var bgAlphaPref;
var compassAlphaPref;
var compassStylePref;
var rotationPref;
var markerAnglePref;
var markerDirectionPref;
var compassDeltaPref;

var scale = Number(scalePref / 100);

var bgAlpha = Number(bgAlphaPref) / 100;
var compassAlpha = Number(compassAlphaPref) / 100;
var compassDelta = Number(compassDeltaPref);

//////////////////////////////////////// Globals /////////////////////////////////////////

var downRotation;
var downAngle;
var moveEnabled = false;
var fontSize = String(Math.floor(14 * scale)) + "px";
var style = "font-family:'Lucida Grande','Lucida Sans Unicode','Arial'; font-size:" + fontSize + "; font-weight:normal; font-style:normal; opacity:1.0;";

var degrees;
var radians;

var getCompassImage = function (compassStyle) {
	switch (compassStyle) {
	case "Default Compass":
		return "Resources/compass0.png";
	case "Four Point Compass":
		return "Resources/compass1.png";
	case "Eight Point Compass":
		return "Resources/compass2.png";
	}
};

var compassImg = getCompassImage(compassStylePref);

var legend = newCanvas(295 - 295, 295 - 295, 590, 590, "Resources/legend.png", 10, bgAlpha, 295, 295);
var zero = newCanvas(295 - 273, 295 - 273, 546, 546, "Resources/zero.png", 20, 1.0, 273, 273);
var mark = newCanvas(295 - 256, 295 - 256, 512, 512, "Resources/mark.png", 30, 1.0, 256, 256);
var drag = newCanvas(295 - 273, 295 - 273, 546, 546, "Resources/drag.png", 40, 1.0, 273, 273);
var compass = newCanvas(295 - 180, 295 - 180, 360, 360, compassImg, 50, compassAlpha, 180, 180);

var boss = newImage(295 - 50, 295 - 50, 100, 100, "Resources/boss.png", 60, 0.1);
var helpButton = newImage(526, 16 / scale, 30 / scale, 22 / scale, "Resources/help.png", 60, 1.0);

legend.rotation = Number(rotationPref);
zero.rotation = Number(rotationPref);
mark.rotation = Number(markerAnglePref);
drag.rotation = Number(markerAnglePref);
compass.rotation = compassDelta;

function legendLoaded(img) {
	drawImage(legend, img, 0, 0, legend.rotation);
}

function zeroLoaded(img) {
	drawImage(zero, img, 0, 0, zero.rotation);
}

function markLoaded(img) {
	drawImage(mark, img, 0, 0, mark.rotation + zero.rotation);
}

function dragLoaded(img) {
	drawImage(drag, img, 0, 0, drag.rotation + zero.rotation);
}

function compassLoaded(img) {
	drawImage(compass, img, 0, 0, compass.rotation + zero.rotation);
}

var legendImage = newCanvasImage(legend, legendLoaded);
var zeroImage = newCanvasImage(zero, zeroLoaded);
var markImage = newCanvasImage(mark, markLoaded);
var dragImage = newCanvasImage(drag, dragLoaded);
var compassImage = newCanvasImage(compass, compassLoaded);

var ztxtL = newText(295 - 130, 178, 110, 16, "", 60, style + "text-align:right; color: red;");
var dtxtL = newText(295 - 130, 392, 110, 16, "", 60, style + "text-align:right; color: blue;");

var ztxtR = newText(295 + 8, 178, 115, 16, "", 60, style + "text-align:right; color: red;");
var dtxtR = newText(295 + 8, 392, 115, 16, "", 60, style + "text-align:right; color: blue;");

radians = Math.PI * zero.rotation / 180;
ztxtL.innerHTML = zero.rotation + " degrees";
ztxtR.innerHTML = radians.toFixed(4) + " radians";

degrees = drag.rotation;
if (markerDirectionPref === "1") {
	degrees = (360 - degrees) % 360;
}
radians = Math.PI * degrees / 180;

dtxtL.innerHTML = degrees + " degrees";
dtxtR.innerHTML = radians.toFixed(4) + " radians";

drag.title = "Drag to set the blue Angle Marker.\n";
drag.title += "Alt+drag to set the red Relative Rotation Marker.";
compass.title = "Double-click to cycle through the three compass styles";
boss.title = "Double-click to set the Compass Deviation.";
helpButton.title = "Displays information about this program.";

var halfwidth = legend.width / 2;
var halfheight = legend.height / 2;

/////////////////////////////////// Anticlockwise-Key ////////////////////////////////////

var acwKey = newInput(120 / scale, 20 / scale, 8, 16, "anticlockwise", 60);
acwKey.setAttribute("type", "checkbox");
acwKey.title = "Enables/disables anticlockwise scale.";

var acwButton = newInput(20 / scale, 20 / scale, 8, 16, "anticlockwise", 60);
acwButton.setAttribute("type", "button");
acwButton.title = "Enables/disables anticlockwise scale.";

//////////////////////////////////////// Alt-Key /////////////////////////////////////////

var altKey = newInput(85 / scale, 48 / scale, 8, 16, "alt-key", 60);
altKey.setAttribute("type", "checkbox");
altKey.title = "Enables/disables the on-screen alt-key.";

var altButton = newInput(20 / scale, 48 / scale, 8, 16, "alt-lock", 60);
altButton.setAttribute("type", "button");
altButton.title = "Enables/disables the on-screen alt-key.";

altButton.onclick = function () {
	"use strict";
	altKey.checked = !altKey.checked;
};

acwKey.checked = (markerDirectionPref === "1");

//////////////////////////////////// Slider functions ////////////////////////////////////

var slider1X = 40;
var slider2X = 313;
var slidersY = 600;
var testInfoY = 630;

var testInfo1 = newText(slider1X + 28, testInfoY, 200, 20, "", 60, style);
var testInfo2 = newText(slider2X + 45, testInfoY, 200, 20, "", 60, style);

testInfo1.innerHTML = "Protractor Opacity: " + bgAlphaPref + "%";
testInfo2.innerHTML = "Compass Opacity: " + compassAlphaPref + "%";

var callback1 = function (value) {
	value = 0.5 * value;

	bgAlphaPref = value.toFixed(0);
	bgAlpha = value / 100;
	legend.style.opacity = bgAlpha;
	legend.style.filter = "alpha(opacity=" + Math.round(100 * bgAlpha) + ")";

	testInfo1.innerHTML = "Protractor Opacity: " + bgAlphaPref + "%";
	setPreference("bgAlphaPref", bgAlphaPref);
};

var callback2 = function (value) {
	value = 0.5 * value;

	compassAlphaPref = value.toFixed(0);
	compassAlpha = value / 100;
	compass.style.opacity = compassAlpha;
	compass.style.filter = "alpha(opacity=" + Math.round(100 * compassAlpha) + ")";

	testInfo2.innerHTML = "Compass Opacity: " + compassAlphaPref + "%";
	setPreference("compassAlphaPref", compassAlphaPref);
};

// Slider(sliderType, hOffset, vOffset, width, height, left, vtop, imgFolder, zOrder, scale, report)

var slider1 = new Slider("horizontal", slider1X, slidersY, 200, 20, left, vtop, "Resources/", 60, scale, callback1);
var slider2 = new Slider("horizontal", slider2X, slidersY, 200, 20, left, vtop, "Resources/", 60, scale, callback2);

slider1.setValue(200 * bgAlpha);
slider2.setValue(200 * compassAlpha);

////////////////////////////////////// Interface functions ///////////////////////////////

function normalize180(angle) {
	angle = angle % 360;
	if (angle < -180) {
		angle += 360;
	} else if (angle >= 180) {
		angle -= 360;
	}
	return angle;
}

function normalize360(angle) {
	angle = angle % 360;
	if (angle < 0) {
		angle += 360;
	}
	return angle;
}

function checkInteger(s) {
	var value = parseInt(s, 10);
	if (Number.isNaN(value)) {
		value = 0;
	}
	return value;
}

function setRelativeRotation(rotation) {
	var rads;

	rotation = normalize180(checkInteger(rotation));
	legend.rotation = rotation;
	zero.rotation = rotation;
	compass.rotation = rotation + compassDelta;

	rotationPref = String(rotation);

	drawImage(legend, legendImage, 0, 0, legend.rotation);
	drawImage(zero, zeroImage, 0, 0, zero.rotation);
	drawImage(compass, compassImage, 0, 0, compass.rotation);

	drawImage(mark, markImage, 0, 0, mark.rotation + zero.rotation);
	drawImage(drag, dragImage, 0, 0, drag.rotation + zero.rotation);

	rads = Math.PI * rotation / 180;
	ztxtL.innerHTML = rotation + " degrees";
	ztxtR.innerHTML = rads.toFixed(4) + " radians";
}

function getRelativeRotation() {
	var rotation = prompt("Relative Rotation Marker (-180..179 degrees)", zero.rotation);

	if (rotation !== null) {
		setRelativeRotation(rotation);
		setPreference("rotationPref", rotationPref);
	}
}

function setAngleMarker(rotation) {
	var rads;

	rotation = normalize360(checkInteger(rotation));
	mark.rotation = rotation;
	drag.rotation = rotation;

	markerAnglePref = String(rotation);

	drawImage(mark, markImage, 0, 0, mark.rotation + zero.rotation);
	drawImage(drag, dragImage, 0, 0, drag.rotation + zero.rotation);

	if (markerDirectionPref === "1") {
		rotation = (360 - drag.rotation) % 360;
	} else {
		rotation = drag.rotation;
	}

	rads = Math.PI * rotation / 180;
	dtxtL.innerHTML = rotation + " degrees";
	dtxtR.innerHTML = rads.toFixed(4) + " radians";
}

function getAngleMarker() {
	var rotation = prompt("Angle Marker (0..359 degrees)", drag.rotation);

	if (rotation !== null) {
		setAngleMarker(rotation);
		setPreference("markerAnglePref", markerAnglePref);
	}
}

function setCompassDeviation(rotation) {
	rotation = normalize180(checkInteger(rotation));
	compassDelta = rotation;
	compassDeltaPref = String(rotation);
	setRelativeRotation(zero.rotation);
}

function getCompassDeviation() {
	var rotation = prompt("Compass Deviation (-180..179 degrees)", compassDelta);

	if (rotation !== null) {
		setCompassDeviation(rotation);
		setPreference("compassDeltaPref", compassDeltaPref);
	}
}

//////////////////////////////////////// Menu Code ///////////////////////////////////////

var Menu = [];

var MenuArray = [		// id array for menu items defined in the html file
	"selector0"
];

var MenuTitleArray = [	// tooltip array for menu items defined in the html file
	"Protractor Size"
];

var MenuSelectedIndex = [1]; // 100%

(function () {				// initialise the menus
	MenuArray.forEach(function (ele, i) {
		Menu[i] = document.getElementById(ele);	// addToMenu(Menu[i], ele);
		Menu[i].selectedIndex = MenuSelectedIndex[i];
		Menu[i].title = MenuTitleArray[i];
	});
}());

Menu[0].onchange = function () {
	scalePref = Menu[0].value;
	setPreference("scalePref", scalePref);
	setTimeout("location.reload(true);", 1000);
};

var setMenus = function () {
	Menu[0].value = scalePref;
};

setMenus();

//////////////////////////////////////// Event Handlers //////////////////////////////////

acwButton.onclick = function () {
	acwKey.checked = !acwKey.checked;
	markerDirectionPref = (
		acwKey.checked
		? "1"
		: "0"
	);
	setAngleMarker(drag.rotation);
	setPreference("markerDirectionPref", markerDirectionPref);
};

acwKey.onchange = function () {
	markerDirectionPref = (
		acwKey.checked
		? "1"
		: "0"
	);
	setAngleMarker(drag.rotation);
	setPreference("markerDirectionPref", markerDirectionPref);
};

drag.ondblclick = function (evt) {
	evt.preventDefault();

	if (evt.altKey || altKey.checked) {
		getRelativeRotation();
	} else {
		getAngleMarker();
	}
};

drag.onmousedown = function (evt) {
	evt.preventDefault();
	moveEnabled = true;

	if (evt.altKey || altKey.checked) {
		downRotation = zero.rotation;
	} else {
		downRotation = drag.rotation;
	}
	downAngle = (Math.atan2(halfheight - evt.clientY, halfwidth - evt.clientX) / Math.PI) * 180;
};

drag.ontouchstart = function (evt) {
	evt.preventDefault();
	moveEnabled = true;

	if (evt.altKey || altKey.checked) {
		downRotation = zero.rotation;
	} else {
		downRotation = drag.rotation;
	}
	downAngle = (Math.atan2(halfheight - evt.targetTouches[0].clientY, halfwidth - evt.targetTouches[0].clientX) / Math.PI) * 180;
};

drag.onmousemove = function (evt) {
	var angle;

	evt.preventDefault();
	angle = (Math.atan2(halfheight - evt.clientY, halfwidth - evt.clientX) / Math.PI) * 180;
	if (moveEnabled) {
		if (evt.altKey || altKey.checked) {
			setRelativeRotation(Math.round(downRotation + angle - downAngle));
		} else {
			setAngleMarker(Math.round(downRotation + angle - downAngle));
		}
	}
};

drag.ontouchmove = function (evt) {
	var angle;

	evt.preventDefault();
	angle = (Math.atan2(halfheight - evt.targetTouches[0].clientY, halfwidth - evt.targetTouches[0].clientX) / Math.PI) * 180;
	if (moveEnabled) {
		if (evt.altKey || altKey.checked) {
			setRelativeRotation(Math.round(downRotation + angle - downAngle));
		} else {
			setAngleMarker(Math.round(downRotation + angle - downAngle));
		}
	}
};

drag.onmouseup = function (evt) {
	evt.preventDefault();
	moveEnabled = false;
	setPreference("markerAnglePref", markerAnglePref);
	setPreference("rotationPref", rotationPref);
};

drag.ontouchend = function (evt) {
	evt.preventDefault();
	moveEnabled = false;
	setPreference("markerAnglePref", markerAnglePref);
	setPreference("rotationPref", rotationPref);
};

function cycleCompass(evt) {
	evt.preventDefault();

	switch (compassStylePref) {
	case "Default Compass":
		compassStylePref = "Four Point Compass";
		compass.src = "Resources/compass1.png";
		break;
	case "Four Point Compass":
		compassStylePref = "Eight Point Compass";
		compass.src = "Resources/compass2.png";
		break;
	case "Eight Point Compass":
		compassStylePref = "Default Compass";
		compass.src = "Resources/compass0.png";
		break;
	}

	compassImage = newCanvasImage(compass, compassLoaded);
	setPreference("compassStylePref", compassStylePref);
}

compass.ondblclick = cycleCompass;
compass.ontouchstart = cycleCompass;

compass.ontouchend = function () {
	return;
};

boss.ondblclick = function (evt) {
	evt.preventDefault();
	getCompassDeviation();
};

boss.ontouchstart = function (evt) {
	evt.preventDefault();
	getCompassDeviation();
};

boss.ontouchend = function () {
	return;
};

helpButton.onmousedown = function () {
	helpButton.style.opacity = "0.5";
};
helpButton.onmouseup = function () {
	helpButton.style.opacity = "1.0";
	window.open("Help.html");
};
