'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");
}


// hyperscript helper - stupid version
// TODO: add support for id (div.class#id)
const h = (tag, props, children) => {
  if (tag.indexOf('.') < 0) { // TODO: does this really make it faster?
    return rce(tag || 'div', props, React.Children.toArray(children))
  }
  const parts = tag.split('.')
  const tag_ = parts[0]
  const classes = parts.slice(1)
  const props_ = props || {}
  if (classes) {
    props_.className = (props_.className ? props_.className + ' ' : '') + classes.join(' ')
  }
  return rce(tag_ || 'div', props_, React.Children.toArray(children))
}


const NAMES = {
	'application': 'řídící program dopravy kameniva',
	'YES': 'ANO',
	'NO': 'NE',
	'AggregateSilo1': 'Zásobník kemeniva 1',
	'AggregateSilo2': 'Zásobník kemeniva 2',
	'AggregateSilo3': 'Zásobník kemeniva 3',
	'AggregateSilo4': 'Zásobník kemeniva 4',
	'AggregateSilo5': 'Zásobník kemeniva 5',
	'AggregateSilo6': 'Zásobník kemeniva 6',
}

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 NodeMessagesConcretecart extends React.Component {
	shouldComponentUpdate(nextProps, nextState) {
		return myShouldComponentUpdate('NodeMessagesConcretecart', this.props, nextProps, this.state, nextState);
	}
	render() { return renderNodeMessagesConcretecart(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);}
}

class NodeConcreteCart extends React.Component {
	shouldComponentUpdate(nextProps, nextState) {
		return myShouldComponentUpdate('NodeConcreteCart', this.props, nextProps, this.state, nextState);
	}
	render() { return renderNodeConcreteCart(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 = [];
    
    const pasovkaLetnany = _.get(props.nodes, ["ORF"]);

		let nodesMessages = [];
		nodes["Messages"] = []
		for (let i in props.nodes["Messages"]) {
      if (pasovkaLetnany) {
        nodes["Messages"].push(rce(NodeMessages, {key: "Messages" + i, data: props.nodes["Messages"][i], xxx_user_mode: props.nodes["UserMode"]}));  
      }
      else {
        nodes["Messages"].push(rce(NodeMessagesConcretecart, {key: "Messages" + i, data: props.nodes["Messages"][i], xxx_user_mode: props.nodes["UserMode"]}));  
      }
		}
    //MJ42 Skanska Letnany pasovka detect and show right content
    if (pasovkaLetnany) {
      
      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, ['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"]}));

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


      subTree =
        h("", {}, [
          h(".middle", {}, [
            h(".left", {}, [
              h(".topLeft", {}, [
                rce("div", {"key": 140, "className": "content-cement"}, [
                  rce("div", {"key": 150 ,"className": "cementSpacer"}),
                  nodes[CEMENT + "Silos"]
                ]),
                rce("div", {"key": 160, "className": "content-aggregate"}, [
                  _.get(props.nodes, ['TopConveyer'])
                    ? rce(NodeTopConveyer, {key: "TopConveyer", data: props.nodes["TopConveyer"], xxx_user_mode: props.nodes["UserMode"]})
                    : null,
                  _.get(props.nodes, ["ORF"])
                    ? rce(NodeOrf, {key: "orf", data: props.nodes["ORF"], xxx_user_mode: props.nodes["UserMode"]})
                    : null,
                  nodes[AGGREGATE + "Silos"],
                ]),
              ]),
              rce("div", {"key": 170, "className": "bottomLeft"}, [
                rce("div", {"key": 180, "className": "middleLeft"}, [
                  rce("div", {"key": 190, "className": "content-boxes"}, [
                    nodes[AGGREGATE + "Boxes"],
                  ]),
                  nodes["BottomConveyer"],
                ]),
              ]),
            ]),
            rce("div", {"key": 200, "className": "right"}, [
              rce("div", {"key": 210, "className": "topRight"}, [
                nodes["Elevator"],
                rce("div", {"key": 215, "className": "topRightMobileAndHopper"}, [
                  nodes["SwitchingUserMode"],
                  nodes["Hopper"],
                ]),
              ]),
              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,
        ]);
    }
    else if (_.get(props.nodes, ['ConcreteCart'])) {
            
      subTree =
        h(".c_main", {}, [
          h(".c_left", {}, [
            rce(NodeConcreteCart, {key: "ConcreteCart", data: props.nodes["ConcreteCart"]}),
          ]),
          h(".c_right", {}, [
            h(".c_rightTop", {}, [
              nodes["Messages"]
            ]),
            h(".c_rightBottom", {}, [
              <ClockC/>,
            ]),
          ])
        ])
    }
	}
  const manual = (_.get(props.nodes, ['ConcreteCart', 'isManual'])) ? 1 : 0

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

  //MJ42 Skanska Letnany pasovka detect and show right content
  const pasovkaContentVisible = _.get(props.nodes, ["ORF"]);
  const pasovkaContent = [
      rce("div", {"key": "1002", "className": "header"}, [
        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",
          ]),
          rce("div", {"key": "1005", "className": "top-menu-button", onMouseDown: (e) => openPlacement()}, [
            "Umístění materiálů",
          ]),
        ]),
      ]),
    ];
  
  const subTreeOpacity = props.xxx_disconnected ? 0.5 : 1;
  

	return h("", {"className": contentClass}, [
    pasovkaContentVisible ? pasovkaContent : null,
    h("", {"style": {"opacity": subTreeOpacity}}, [
      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 = 5;
	const sizeCoeficientHeight = 23;
	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 onMouseUp = IS_TOUCH_DEVICE ? "onTouchEnd" : "onMouseUp";
	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(0.8vw + 0.8vh)"}, title: VISUAL_BUTTONS['conveyerStartStop']};
	const divVibrationButton = {"key": 12, "className": conveyerVibrationButton, "style": {"fontSize": "calc(0.9vw + 0.9vh)"}, 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(1vw + 1vh)"}}, [
					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 canControl = (IS_TOUCH_DEVICE && isMobileActive) || (IS_TOUCH_DEVICE != 1 && isMobileActive != 1);
	
	const divStartButton = {"key": 6, "className": elevatorControlStartButton, "style": {"fontSize": "calc(0.8vw + 0.8vh)"}, 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(1vw + 1vh)"}}, [
				rce('i', {"key": 9, "className": "fa fa-angle-double-up"}),
			]),
		]),
		rce('div', {'key': 10, "className": "elevatorConsistency"}, [
			consistencyStr
		]),
	]);
}

const renderNodeBottomConveyer = props => {
	if (DEBUG_REACT) {
		console.log('render renderNodeBottomConveyer', props);
		_.updateWith(debug_stuff, ['render_count', 'renderNodeBottomConveyer'], x => x ? x + 1 : 1);
	}
	const id = props.data['id'];
	const isRunning = _.get(props.data, ['isRunning']);
	const isFlushing = _.get(props.data, ['isFlushing']);
	const isVibrating = _.get(props.data, ['isVibrating']);
	const action = isRunning ? "stop" : "start";
  const isSlow = _.get(props.data, ['isSlow']);
	const running = isRunning ? "bottomConveyer-run" + ( isSlow ? "-slow" : "" ) : "bottomConveyer-idle";
	const conveyerControlStartButton = "bottomConveyerControl isButton" + (isRunning ? " red": " green");
	const conveyerArrow = "bottomConveyerArrow" + (isRunning ? " activeSignal": "");
	const conveyerFlushButton = "bottomConveyerControl isButton" + (isFlushing ? " activeSignal": "");
	const conveyerVibrationButton = "bottomConveyerControl isButton" + (isVibrating ? " activeSignal": "");
	const isSensorMaterial = _.get(props.data, ["sig_isMaterial"]);
	const isMaterial = _.get(props.data, ["isMaterial"]);
	const sensorMaterialClass = "bottomConveyerSensor" + (isMaterial ? " activeSignal": "");

	const isMobileActive = _.get(props.xxx_user_mode, ["isMobileActive"]);
	const onMouseDown = IS_TOUCH_DEVICE ? "onTouchStart" : "onMouseDown";
  const onMouseUp = IS_TOUCH_DEVICE ? "onTouchEnd" : "onMouseUp";
	const canControl = (IS_TOUCH_DEVICE && isMobileActive) || (IS_TOUCH_DEVICE != 1 && isMobileActive != 1);

	const divStartButton = {"key": 10, "className": conveyerControlStartButton, "style": {"fontSize": "calc(0.8vw + 0.8vh)"}, title: VISUAL_BUTTONS['conveyerStartStop']};
	const divFlushButton = {"key": 11, "className": conveyerFlushButton, "style": {"fontSize": "calc(0.7vw + 0.7vh)"}, title: VISUAL_BUTTONS['conveyerFlush']};
	const divVibrationButton = {"key": 12, "className": conveyerVibrationButton, "style": {"fontSize": "calc(0.9vw + 0.9vh)"}, title: VISUAL_BUTTONS['conveyerVibration']};
	if (canControl) {
		divStartButton[onMouseDown] = (e) => sendApiMessage([id, action], 1, 0);
		divFlushButton[onMouseDown] = (e) => sendApiMessage([id, "flush"], 1, 0);
		divVibrationButton[onMouseDown] = (e) => sendApiMessage([id, "vibration"], 1);
		divVibrationButton[onMouseUp] = (e) => sendApiMessage([id, "vibration"], 0);
	}

	return rce("div", {"key": 1, "className": "bottomConveyer-content"}, [
		isSensorMaterial ?
			rce("div", {"key": 2, "className": sensorMaterialClass}, [
				rce('i', {"key": 3, "className": "fas fa-ellipsis-h"}),
			])
		:
		null,
		rce("div", {"key": 5, "className": "bottomConveyer"}, [
			rce("div", {"key": 6, "className": "bottomConveyerRotationBackground"}, [
				rce("div", {"key": 7, "className": running}),
			]),
			rce("div", {"key": 8, "className": "bottomConveyer-control-content"}, [
				rce("div", {"key": 9, "className": conveyerArrow, "style": {"fontSize": "calc(1vw + 1vh)"}}, [
					rce("i", {"key": 9 + "ico", "className": "fa fa-angle-double-right"}),
				]),
				rce("div", divStartButton, [
					rce("i", {"key": 10 + "ico", "className": "far fa-dot-circle"}),
				]),
				rce("div", divFlushButton, [
					rce("i", {"key": 11 + "ico", "className": "fas fa-tint"}),
				]),
				rce("div", divVibrationButton, [
					rce("i", {"key": 12 + "ico", "className": "icon-vibration"}),
				]),
			]),
		]),
	]);
}

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 && isCementSilosVibration != 1) {
			divStartCementSilosVibrationButton[onMouseDown] = (e) => sendApiMessage([id, 'cementSilosVibrationStart'], 1, 0);
		}
    if (canControl && isCementSilosVibration && _.get(props.data, ['sig_cementSilosVibrationStart'])) {
      divStartCementSilosVibrationButton[onMouseDown] = (e) => sendApiMessage([id, 'cementSilosVibrationStop'], 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, 0) : null;
		}
		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 renderNodeConcreteCart = props => {
	if (DEBUG_REACT) {
		console.log('render renderConcreteCart', props);
		_.updateWith(debug_stuff, ['render_count', 'renderNodeConcreteCart'], x => x ? x + 1 : 1);
	}
   
  //console.log(props.data);
  const id = _.get(props.data, ["id"])
  const isControlEnable = _.get(props.data, ["isControlEnable"])
  const isControlEnableInPosition5 = _.get(props.data, ["isControlEnableInPosition5"])
  const numOfPositions = _.get(props.data, ["numOfPositions"])
  const isManual = _.get(props.data, ["isManual"])

  const durationPourOutInPosition_5 = _.get(props.data, ["durationPourOutInPosition_5"]) > 0 ? _.get(props.data, ["durationPourOutInPosition_5"]) : ""
  const isFull = _.get(props.data, ['isFull'])
  const isFilling = _.get(props.data, ['isFilling'])
  const closed = _.get(props.data, ['isClosed'])
  const open = _.get(props.data, ['isOpen'])
  const sensorOpenClass = open ? ".c_sensor .activeSignal" : ".c_sensor"
  const sensorClosedClass = closed ? ".c_sensor .activeSignal" : ".c_sensor"
  const openStateClass = closed ? "" : (open ? ".c_open" : ".c_semi")
  const cartClass = ".c_cart " + openStateClass + ( isFull ? " .c_cartNonEmpty" : "" )
  const isGoingLeft = _.get(props.data, ['isGoingLeft'])
  const isGoingRight = _.get(props.data, ['isGoingRight'])
  const isOpening = _.get(props.data, ['isOpening'])
  const isClosing = _.get(props.data, ['isClosing'])
  const lastPosition = _.get(props.data, ['lastPosition'])
  const leftArrowClass = isGoingLeft ? ".c_arrow .activeSignal" : ".c_arrow"
  const rightArrowClass = isGoingRight ? ".c_arrow .activeSignal" : ".c_arrow"
  const fillingArrowClass = isFilling ? ".c_arrow .activeSignal" : ".c_arrow"
  const isClosingClass = ( isClosing ? ".c_openCloseArrow .activeSignal" : ".c_openCloseArrow" ) + ( isControlEnableInPosition5 ? "" : " .c_buttonDisable")
  const isOpeningClass = ( isOpening ? ".c_openCloseArrow .activeSignal" : ".c_openCloseArrow" ) + ( isControlEnableInPosition5 ? "" : " .c_buttonDisable")

  let sensors = [];
  for (let i = 1; i < numOfPositions + 1; i++) {
    const sensorClass = _.get(props.data, ['isPosition_' + i]) ? ".c_sensor .activeSignal" : ".c_sensor"
    const action = 'goToPosition_' + i
    const title_ = "Pozice " + i
    const enabledControl = isControlEnable ? 1 : ( isControlEnableInPosition5 && i == 3 && closed) ? 1 : 0
    const buttonClass = ( _.get(props.data, ['isTargetPosition_' + i]) ? ".c_sensorButon c_reasonAutomat" : ".c_sensorButton" ) + (enabledControl ? "" : " c_buttonDisable")
    
    sensors.push(h(".c_sensorButtonsArea", {}, [
      h(buttonClass, {
        onMouseDown: enabledControl ? (e) => sendApiMessage([id, action], 1, 0) : null,
        title: title_,
      }, [
        h(sensorClass, {}, [
          h('i.fa.fa-circle'),
        ]),
      ]),
    ]))    
  }
  
  let carts = [];
  for (let i = 1; i < numOfPositions + 1; i++) {
    
    const cartVisibility = _.get(props.data, ['isPosition_' + i]) ? "visible" : "hidden"
    carts.push(h(".c_cartContent", {}, [
        h(cartClass, {"style": {"visibility": cartVisibility}}),
        i == 5 ? h(".c_cartContentDisplay", {}, durationPourOutInPosition_5) : null
      ])
    )
    if ( i < numOfPositions ) {
      const isOnRightPositionInMove = ( lastPosition == i && isGoingRight == 1 && _.get(props.data, ['isPosition_' + i]) != 1 )
      const isOnLeftPositionInMove = ( lastPosition == (i + 1) && isGoingLeft == 1 && _.get(props.data, ['isPosition_' + (i + 1)]) != 1 )
      const cartFakePosVisibility = ( isOnRightPositionInMove || isOnLeftPositionInMove ) ? "visible" : "hidden"
      
      carts.push(h(".c_cartContent", {}, [
        h(cartClass, {"style": {"visibility": cartFakePosVisibility}})
        ])
      )
    }
  }
  
	return h('.c_concreteCart-content', {}, [
    h(".c_leftLeft", {}, [
      h(".c_manualMode", {}, [
        isManual
        ? h(".c_manualModeButton .activeSignal", {
            onMouseDown: (e) => sendApiMessage([id, 'manualUnset'], 1, 0),
            title: 'přepnutí do automatu',
          }, [
            h('i', {"className": "fas fa-hand-paper"}),
            "manual",
          ])
        : h(".c_manualModeButton", {
            onMouseDown: (e) => sendApiMessage([id, 'manualSet'], 1, 0),
            title: 'přepnutí do manualu',
          }, [
            h('i', {"className": "fas fa-hand-paper"}),
            "automat",
          ]),
        h(".c_openCloseButtonsArea", {}, [
          h(isOpeningClass, {
          onMouseDown: isControlEnableInPosition5 ? (e) => sendApiMessage([id, 'open'], 1, 0) : null,
          title: 'otevíráni vozíku',
          }, [
            h('i', {"className": "fas fa-arrow-left"}),
            h(sensorOpenClass, {}, [
              h('i.fa.fa-circle'),
            ]),
            h('i', {"className": "fas fa-arrow-right"})
          ]),
        ]),
        h(".c_openCloseButtonsArea", {}, [
          h(isClosingClass, {
          onMouseDown: isControlEnableInPosition5 ? (e) => sendApiMessage([id, 'close'], 1, 0) : null,
          title: 'zavírání vozíku',
          }, [
            h('i', {"className": "fas fa-arrow-right"}),
            h(sensorClosedClass, {}, [
              h('i.fa.fa-circle'),
            ]),
            h('i', {"className": "fas fa-arrow-left"})
          ]),
        ]),
      ]),
    ]),
    h(".c_leftRight", {}, [
      h(".c_topIconsArea", {}, [
        h(leftArrowClass, {}, [
          h('i', {"className": "fas fa-angle-double-left"})
        ]),
        h(fillingArrowClass, {}, [
          h('i', {"className": "fas fa-arrow-down"})
        ]),
        h(rightArrowClass, {}, [
          h('i', {"className": "fas fa-angle-double-right"})
        ]),
      ]),
      h(".c_horizontal_spacer", {}),
      h(".c_cartArea", {}, [
        carts
      ]),
      h(".c_horizontal_spacer", {}),
      h(".c_rail", {}),
      h(".c_horizontal_spacer", {}),
      h(".c_sensorsArea", {}, [
        sensors
      ]),
      h(".c_sensorsArea", {}, [
        h(".c_nameOfPosition" , {}, "stará hala"),
        h(".c_nameOfPosition" , {}, "nová hala"),
        h(".c_nameOfPosition" , {}, "míchačka"),
        h(".c_nameOfPosition" , {}, "mytí"),
        h(".c_nameOfPosition" , {}, "transport"),
      ]),
    ]),
	]);
}


const renderNodeMessagesConcretecart = 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(h('div', {
			onMouseDown: e => {sendApiMessage(["Messages", 'set_message_answer', props.data['k']], option['id'])},
			className: props.xxx_auto_confirm && option['id'] == 'yes' ? "message2-button active" : "message2-button",
			}, option['text']));
	}
  const active = _.get(props.data, ['active']);
	const levelClass = ".message2 " + props.data['level'] + ( active === 0 ? " inactive" : "" );
	const text = props.data['text'];

	return h(".messageContent2", {}, [
		h(levelClass, {}, [
			h(".message2-text", {}, text),
			h(".message2-buttons", {}, options),
		])
	]);
}


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 levelClass = "message " + props.data['level'];
	const text = props.data['text'];
	// 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": levelClass, "key": 0}, [
			rce('div', {"className": "message-text", "key": 1}, text),
			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();
		}
	}
}

function openPlacement() {
	$.ajax({url: 'http://localhost:7701/get_place', timeout: 5000,
		success: function (data) {
			const d = JSON.parse(data);
			let html = "<form id='Place'><table>";
			for (let k in d) {
				html += "<tr><td>" + NAMES[k] + "</td><td><input name='" + k + "' value='" + d[k] + "'></input></td></tr>";
			}
			html += "<tr><td></td><td><button name='SaveParam' value='1'>Uložit</button></td></tr>";
			html += "</table></form>";
			alertify.param(html, e => {}).setHeader("Umístění materiálů").set({transitionOff:true});
		}
	});
}

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);
	}
}

// TODO: ugly shit copy paste Clock class!!!!!!!
class ClockC extends React.Component {
	constructor() {
		super();
		this.state = {clockStr: ""};
	}
	componentDidMount() {
		setInterval(() => {this.setState({key: 50001, clockStr: dateTimeString()})}, 1000);
	}
	render() {
		return rce("div", {key: 50000, className: "c_clock"}, this.state.clockStr);
	}
}

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