'use strict';
const rce = React.createElement;
const AGGREGATE = "Aggregate";
const CEMENT = "Cement";
const CONVEYER = "Conveyer"
const materials = [AGGREGATE, CEMENT];

function isMobile(){
    return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino|android|ipad|playbook|silk/i.test(navigator.userAgent||navigator.vendor||window.opera)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test((navigator.userAgent||navigator.vendor||window.opera).substr(0,4)))
}

var precalc = null;
const urlParams = new URLSearchParams(window.location.search);
var DEBUG_REACT = urlParams.get('debug');
var IS_TOUCH_DEVICE = isMobile();
var debug_stuff = {};

var glo_ctrl = false;
var glo_message = null;
var IS_TOUCH_DEVICE_ACTIVE = true;
var glo_state_is_pc = false;


// taken from https://stackoverflow.com/questions/879152/how-do-i-make-javascript-beep
//if you have another AudioContext class use that one, as some browsers have a limit
var audioCtx = new (window.AudioContext || window.webkitAudioContext || window.audioContext);

//All arguments are optional:

//duration of the tone in milliseconds. Default is 500
//frequency of the tone in hertz. default is 440
//volume of the tone. Default is 1, off is 0.
//type of tone. Possible values are sine, square, sawtooth, triangle, and custom. Default is sine.
//callback to use on end of tone
function beep(duration, frequency, volume, type, callback) {
    var oscillator = audioCtx.createOscillator();
    var gainNode = audioCtx.createGain();

    oscillator.connect(gainNode);
    gainNode.connect(audioCtx.destination);

    if (volume){gainNode.gain.value = volume;}
    if (frequency){oscillator.frequency.value = frequency;}
    if (type){oscillator.type = type;}
    if (callback){oscillator.onended = callback;}

    oscillator.start(audioCtx.currentTime);
    oscillator.stop(audioCtx.currentTime + ((duration || 500) / 1000));
};

function soundAlert() {
	beep(200, 490);
	console.log("play tone");
}

const NAMES = {
	'application': 'řídící program dopravy kameniva',
	'YES': 'ANO',
	'NO': 'NE',
}

const VISUAL_BUTTONS = {
	'goToSilo': "Otočný rozdělovač do pozice ",
	'conveyerStartStop': "Start / Stop pasu",
	'conveyerFlush': "Oplach pasu",
	'conveyerVibration': "Vibrace pasu",
	'elevatorStartStop': "Start / Stop elevatoru",
	'hopperFeederLeft': "Vibrační podavač Levý",
	'hopperFeederRight': "Vibrační podavač Pravý",
	'hopperFeederLock': "Zamknutí / odemknutí vibračních podavačů",
}

const precalcStuff = pnodes => {
	console.log(JSON.stringify(pnodes));
	let ret = {
		conveyers: 0,
		orf: 1
	};
	const ks = Object.keys(pnodes).sort();
	for (let material of materials) {
		for (let k of ks) {
			if (k.indexOf(material + "Silo") !== -1) {
				if (ret[material + "Silos"]) {
					ret[material + "Silos"] = ret[material + "Silos"] + 1;
				}
				else {
					ret[material + "Silos"] = 1;
				}
			}
			if (k.indexOf(material + "Box") !== -1) {
				if (ret[material + "Boxes"]) {
					ret[material + "Boxes"] = ret[material + "Boxes"] + 1;
				}
				else {
					ret[material + "Boxes"] = 1;
				}
			}
		}
	}
	console.log('ret:' + JSON.stringify(ret));
	return ret;
}

function difference(object, base) {
	return _.transform(object, (result, value, key) => {
		if (!_.isEqual(value, base[key])) {
			result[key] = _.isObject(value) && _.isObject(base[key]) ? difference(value, base[key]) : value;
		}
	});
}

function dateTimeString() {
  const now = new Date();
  const offsetMs = now.getTimezoneOffset() * 60 * 1000;
  const dateLocal = new Date(now.getTime() - offsetMs);
  return dateLocal.toISOString().slice(0, 19).replace("T", " ");
}

function myShouldComponentUpdate(name, props, nextProps, state, nextState) {
    const x = !_.isEqual(props, nextProps);
	if (DEBUG_REACT && x) {
		console.log('shouldComponentUpdate', name, difference(nextProps, props));
	}
	return x;
}

class Main extends React.Component {
	shouldComponentUpdate(nextProps, nextState) {
		return myShouldComponentUpdate('Main', this.props, nextProps, this.state, nextState);
	}
	render() { return renderMain(this.props); }
}

class NodeSilos extends React.Component {
	shouldComponentUpdate(nextProps, nextState) {
		return myShouldComponentUpdate('NodeSilos', this.props, nextProps, this.state, nextState);
	}
	render() { return renderNodeSilos(this.props);}
}

class NodeBoxes extends React.Component {
	shouldComponentUpdate(nextProps, nextState) {
		return myShouldComponentUpdate('NodeBoxes', this.props, nextProps, this.state, nextState);
	}
	render() { return renderNodeBoxes(this.props);}
}

class NodeOrf extends React.Component {
	shouldComponentUpdate(nextProps, nextState) {
		return myShouldComponentUpdate('NodeOrf', this.props, nextProps, this.state, nextState);
	}
	render() { return renderNodeOrf(this.props);}
}

class NodeTopConveyer extends React.Component {
	shouldComponentUpdate(nextProps, nextState) {
		return myShouldComponentUpdate('NodeTopConveyer', this.props, nextProps, this.state, nextState);
	}
	render() { return renderNodeTopConveyer(this.props);}
}

class NodeElevator extends React.Component {
	shouldComponentUpdate(nextProps, nextState) {
		return myShouldComponentUpdate('NodeElevator', this.props, nextProps, this.state, nextState);
	}
	render() { return renderNodeElevator(this.props);}
}

class NodeBottomConveyer extends React.Component {
	shouldComponentUpdate(nextProps, nextState) {
		return myShouldComponentUpdate('NodeBottomConveyer', this.props, nextProps, this.state, nextState);
	}
	render() { return renderNodeBottomConveyer(this.props);}
}

class NodeHopper extends React.Component {
	shouldComponentUpdate(nextProps, nextState) {
		return myShouldComponentUpdate('NodeHopper', this.props, nextProps, this.state, nextState);
	}
	render() { return renderNodeHopper(this.props);}
}

class NodeMessages extends React.Component {
	shouldComponentUpdate(nextProps, nextState) {
		return myShouldComponentUpdate('NodeMessages', this.props, nextProps, this.state, nextState);
	}
	render() { return renderNodeMessages(this.props);}
}

class NodeMainControl extends React.Component {
	shouldComponentUpdate(nextProps, nextState) {
		return myShouldComponentUpdate('NodeMainControl', this.props, nextProps, this.state, nextState);
	}
	render() { return renderNodeMainControl(this.props);}
}

class NodeSwitchingUserMode extends React.Component {
	shouldComponentUpdate(nextProps, nextState) {
		return myShouldComponentUpdate('NodeSwitchingUserMode', this.props, nextProps, this.state, nextState);
	}
	render() { return renderNodeSwitchingUserMode(this.props);}
}

const renderMain = props => {
	if (DEBUG_REACT) {
		console.log('render Main', props);
		_.updateWith(debug_stuff, ['render_count', 'Main'], x => x ? x + 1 : 1);
	}
	if (!precalc && props.nodes) {
		precalc = precalcStuff(props.nodes);
	}

	let subTree = null;
	if (props.nodes) {	
		let nodes = [];
		for (let material of materials) {
			nodes[material + "Silos"] = [];
			for (var i = 1; i < precalc[material + "Silos"] + 1; i++) {
				nodes[material + "Silos"].push(rce(NodeSilos, {key: material + "Silo" + i, data: props.nodes[material + "Silo" + i], xxx_user_mode: props.nodes["UserMode"]}));
			}
		}
		for (let material of materials) {
			nodes[material + "Boxes"] = [];
			for (var i = 1; i < precalc[material + "Boxes"] + 1; i++) {
				nodes[material + "Boxes"].push(rce(NodeBoxes, {key: material + "Box" + i, data: props.nodes[material + "Box" + i], xxx_user_mode: props.nodes["UserMode"]}));
			}
		}

		if (_.get(props.nodes, ['ORF'])) {
			nodes["ORF"] = [];
			nodes["ORF"].push(rce(NodeOrf, {key: "orf", data: props.nodes["ORF"], xxx_user_mode: props.nodes["UserMode"]}));
		}

		if (_.get(props.nodes, ['TopConveyer'])) {
			nodes["TopConveyer"] = [];
			nodes["TopConveyer"].push(rce(NodeTopConveyer, {key: "TopConveyer", data: props.nodes["TopConveyer"], xxx_user_mode: props.nodes["UserMode"]}));
		}

		if (_.get(props.nodes, ['Elevator'])) {
			nodes["Elevator"] = [];
			nodes["Elevator"].push(rce(NodeElevator, {key: "Elevator", data: props.nodes["Elevator"], xxx_user_mode: props.nodes["UserMode"]}));
		}

		if (_.get(props.nodes, ['Hopper'])) {
			nodes["Hopper"] = [];
			nodes["Hopper"].push(rce(NodeHopper, {key: "Hopper", data: props.nodes["Hopper"], xxx_user_mode: props.nodes["UserMode"]}));
		}

		if (_.get(props.nodes, ['BottomConveyer'])) {
			nodes["BottomConveyer"] = [];
			nodes["BottomConveyer"].push(rce(NodeBottomConveyer, {key: "BottomConveyer", data: props.nodes["BottomConveyer"], xxx_user_mode: props.nodes["UserMode"]}));
		}
		nodes["MainControl"] = []
		nodes["MainControl"].push(rce(NodeMainControl, {key: "MainControl", data: props.nodes["Main"], xxx_user_mode: props.nodes["UserMode"]}));

		nodes["SwitchingUserMode"] = []
		nodes["SwitchingUserMode"].push(rce(NodeSwitchingUserMode, {key: "UserMode", data: props.nodes["UserMode"]}));

		let nodesMessages = [];
		nodes["Messages"] = []
		for (let i in props.nodes["Messages"]) {
			nodes["Messages"].push(rce(NodeMessages, {key: "Messages" + i, data: props.nodes["Messages"][i], xxx_user_mode: props.nodes["UserMode"]}));
		}
		
		const subTreeOpacity = props.xxx_disconnected ? 0.5 : 1;
		
		subTree =
			rce("div", {"key": 100, "style": {"opacity": subTreeOpacity}}, [
				rce("div", {"key": 110, "className": "middle"}, [
					rce("div", {"key": 120, "className": "left"}, [
						rce("div", {"key": 130, "className": "topLeft"}, [
							rce("div", {"key": 160, "className": "content-aggregate"}, [
								nodes["TopConveyer"],
								nodes["ORF"],
								nodes[AGGREGATE + "Silos"],
							]),
						]),
						rce("div", {"key": 170, "className": "bottomLeft"}, [
							rce("div", {"key": 180, "className": "middleLeft"}, [
								nodes["Hopper"],
							]),
						]),
					]),
					rce("div", {"key": 200, "className": "right"}, [
						rce("div", {"key": 210, "className": "topRight"}, [
							nodes["Elevator"],
							rce("div", {"key": 215, "className": "topRightMobileAndHopper"}, [
								nodes["SwitchingUserMode"],
							]),
						]),
						rce("div", {"key": 220, "className": "middleRight scroller"}, [
							nodes["Messages"]
						]),
					]),
				]),
				rce("div", {"key": 230, "className": "footer"}, [
					nodes["MainControl"]
				]),
				<Clock/>,
				DEBUG_REACT ? 
					rce("div", {"key": 240, "className": "debug"}, [
						rce("pre", {"key": 250}, JSON.stringify(debug_stuff, null, 2)),
					])
				: null,
            ]);
	}

	const contentClass = "content " + (props.xxx_disconnected ? "disconnected" : (props.xxx_state_is_pc ? "pcActive" : "pcNotActive"));

	return rce("div", {"key": "1000", "className": contentClass}, [
		rce("div", {"key": "1002", "className": "header"}, [
			IS_TOUCH_DEVICE != true ? rce("div", {"key": "1003", "className": "top-menu"}, [
				rce("div", {"key": "1004", "className": "top-menu-button", onMouseDown: (e) => sendTitleMessage('control:exit', 'Opravdu ukončit řídící program?')}, [
					rce("i", {"key": "4ico", "className": "fa fa-power-off"}),
					"\xa0Ukončit řídící program",
				]),
			]) : null,
		]),
		precalc ? subTree : null,		
	]);
}

const renderNodeSilos = props => {
	if (DEBUG_REACT) {
		console.log('render renderNodeSilos', props);
		_.updateWith(debug_stuff, ['render_count', 'renderNodeSilos'], x => x ? x + 1 : 1);
	}
	
	const MouseDown = IS_TOUCH_DEVICE ? "touchStart" : "onMouseDown";
	const id = props.data['id'];
	const sizeCoeficientWidth = 9;
	const sizeCoeficientHeight = 43;
	const siloSizeWidth = sizeCoeficientWidth + "vw";
	const siloSizeHeight = sizeCoeficientHeight + "vh";
	const siloSizeHeightTop = (sizeCoeficientHeight * 0.1) + "vh";
	const siloSizeHeightBody = (sizeCoeficientHeight * 0.41) + "vh";
	const siloSizeHeightBottom = (sizeCoeficientHeight * 0.12) + "vh";
	const siloContentMarginLeft = (sizeCoeficientWidth * 0.15) + "vw";

	const sensorHeight = (sizeCoeficientHeight * 0.02) + "vh";
	const arrowHeight = (sizeCoeficientHeight * 0.45) + "vh";
	const arrowWidth = (sizeCoeficientWidth * 0.45) + "vw";

	const materialType = _.get(props.data, ['materialType']);

	const number = props.data["idx"];
	const nameShort = props.data["Material"];

	const orfSectionVisible = materialType == AGGREGATE ? "" : "hidden";
	const orfPositionState = props.data['isInPosition'];
	const orfPosition = "orf-position" + (orfPositionState == 1 ? " orf-position-inposition" : "");

	let siloBottom = "silo-bottom " + materialType + (orfPositionState != 1 ? "Dimmed" : "");

	let maximum = _.get(props.data, ['sig_isMax']);
	let minimum = _.get(props.data, ['sig_isMin']);
	const maximumVisibility = maximum ? "" : "hidden";
	const minimumVisibility = minimum ? "" : "hidden";
	let maximumClass = _.get(props.data, ['isMax']) ? 'silo-maximum backgroundRed' : 'silo-maximum';
	let minimumClass = _.get(props.data, ['isMin']) ? 'silo-minimum backgroundRed' : 'silo-minimum';


	let level = _.get(props.data, ['level']);
	let levelClass;
	let levelPercent;
	let levelPercentIvert;
	let levelNumber;
	if (level) {
		levelPercentIvert = '100%';
		levelClass = 'silo-level-box-red';
		if (level < 5) {
			level = 5;
		} else if (level > 100) {
			level = 100;
		}
		levelPercent = level;
		levelPercentIvert = (100 - levelPercent) + '%';
		if (levelPercent > 20) {
			levelClass = 'silo-level-box-green';
		}
		levelNumber = parseFloat(level).toFixed(1);
	}

	const siloDisplayWidth = '100%';
	const siloDisplayHeight = (sizeCoeficientHeight * 0.1) + "vh";
	const style = {width: siloDisplayWidth, height: siloDisplayHeight};
	
	const isMobileActive = _.get(props.xxx_user_mode, ["isMobileActive"]);
	const onMouseDown = IS_TOUCH_DEVICE ? "onTouchStart" : "onMouseDown";
	const canControl = (IS_TOUCH_DEVICE && isMobileActive) || (IS_TOUCH_DEVICE != 1 && isMobileActive != 1);
	
	const divSiloButton = {"key": 5, "className": "silo-body", "style": {"width": siloSizeWidth, "height": siloSizeHeightBody}, title: VISUAL_BUTTONS['goToSilo'] + number};
	if (canControl) {
		divSiloButton[onMouseDown] = (e) => sendApiMessage([id, "goToPosition"], 1, 0);
	}

	return rce("div", {"key": 1, "className": "silo-content", "style": {"width": siloSizeWidth, "height": siloSizeHeight, "marginLeft": siloContentMarginLeft}}, [
		rce("div", {"key": 2, "className": orfPosition, "style": {"height": sensorHeight, "visibility": orfSectionVisible}}),			
		rce("div", {"key": 3, "className": "silo-top", "style": {"width": siloSizeWidth, "height": siloSizeHeightTop}}, [
			rce("div", {"key": 4, "className": "number"}, number),
		]),
		rce("div", divSiloButton, [
			rce("div", {"key": 6, "className": "silo-level-content"}, [
				level ?
					rce("div", {"key": 7, "className": levelClass}, [
						rce("div", {"key": 8, "className": "level", "style": {"height": levelPercentIvert}})
					]) : null,
			]),
			rce("div", {"key": 9, "className": "silo-display-content"}, [
				rce("div", {"key": 10, "className": maximumClass, "style": {"visibility": maximumVisibility}}),
				level ? rce("div", {"key": 11, "className": "silo-requestDisplay", "style": style}, levelNumber) : null,
				rce("div", {"key": 12, "className": minimumClass, "style": {"visibility": minimumVisibility}}),
			]),
		]),
		rce("div", {"key": 13, "className": siloBottom, "style": {"width": siloSizeWidth, "height": siloSizeHeightBottom}}, [
			rce("div", {"key": 14, "className": "nameShort"}, nameShort),
		]),
	]);
}


const renderNodeBoxes = props => {
	if (DEBUG_REACT) {
		console.log('render renderNodeBoxes', props);
		_.updateWith(debug_stuff, ['render_count', 'renderNodeBoxes'], x => x ? x + 1 : 1);
	}
	const id = props.data['id'];
	const number = props.data["idx"];
	const nameShort = props.data["Material"];
	const active = _.get(props.data, ["freePath"]);
	const boxFlapClass = active ? "boxFlap-active" : "boxFlap";
	const isSensorMaterial = _.get(props.data, ["sig_isMaterial"]);
	const isMaterial = _.get(props.data, ["isMaterial"]);
	const sensorMaterialClass = "boxSensor" + (isMaterial ? " activeSignal": "");
	
	const isMobileActive = _.get(props.xxx_user_mode, ["isMobileActive"]);
	const onMouseDown = IS_TOUCH_DEVICE ? "onTouchStart" : "onMouseDown";
	const canControl = (IS_TOUCH_DEVICE && isMobileActive) || (IS_TOUCH_DEVICE != 1 && isMobileActive != 1);
	
	let arrowIcon = "fad fa-arrow-alt-down";
	let boxes = [];
	for (var i = 1; i < props.data["countOfFlaps"] + 1; i++) {
		let k = "box" + number + "flap" + i;
		let isClosed = _.get(props.data, ['isClosedFlap' + i]);
		let sensorClass = "boxFlapSensor " + (isClosed ? "" : "opened");
		let setOpenFlap = "open_flap" + i;
		let unsetOpenFlap = "open_flap" + i;
		let isOpening = _.get(props.data, ['isOpeningFlap' + i]);
		let arrowClass = "boxFlapArrow" + (isOpening ? " active" : "");
		
	
		let divArrowButton = {"className": arrowClass, "key": k + "arrow"};
		if (canControl) {
			divArrowButton[onMouseDown] = (e) => sendApiMessage([id, setOpenFlap], 1);
			divArrowButton[onMouseUp] = (e) => sendApiMessage([id, unsetOpenFlap], 0);
		}
			
		boxes.push(
			rce("div", {"className": "boxFlap-content", "key": k + "content"}, [
				rce("div", {"className": boxFlapClass, "key":k + "flap"}, [
					rce("div", {"className": "boxFlapNumber", "key": k + "number"}, [
						i
					]),
				]),
				rce("div", {"className": sensorClass, "key": k + "sensor"}),
				rce("div", divArrowButton, [
					rce('i', {"key": k + "icon", "className": "icon-arrow-down"}),
				]),
			])
		);
	}

	return rce('div', {'key': 1, "className": "box"}, [
		rce('div', {'key': 2, "className": "boxTop"}, [
			rce('div', {'key': 3, "className": "boxNumber"}, [
				number
			]),
			rce('div', {'key': 4, "className": "boxName"}, [
				nameShort
			]),
		]),
		rce('div', {'key': 5, "className": "boxFlaps"}, [
			boxes
		]),
		isSensorMaterial ?
			rce('div', {'key': 6, "className": sensorMaterialClass}, [
				rce('i', {"key": 7, "className": "fas fa-ellipsis-h"})
			])
		:
		null,			
	]);
}


const renderNodeOrf = props => {
	if (DEBUG_REACT) {
		console.log('render renderNodeOrf', props);
		_.updateWith(debug_stuff, ['render_count', 'renderNodeOrf'], x => x ? x + 1 : 1);
	}
	const leftArrow = "orf-arrow" + (props.data["runningLeft"] ? " active" : "");
	const rightArrow = "orf-arrow" + (props.data["runningRight"] ? " active" : "")
	return rce('div', {'key': 1, "className": "orf-content"}, [
		rce('div', {'key': 2, "className": "orf-body"}, [
			rce('div', {'key': 3, "className": leftArrow}, [
				rce('i', {"key": 4, "className": "fas fa-angle-double-left"})
			]),
			rce('div', {'key': 5, "className": rightArrow}, [
				rce('i', {"key": 6, "className": "fas fa-angle-double-right"})
			]),
		]),
	]);
}

const renderNodeSwitchingUserMode = props => {
	const icon = IS_TOUCH_DEVICE ? "fas fa-tablet-alt" : "fas fa-desktop";
	const sentence = "Opravdu chcete přepnout ovládání z " + ( IS_TOUCH_DEVICE_ACTIVE ? "Mobilního zařízení na PC?" : "PC na mobilní zařízení?");
	const contentClass = "content-userMode " + (IS_TOUCH_DEVICE ? (IS_TOUCH_DEVICE_ACTIVE ? "userModeMobileActive" : "userModeMobileNotActive") : (IS_TOUCH_DEVICE_ACTIVE ? "userModeMobileNotActive" : "userModeMobileActive"));
	return rce('div', {'key': 1, "className": contentClass, onMouseDown: (e) => sendTitleMessage('UserMode', sentence)}, [
		rce('i', {"key": 2, "className": icon})
	]);
}

const renderNodeHopper = props => {
	const id = props.data['id'];
	const nameShort = props.data["Material"];
	const hopperFeederIsMaterialSensorLeft = _.get(props.data, ['sig_isMaterialOnVibratingFeederLeft']);
	const hopperFeederMaterialSensorLeft = "fas fa-ellipsis-h" + (_.get(props.data, ['isMaterialOnVibratingFeederLeft']) ? " activeSignal" : "");
	const hopperFeederIsVibratorLeft = _.get(props.data, ['sig_vibrationFeederLeft']);
	const hopperFeederVibrationButtonLeft = "hopperControl isButton" + (_.get(props.data, ['isVibrationFeederLeft']) ? " activeSignal" : "");
	const hopperFeederIsMaterialSensorRight = _.get(props.data, ['sig_isMaterialOnVibratingFeederRight']);
	const hopperFeederMaterialSensorRight = "fas fa-ellipsis-h" + (_.get(props.data, ['isMaterialOnVibratingFeederRight']) ? " activeSignal" : "");
	const hopperFeederIsVibratorRight = _.get(props.data, ['sig_vibrationFeederRight']);
	const hopperFeederVibrationButtonRight = "hopperControl isButton" + (_.get(props.data, ['isVibrationFeederRight']) ? " activeSignal" : "");
	const hopperFeederMaterialSensors = hopperFeederIsMaterialSensorLeft && hopperFeederIsMaterialSensorRight ? 1 : 0;
	const hopperFeederVibrators = hopperFeederIsVibratorLeft && hopperFeederIsVibratorRight ? 1 : 0;
	const hopperFeederVibratorsLock = _.get(props.data, ['sig_lock']);
	const hopperFeederVibratorLockState = _.get(props.data, ['isLocked']);
	const hopperFeederVibratorLockButton = "hopperControl isButton isLockButton" + (hopperFeederVibratorLockState ? " red" : " green");
	const hopperFeederVibratorLockButtonIcon = hopperFeederVibratorLockState ? "fa fa-lock" : "fa fa-lock-open";

	const isMobileActive = _.get(props.xxx_user_mode, ["isMobileActive"]);
	const onMouseDown = IS_TOUCH_DEVICE ? "onTouchStart" : "onMouseDown";
	const canControl = (IS_TOUCH_DEVICE && isMobileActive) || (IS_TOUCH_DEVICE != 1 && isMobileActive != 1);
	const divHopperFeederVibrationButtonLeft = {"key": 5, "className": hopperFeederVibrationButtonLeft, title: VISUAL_BUTTONS['hopperFeederLeft']};
	const divHopperFeederVibrationButtonRight = {"key": 6, "className": hopperFeederVibrationButtonRight, title: VISUAL_BUTTONS['hopperFeederRight']};
	const divHopperFeederVibratorLockButton = {"key": 7, "className": hopperFeederVibratorLockButton, title: VISUAL_BUTTONS['hopperFeederLock']};
	if (canControl) { 
		divHopperFeederVibrationButtonLeft[onMouseDown] = (e) => sendApiMessage([id, "vibrationFeederLeft"], 1, 0);
		divHopperFeederVibrationButtonRight[onMouseDown] = (e) => sendApiMessage([id, "vibrationFeederRight"], 1, 0);
		divHopperFeederVibratorLockButton[onMouseDown] = (e) => sendApiMessage([id, "lock"], hopperFeederVibratorLockState ? 0 : 1);
	}

	if (DEBUG_REACT) {
		console.log('render renderNodeHopper', props);
		_.updateWith(debug_stuff, ['render_count', 'renderNodeHopper'], x => x ? x + 1 : 1);
	}
	return rce("div", {"key": 1, "className": "content-hopper"}, [
		rce("div", {"key": 2, "className": "hopper"}, [
			nameShort
		]),
		hopperFeederMaterialSensors ?
			rce("div", {"key": 3, "className": "hopperSensors"}, [
				hopperFeederIsMaterialSensorLeft ? rce("i", {"key": "3sensorL", "className": hopperFeederMaterialSensorLeft}) : null,
				hopperFeederIsMaterialSensorRight ? rce("i", {"key": "3sensorR", "className": hopperFeederMaterialSensorRight}) : null,
			])
		: null,
		hopperFeederVibrators ?
			rce("div", {"key": 4, "className": "hopperVibrators"}, [
				rce('div', divHopperFeederVibrationButtonLeft, [
					rce('i', {"key": "5ico", "className": "icon-vibration"}),
				]),
				rce('div', divHopperFeederVibrationButtonRight, [
					rce('i', {"key": "6ico", "className": "icon-vibration"}),
				]),
			])
		: null,
		rce("div", {"key": 14, "className": "hopperVibrators"}, [
			rce('div', divHopperFeederVibratorLockButton, [
				rce('i', {"key": "7ico", "className": hopperFeederVibratorLockButtonIcon}),
			]),
		]),
	]);
}

const renderNodeTopConveyer = props => {
	if (DEBUG_REACT) {
		console.log('render renderNodeTopConveyer', props);
		_.updateWith(debug_stuff, ['render_count', 'renderNodeTopConveyer'], x => x ? x + 1 : 1);
	}
	// const mouseDown = "onMouseDown";
	const id = props.data['id'];
	const isRunning = _.get(props.data, ['isRunning']);
	const action = isRunning ? "stop" : "start";
	const running = isRunning ? "topConveyer-run" : "topConveyer-idle";
	const isVibrating = _.get(props.data, ['isVibrating']);
	const conveyerControlStartButton = "topConveyerControl isButton" + (isRunning ? " red": " green");
	const conveyerArrow = "topConveyerArrow" + (isRunning ? " activeSignal": "");
	const conveyerVibrationButton = "bottomConveyerControl isButton" + (isVibrating ? " activeSignal": "");
	const isMobileActive = _.get(props.xxx_user_mode, ["isMobileActive"]);
	const onMouseDown = IS_TOUCH_DEVICE ? "onTouchStart" : "onMouseDown";
	const canControl = (IS_TOUCH_DEVICE && isMobileActive) || (IS_TOUCH_DEVICE != 1 && isMobileActive != 1);
	
	const divStartButton = {"key": 6, "className": conveyerControlStartButton, "style": {"fontSize": "calc(1.5vw + 1.5vh)"}, title: VISUAL_BUTTONS['conveyerStartStop']};
	const divVibrationButton = {"key": 12, "className": conveyerVibrationButton, "style": {"fontSize": "calc(1.5vw + 1.5vh)"}, title: VISUAL_BUTTONS['conveyerVibration']};
	if (canControl) {
		divStartButton[onMouseDown] = (e) => sendApiMessage([id, action], 1, 0);
		divVibrationButton[onMouseDown] = (e) => sendApiMessage([id, "vibration"], 1, 0);
	}
	
	return rce('div', {"key": 1, "className": "topConveyer-content"}, [
		rce('div', {"key": 2, "className": "topConveyer"}, [
			rce('div', {"key": 3, "className": "topConveyerRotationBackground"}, [
				rce('div', {"key": 4, "className": running}),
			]),
			rce('div', {"key": 5, "className": "topConveyer-control-content"}, [
				rce("div", divVibrationButton, [
					rce("i", {"key": 12 + "ico", "className": "icon-vibration"}),
				]),
				rce('div', divStartButton, [
					rce('i', {"key": 7, "className": "far fa-dot-circle"}),
				]),
				rce('div', {"key": 8, "className": conveyerArrow, "style": {"fontSize": "calc(1.5vw + 1.5vh)"}}, [
					rce('i', {"key": 9, "className": "fa fa-angle-double-left"}),
				]),
			]),
		]),
	]);
}

const renderNodeElevator = props => {
	if (DEBUG_REACT) {
		console.log('render renderNodeElevator', props);
		_.updateWith(debug_stuff, ['render_count', 'renderNodeElevator'], x => x ? x + 1 : 1);
	}
	const id = props.data['id'];
	const isRunning = _.get(props.data, ['isRunning']);
	const action = isRunning ? "stop" : "start";
	const running = "elevator" + ( isRunning ? "-run" : "-idle");
	const elevatorControlStartButton = "elevatorControl isButton" + (isRunning ? " red": " green");
	const elevatorArrow = "elevatorArrow" + (isRunning ? " activeSignal": "");
	const consistency = _.get(props.data, ['analogConstistency']);
	const isConsistency = _.get(props.data, ['sig_analogConsistency']);
	const consistencyStr = isConsistency ? consistency + "%" : "";
	
	const isMobileActive = _.get(props.xxx_user_mode, ["isMobileActive"]);
	const onMouseDown = IS_TOUCH_DEVICE ? "onTouchStart" : "onMouseDown";
	const onMouseUp = IS_TOUCH_DEVICE ? "onTouchEnd" : "onMouseUp";
	const onMouseLeave = IS_TOUCH_DEVICE ? "onTouchEnd" : "onMouseLeave";
	const canControl = (IS_TOUCH_DEVICE && isMobileActive) || (IS_TOUCH_DEVICE != 1 && isMobileActive != 1);
	
	const divStartButton = {"key": 6, "className": elevatorControlStartButton, "style": {"fontSize": "calc(1.5vw + 1.5vh)"}, title: VISUAL_BUTTONS['conveyerStartStop']};
	if (canControl) {
		divStartButton[onMouseDown] = (e) => sendApiMessage([id, action], 1, 0);
	}

	return 	rce('div', {'key': 1, "className": "elevator-content"}, [
		rce('div', {'key': 2, "className": "elevator"}, [
			rce('div', {'key': 3, "className": "elevatorRotationBackground"}, [
				rce('div', {'key': 4, "className": running}),
			]),
		]),
		rce('div', {'key': 5, "className": "elevator-control-content"}, [
			rce('div', divStartButton, [
				rce('i', {"key": 7, "className": "far fa-dot-circle"}),
			]),
			rce('div', {"key": 8, "className": elevatorArrow, "style": {"fontSize": "calc(1.5vw + 1.5vh)"}}, [
				rce('i', {"key": 9, "className": "fa fa-angle-double-up"}),
			]),
		]),
		rce('div', {'key': 10, "className": "elevatorConsistency"}, [
			consistencyStr
		]),
	]);
}


const renderNodeMainControl = props => {
	if (DEBUG_REACT) {
		console.log('render renderNodeMainControl', props);
		_.updateWith(debug_stuff, ['render_count', 'renderNodeMainControl'], x => x ? x + 1 : 1);
	}
	const id = props.data['id'];
	const isRunning = _.get(props.data, ['isRunning']);
	const hornButtonClass = "functionsKey-button" + (_.get(props.data, ['isHorn']) ? " active" : "");
	const startButtons = "functionsKey-button" + (isRunning ? " disabled" : "");
	const stopButton = "functionsKey-button" + (isRunning ? "" : " disabled");
	
	const isMobileActive = _.get(props.xxx_user_mode, ["isMobileActive"]);
	const onMouseDown = IS_TOUCH_DEVICE ? "onTouchStart" : "onMouseDown";
	const canControl = (IS_TOUCH_DEVICE && isMobileActive) || (IS_TOUCH_DEVICE != 1 && isMobileActive != 1);
	
	let keys = [];
	if (_.get(props.data, ['sig_cementSilosVibrationStart'])) {
		const isCementSilosVibration = _.get(props.data, ['isCementSilosVibration']);
		const divStartCementSilosVibrationButton = {"key": 14, "className": "functionsKey-button" + (isCementSilosVibration ? " active" : "")};
		if (canControl) {
			divStartCementSilosVibrationButton[onMouseDown] = (e) => sendApiMessage([id, 'cementSilosVibrationStart'], 1, 0);
		}
		keys.push(rce("div", divStartCementSilosVibrationButton, ["Oklepy filtrů sil"]));
	}
	/*
	for (let i = 1; i < 5; i++) {
		if (_.get(props.data, ['sig_fillByFlap' + i])) {
			const flapActive = _.get(props.data, ['isFillByFlap' + i]);
			const action = flapActive ? 0 : 1;
			const buttonClass = "functionsKey-button" + (flapActive ? " active" : "");			
			const divButton = {"key": i, "className": buttonClass};
			if (canControl) {
				divButton[onMouseDown] = (e) => sendApiMessage([id, "fillByFlap" + i, action]);
			}
			keys.push(rce("div", divButton, ["Vsyp " + i]));			
		}
	}
	if (_.get(props.data, ['sig_startFillingByBottomConveyer'])) {
		const divStartFillingByBottomConveyerButton = {"key": 10, "className": startButtons};
		if (canControl) {
			divStartFillingByBottomConveyerButton[onMouseDown] = (e) => isRunning != 1 ? sendApiMessage([id, 'startFillingByBottomConveyer', 1]) : null;
			divStartFillingByBottomConveyerButton[onMouseUp] = (e) => sendApiMessage([id, 'startFillingByBottomConveyer', 0]);
			divStartFillingByBottomConveyerButton[onMouseLeave] = (e) => sendApiMessage([id, 'startFillingByBottomConveyer', 0]);
		}
		keys.push(rce("div", divStartFillingByBottomConveyerButton, ["Start pasem"]));
	}
	*/
	if (_.get(props.data, ['sig_startFillingByElevatorOnly'])) {
		const divStartFillingByElevatorOnlyButton = {"key": 11, "className": startButtons};
		if (canControl) {
			divStartFillingByElevatorOnlyButton[onMouseDown] = (e) => isRunning != 1 ? sendApiMessage([id, 'startFillingByElevatorOnly'], 1, 0) : null;
		}
		keys.push(rce("div", divStartFillingByElevatorOnlyButton, ["Start násypkou"]));
	}
	if (_.get(props.data, ['sig_stopFilling'])) {
		const divStopFillingButton = {"key": 12, "className": stopButton};
		if (canControl) {
			divStopFillingButton[onMouseDown] = (e) => isRunning ? sendApiMessage([id, 'stopFilling'], 1, 0) : null;
		}
		keys.push(rce("div", divStopFillingButton, ["Stop dopravy"]));
	}
	if (_.get(props.data, ['sig_horn'])) {
		const divHornButton = {"key": 13, "className": hornButtonClass};
		if (canControl) {
			divHornButton[onMouseDown] = (e) => sendApiMessage([id, 'horn'], 1, 0);
		}
		keys.push(rce("div", divHornButton, [ rce("i", {"key": 13 + "horn", "className": "fa fa-bullhorn"})]));
	}

	return rce("div", {"className": "functionKeys-content"}, [
		keys
	]);
}

const renderNodeMessages = props => {
	if (DEBUG_REACT) {
		console.log('render NodeMessages', props);
		_.updateWith(debug_stuff, ['render_count', 'NodeMessages'], x => x ? x + 1 : 1);
	}
	const _id = props.data['k'];
	let options = [];
	for (let idx in props.data['options']) {
		const option = props.data['options'][idx];
		options.push(rce('div', {
			"key": props.data['k'],
			"onClick": e => {sendApiMessage(["Messages", 'set_message_answer', props.data['k']], option['id'])},
			"className": props.xxx_auto_confirm && option['id'] == 'yes' ? "message-button active" : "message-button",
			}, option['text']));
	}
	const type = "message " + props.data['level'];
	const message = props.data['msg'];
	// the "key" stuff below is there only to silence react warning - investigate how to solve this better
	//if (props.xxx_auto_confirm) {
		//sendApiMessage([props.data['node_id'], 'set_message_answer', props.data['k'], "yes"]);
	//}

	return rce('div', {"className": "messageContent", "key": _id},
		rce('div', {"className": type, "key": 0}, [
			rce('div', {"className": "message-text", "key": 1}, message),
			rce('div', {"className": "message-buttons", "key": 2}, options),
		])
	);
}


class NodeKeyDetector extends React.PureComponent {
	componentDidMount () {
		//console.log('NodeKeyDetector componentDidMount');
		window.onkeydown = this.onkeydown.bind(this);
		window.onkeyup = this.onkeyup.bind(this);
		window.onmouseup = this.onmouseup.bind(this);
		window.ontouchend = this.ontouchend.bind(this);
		this.setState({});
	}
	onkeydown (e) {
		let keys = this.state;
		let prevent = true;
		if (e.repeat) {
			// nothing
		} else if (e.ctrlKey && e.keyCode == 'D'.charCodeAt(0)) {
			DEBUG_REACT = !DEBUG_REACT;
		} else if (e.keyCode == 17 || e.key == 'Control') {
			keys = {...keys, 'CTRL': true};
		} else {
			prevent = false;
		}
		if (prevent) {
			e.preventDefault();
		}
		this.setState(keys);
		glo_ctrl = keys['CTRL'];
	}
	onkeyup (e) {
		let keys = this.state;
		let prevent = true;
		if (e.repeat) {
			// nothing
		} else if (e.key == 17 || e.key == 'Control') {
			keys = {...keys, 'CTRL': false};
		} else {
			prevent = false;
		}
		if (prevent) {
			e.preventDefault();
		}
		this.setState(keys);
		glo_ctrl = keys['CTRL'];
	}
	onmouseup (e) {
		// console.log('mouse up');
		sendApiMessageMouseUp();
	}
	ontouchend (e) {
		// console.log('touch end');
		sendApiMessageMouseUp();
	}
	render() {
		/*console.log('nodicek', this.state);*/
		return rce(NodeEventSource, {xxx_keys: this.state});
	}
}


function sendApiMessage(msg, down, up) {
	$.ajax({url: '/' + msg.join('/') + '/' + down, timeout: 5000});
	if (up != null) {
		glo_message = msg.join('/') + '/' + up;
	}
}


function sendApiMessageMouseUp() {
	if (glo_message) {
		$.ajax({url: '/' + glo_message, timeout: 5000});
		glo_message = null;
	}
}


// TODO: what an ugly name!
function sendTitleMessage(msg, dialogMessage='') {
	if (dialogMessage) {
		alertify.confirm(dialogMessage, e => {
			if (e) {
				if (msg == 'control:exit') {
					document.title = msg;
					document.title = 'control';
					sendApiMessage(['control','exit'])
					window.close();
				}
				if (msg == 'UserMode') {
					document.title = msg;
					document.title = 'control';
					
					sendApiMessage(['UserMode',IS_TOUCH_DEVICE_ACTIVE ? 0 : 1])
				}
			}
		}).setHeader(NAMES['application']).set('labels', {ok: NAMES['YES'], cancel: NAMES['NO']});
	} else {
		document.title = msg;
		document.title = 'control';
		if (msg == 'control:exit') {
			window.close();
		}
	}
}


var lastCountOfMessages = 0;
var lastComunicationError = 0;
class NodeEventSource extends React.PureComponent {
	init() {
		let evtSource = new EventSource('/stream');
		evtSource.onmessage = this.onmessage.bind(this);
		evtSource.onerror = this.onerror.bind(this);
		this.setState({evtSource: evtSource});
	}
	onmessage(e) {
		const data = JSON.parse(e.data);
		const isDisconnected = _.get(data, ['Router', 'connected']) == 0;
		if (isDisconnected && lastComunicationError == 0) {
			alertify.error('[ ERROR 406 ]<br>ztráta komunikace s řídící jednotkou. Řídící rozvaděč vypnut', 3, function() { lastComunicationError = 0; });
			lastComunicationError = 1;
		}
		if (isDisconnected != 1 && lastComunicationError) {
			lastComunicationError = 0;
		}
		const countOfMessages = Object.keys(_.get(data, ['Messages'])).length;
		if (countOfMessages > lastCountOfMessages) {
			soundAlert();
		}
		lastCountOfMessages = countOfMessages;
		this.setState({data: data, isDisconnected: isDisconnected});
	}
	onerror(e) {
		this.state.evtSource.close();
		console.log('sse error');
		alertify.error('[ ERROR 404 ]<br>obnovení výroby je možné až po ukončení systému a jeho opětovném startu', 5);
		this.setState({evtSource: null, isDisconnected: true});
		setTimeout(this.init.bind(this), 2000);
	}
	componentDidMount() {
		this.init();
	}
	render() {
		const isDisconnected = _.get(this.state, ["isDisconnected"]);
		const state_is_pc = _.get(this.state, ['data', 'PC', 'isActive']);
		const state_is_mobile_active = _.get(this.state, ['data', 'UserMode', 'isMobileActive']);
		glo_state_is_pc = IS_TOUCH_DEVICE ? (IS_TOUCH_DEVICE_ACTIVE ? state_is_pc : 0) : (IS_TOUCH_DEVICE_ACTIVE ? 0 :state_is_pc);
		IS_TOUCH_DEVICE_ACTIVE = state_is_mobile_active;
		return rce(Main, {...this.props, nodes: _.get(this.state, ["data"]), xxx_state_is_pc: glo_state_is_pc, xxx_disconnected: isDisconnected});
	}
}

class Clock extends React.Component {
	constructor() {
		super();
		this.state = {clockStr: ""};
	}
	componentDidMount() {
		setInterval(() => {this.setState({key: 50001, clockStr: dateTimeString()})}, 1000);
	}
	render() {
		return rce("div", {key: 50000, className: "clock"}, this.state.clockStr);
	}
}

ReactDOM.render(rce(NodeKeyDetector), document.getElementById('content'));
