Node.prototype.getFirstChildByAttribute = function(attr, value) {
	var children = this.childNodes;
	for (var i = children.length - 1; i >= 0; i--) {
		var child = children[i];
		if (child.nodeType == 1 && child.getAttribute(attr) == value) {
			return child;
		}
	}

	return null;
};

Node.prototype.getElementByTagAndAttribute = function(tag, attr, value) {
	var elements = Array.prototype.slice.call(this.getElementsByTagName(tag));
	elements = elements.filter(function (item) {
		return item.nodeType == 1 && item.getAttribute(attr) == value;
	});

	if (elements.length > 0)
		return elements[0];

	return null;
}

Element.prototype.getFirstElement = function() {
	var firstChild = this.firstChild;
	while (firstChild !== null && firstChild.nodeType != 1)
		firstChild = firstChild.nextSibling;

	return firstChild;
};

window.createBlink = function(node, attr, onValue, offValue) {
	return {
		node: node,
		isOn: true,
		attr: attr,
		onValue: onValue,
		offValue: offValue
	};
};

window.blinkOn = true;

window.map = {
	mapElementsById: {},
	blinkInterval: null,
	elements: [],
	blinkElements: [],
	blinkElementsShadow: [],

	canBeCMElement: function (id) {
		return (/D\d+|GP?\d+|S\d+|SW\d+|P\d+:G?\d+/).test(id);
	},

	isGroupOfElements: function (node) {
		var id = node.getAttribute('id');
		if (/GP?\d+|SW\d+/.test(id))
			return node.getFirstElement().nodeName == 'g';

		return node.nodeName == 'g';
	},

	createCMElement: function (node, id) {
		id = id || node.getAttribute('id');
		var type = /(.*?)\d.*/.exec(id)[1],
			result = null;


		if (type === 'D') {
			node.style.cursor = 'pointer';

			result = {
				id: parseInt(id.substr(1)),
				node: node,
				setData: this.updateDetector,
				type: 'detector'
			};
		} else if (type === 'G' || type === 'GP') {
			result = {
				redPath: node.getFirstChildByAttribute('sekcja', 'R'),
				yellowPath: node.getFirstChildByAttribute('sekcja', 'Y'),
				setData: this.updateGroup,
				type: (type === 'G') ? 'group' : "ped-group",
				id: (type === 'G') ? id.substr(1) : id.substr(2),
				pedestrian: type === 'GP'
			};
		} else if (type === 'S') {
			result = {
				id: parseInt(id.substr(1)),
				node: node,
				setData: this.updateStream,
				type: 'stream'
			};
		} else if (type === 'SW') {
			result = {
				id: parseInt(id.substr(2)),
				node: node.getElementsByTagName('path')[0],
				setData: this.updateStream,
				type: 'arrow'
			};
		} else if (type === 'P') {
			node.style.cursor = 'pointer';
			match = /P(\d+):G?(\d+)/.exec(id);
			result = {
				id: parseInt(match[1]),
				secondId: parseInt(match[2]),
				highlightFromGroup: id.indexOf('G') != -1,
				setData: this.updatePedButton,
				node: node,
				type: 'button'
			};
		}

		if (result !== null) {
			node.setAttribute('id', id);
			node.addEventListener('click', this.elementClick, true);
			node.addEventListener('mouseover', this.elementMouseOver, true);
			node.addEventListener('mouseout', this.elementMouseOut, true);
		}

		return result;
	},

	parseSVG: function(root) {
		this.walkXML(root, function (node) {
			var id = node.getAttribute('id'),
				cmElement;

			if (this.canBeCMElement(id)) {
				// Jeżeli spełnia przesłanki do bycia elementem mapy
				if (this.isGroupOfElements(node)) {
					var children = node.childNodes;
					for (var i = children.length - 1; i >= 0; i--) {
						var child = children[i];
						if (child.nodeType == 1) {
							cmElement = this.createCMElement(child, id);
							this.mapElementsById[id] = cmElement;
							this.elements.push(cmElement);
						}
					}
				} else {
					cmElement = this.createCMElement(node);
					this.mapElementsById[node.getAttribute('id')] = cmElement;
					this.elements.push(cmElement);
				}

				return false;
			}

			return true;
		});

		var that = this;
		this.blinkInterval = setInterval(function (){
			that.blinkCallback.call(that);
		}, 500);
	},

	walkXML: function (root, callback) {
		var children = root.childNodes;
		for (var i = children.length - 1; i >= 0; i--) {
			var child = children[i];
			if (child.nodeType == 1) {
				if (callback.call(this, child))
					this.walkXML(child, callback);
			}
		}
	},

	updateLayersVisibility: function(layersVisibility) {
		var displayFromBool = function (b) { return b ? '' : 'none'; },
			findLayer = function (layerName) {
				root = document.getElementsByTagName('svg')[0];
				return root.getElementByTagAndAttribute('g', 'inkscape:label', layerName);
			},
			setLayerVisibility = function (layerName, visible) {
				var layer = findLayer(layerName);
				if (layer == null)
					return;

				layer.style.display = displayFromBool(visible);
			};

		setLayerVisibility('detektor_nazwa', layersVisibility.detectorsName);
		setLayerVisibility('detektor_numer', layersVisibility.detectorsNumber);
		setLayerVisibility('grupa_nazwa', layersVisibility.groupsName);
		setLayerVisibility('grupa_numer', layersVisibility.groupsNumber);
	},

	setData: function (data) {
		for (var i = this.elements.length - 1; i >= 0; i--) {
			var element = this.elements[i];
			if (element)
				element.setData.call(element, data);
		}

		this.blinkElementsShadow = this.blinkElements;
	},

	elementClick: function () {
		if (window.OnElementClick) {
			window.OnElementClick(map.mapElementsById[this.getAttribute('id')]);
		}
	},

	elementMouseOver: function() {
		if (window.OnElementMouseOver)
			window.OnElementMouseOver(this.getAttribute('id'));
	},

	elementMouseOut: function() {
		if (window.OnElementMouseOut)
			window.OnElementMouseOut(this.getAttribute('id'));
	},

	updateDetector: function (data) {
		var id = this.id - 1;
		if (data.inductions && data.inductions.length > id) {
			var state = data.inductions[id];
			this.state = state;
			this.node.style.stroke = (state & 8) ? '#ff0000' : '#0000ff';
			this.node.style.fill = (state & 1) ? '#0000ff' : ((state & 4) ? '#00ff00' : '#707070') ;
		}
	},

	updatePedButton: function (data) {
		var id = this.id - 1;
		if (data.inductions && data.inductions.length > id) {
			var state = data.inductions[id] || 0;
			var backlightOn = false;
			if (this.highlightFromGroup) {
				backlightOn = (data.groups[this.secondId - 1] & 2) !== 0;
			} else {
				backlightOn = data.outs[this.secondId - 1];
			}

			this.node.style.stroke = backlightOn ? '#8FCED1' : '#000';
			this.node.style.fill = (state & 1) ? '#0000ff' : ((state & 4) ? '#00ff00' : '#000');
		} else {
			this.node.style.stroke = '#000';
			this.node.style.fill = '#000';
		}
	},

	updateStream: function (data) {
		var state = data.groups[this.id - 1];
		this.node.style.display = (state & 1) ? 'inline' : 'none';
	},

	updateGroup: function (data) {
		if (!data.groups || data.groups.length <= this.id)
			return;

		var state = data.groups[this.id - 1] || 0;

		if (this.pedestrian)
			state &= ~0x22;

		if ((state & 7) == 7 || state === 0) {
			map.removeBlink(this.redPath, 'fill', '#666');
			map.removeBlink(this.yellowPath, 'fill', '#666');
		} else if (state & 4) {
			if (state & 0x40)
				map.addBlink(this.redPath, 'fill', '#ff0000', '#666666');
			else
				map.removeBlink(this.redPath, 'fill', '#ff0000');

			if (state & 2) {
				if (state & 0x20)
					map.addBlink(this.yellowPath, 'fill', '#ffff00', '#666666');
				else
					map.removeBlink(this.yellowPath, 'fill', '#ffff00');
			} else if (state & 1) {
				if (state & 0x10)
					map.addBlink(this.yellowPath, 'fill', '#00ff00', '#666666');
				else
					map.removeBlink(this.yellowPath, 'fill', '#00ff00');
			} else {
				if (state & 0x40)
					map.addBlink(this.yellowPath, 'fill', '#ff0000', '#666666');
				else
					map.removeBlink(this.yellowPath, 'fill', '#ff0000');
			}
		} else if (state & 2) {
			if (state & 0x20)
				map.addBlink(this.redPath, 'fill', '#ffff00', '#666666');
			else
				map.removeBlink(this.redPath, 'fill', '#ffff00');

			if (state & 1) {
				if (state & 0x10)
					map.addBlink(this.yellowPath, 'fill', '#00ff00', '#666666');
				else
					map.removeBlink(this.yellowPath, 'fill', '#00ff00');
			} else {
				if (state & 0x20)
					map.addBlink(this.yellowPath, 'fill', '#ffff00', '#666666');
				else
					map.removeBlink(this.yellowPath, 'fill', '#ffff00');
			}

		} else if (state & 1) {
			if (state & 0x10) {
				map.addBlink(this.redPath, 'fill', '#00ff00', '#666');
				map.addBlink(this.yellowPath, 'fill', '#00ff00', '#666');
			} else {
				map.removeBlink(this.redPath, 'fill', '#00ff00');
				map.removeBlink(this.yellowPath, 'fill', '#00ff00');
			}
		}
	},

	addBlink: function(node, attr, onValue, offValue) {
		for (var i in this.blinkElements) {
			if (this.blinkElements[i].node === node) {
				var b = this.blinkElements[i];
				b.attr = attr;
				b.onValue = onValue;
				b.offValue = offValue;
				return;
			}
		}

		var blinkElement = createBlink(node, attr, onValue, offValue);
		this.blinkElements.push(blinkElement);
	},

	removeBlink: function(node, attr, value) {
		for (var i in this.blinkElements) {
			var blinkElement = this.blinkElements[i];
			if (blinkElement.node == node) {
				this.blinkElements.splice(i, 1);
				node.style[attr] = value;
				return;
			}
		}

		node.style[attr] = value;
	},

	blinkCallback: function() {
		for (var i = this.blinkElements.length - 1; i >= 0; i--) {
			var blink = this.blinkElements[i];

			blink.node.style[blink.attr] = window.blinkOn ? blink.onValue : blink.offValue;
			blink.isOn = !blink.isOn;
		}
		
		window.blinkOn = !window.blinkOn;
	}
};