text: const noop = () => {};
  const helper = require('../helper');
  
  var previous;
  var httpOpts = { xml: true };
  var playerData =
  {
  	'time-pos': 'time',
  	'volume': 'volume',
  	'duration': 'length',
  	'pause': 'state',
  	'eof-reached': 'state'
  };
  
  /* Prevent emiting 'eof-reached' at launch */
  var launched = false;
  var loading = false;
  var streams;
  var currStreams;
  
  module.exports =
  {
  	init: function()
  	{
  		previous = {};
  		streams = {};
  		currStreams = {};
  		launched = false;
  		loading = false;
  
  		this._intervalEnabled = true;
  		this._getPlayerData();
  	},
  
  	_connectType: 'web',
  	_forceEnglish: true,
  
  	_getPlayerData: function()
  	{
  		const onTimeout = () =>
  		{
  			this._getDataTimeout = null;
  			this._getPlayerData();
  		}
  
  		httpOpts.path = '/requests/status.xml';
  		helper.httpRequest(httpOpts, (err, result) =>
  		{
  			var time = 1000;
  
  			if(!err && this._intervalEnabled)
  			{
  				this._parseRequest(result);
  
  				var isPlaying = (result.state === 'playing');
  				time = this._getProbeTime(isPlaying, result.time);
  			}
  
  			if(this._intervalEnabled)
  				this._getDataTimeout = setTimeout(() => onTimeout(), time);
  		});
  	},
  
  	_parseRequest: function(result)
  	{
  		for(var key in playerData)
  		{
  			var value = result[playerData[key]];
  
  			switch(key)
  			{
  				case 'pause':
  					value = (value === 'paused');
  					break;
  				case 'eof-reached':
  					value = (launched && !loading && value === 'stopped');
  					break;
  				case 'time-pos':
  				case 'duration':
  					value = parseInt(value);
  					if(value < 0)
  						continue;
  					else if(!launched && key === 'time-pos' && value > 0)
  						launched = true;
  					break;
  				case 'volume':
  					value = Math.round(value / 2.56) / 100;
  					if(value < 0)
  						continue;
  					break;
  				default:
  					if(value == 'true')
  						value = true;
  					else if(value == 'false')
  						value = false;
  					break;
  			}
  
  			if(
  				previous.hasOwnProperty(key)
  				&& previous[key] === value
  			)
  				continue;
  
  			previous[key] = value;
  			this.emit('playback', { name: key, value: value });
  		}
  
  		previous.repeat = (result.repeat === true || result.repeat === 'true');
  		previous.fullscreen = (result.fullscreen > 0);
  
  		if(result.currentplid > 0)
  			previous.id = result.currentplid;
  
  		if(
  			!result.information
  			|| !result.information.category
  			|| !result.information.category.length
  			|| (streams.count
  			&& streams.count === result.information.category.length)
  		)
  			return;
  
  		streams = {
  			count: result.information.category.length,
  			video: [],
  			audio: [],
  			subs: []
  		};
  
  		for(var cat of result.information.category)
  		{
  			if(!cat['$'].name.startsWith('Stream'))
  				continue;
  
  			var index = cat['$'].name.split(' ')[1];
  			var streamType = cat.info.find(inf => inf['$'].name === 'Type');
  
  			switch(streamType._)
  			{
  				case 'Video':
  					streams.video.push(index);
  					break;
  				case 'Audio':
  					streams.audio.push(index);
  					break;
  				case 'Subtitle':
  					streams.subs.push(index);
  					break;
  				default:
  					break;
  			}
  		}
  
  		for(var type in streams)
  		{
  			if(type === 'count')
  				continue;
  
  			if(streams[type].length > 1)
  				streams[type].sort();
  		}
  
  		this.emit('streams-changed');
  	},
  
  	cleanup: function()
  	{
  		this._intervalEnabled = false;
  
  		if(this._getDataTimeout)
  			clearTimeout(this._getDataTimeout);
  	},
  
  	_getSpawnArgs: function(opts)
  	{
  		/* VLC requires password for web interface */
  		httpOpts.pass = opts.httpPass || 'vlc';
  		httpOpts.port = opts.httpPort;
  
  		var presetArgs = [
  			'--no-play-and-exit',
  			'--no-qt-recentplay',
  			'--qt-continue', '0',
  			'--image-duration', '-1',
  			'--extraintf', 'http',
  			'--http-port', httpOpts.port,
  			'--http-password', httpOpts.pass,
  			opts.media
  		];
  
  		return [ ...opts.args, ...presetArgs ];
  	},
  
  	command: function(params, cb)
  	{
  		cb = cb || noop;
  		var command = null;
  
  		if(!Array.isArray(params))
  			return cb(new Error('No command parameters array!'));
  
  		for(var cmd of params)
  		{
  			if(!command)
  				command = cmd;
  			else
  				command += `&${cmd}`;
  		}
  
  		httpOpts.path = '/requests/status.xml?command=' + command;
  		helper.httpRequest(httpOpts, (err, result) =>
  		{
  			if(err) return cb(err);
  
  			httpOpts.path = '/requests/status.xml';
  			helper.httpRequest(httpOpts, (err, result) =>
  			{
  				if(err) return cb(err);
  
  				this._parseRequest(result);
  				cb(null);
  			});
  		});
  	},
  
  	play: function(cb)
  	{
  		cb = cb || noop;
  
  		if(previous.pause)
  			this.cyclePause(cb);
  		else
  			cb(null);
  	},
  
  	pause: function(cb)
  	{
  		cb = cb || noop;
  
  		if(!previous.pause)
  			this.cyclePause(cb);
  		else
  			cb(null);
  	},
  
  	cyclePause: function(cb)
  	{
  		cb = cb || noop;
  		this.command(['pl_pause'], cb);
  	},
  
  	_load: function(media, cb)
  	{
  		cb = cb || noop;
  		previous.duration = null;
  		loading = true;
  		var changeTimeout;
  
  		const onStreamsChange = function()
  		{
  			if(changeTimeout)
  			{
  				clearTimeout(changeTimeout);
  				changeTimeout = null;
  			}
  
  			loading = false;
  			cb(null);
  		}
  
  		var delId = previous.id;
  		this.command(['in_play', `input=${media}`], (err) =>
  		{
  			if(err) return cb(err);
  
  			currStreams = {};
  
  			this.command(['pl_delete', `id=${delId}`], (err) =>
  			{
  				if(err) return cb(err);
  
  				changeTimeout = setTimeout(() =>
  				{
  					changeTimeout = null;
  					loading = false;
  					this.removeListener('streams-changed', onStreamsChange);
  
  					cb(new Error('Streams change timeout'));
  				}, 15000);
  
  				this.once('streams-changed', onStreamsChange);
  				streams = {};
  
  				httpOpts.path = '/requests/status.xml';
  				helper.httpRequest(httpOpts, (err, result) =>
  				{
  					/* Ignore this req and wait for next */
  					if(err) return;
  
  					this._parseRequest(result);
  				});
  			});
  		});
  	},
  
  	seek: function(position, cb)
  	{
  		cb = cb || noop;
  		position = (position > 0) ? parseInt(position) : 0;
  
  		this.command(['seek', `val=${position}`], cb);
  	},
  
  	setVolume: function(value, cb)
  	{
  		cb = cb || noop;
  		value = (value > 0) ? parseInt(value * 256) : 0;
  
  		this.command(['volume', `val=${value}`], (err) =>
  		{
  			if(err) return cb(err);
  
  			/* Setting volume on VLC sometimes requires another refresh */
  			httpOpts.path = '/requests/status.xml';
  			helper.httpRequest(httpOpts, (err, result) =>
  			{
  				if(err) return cb(err);
  
  				this._parseRequest(result);
  				cb(null);
  			});
  		});
  	},
  
  	setRepeat: function(isEnabled, cb)
  	{
  		cb = cb || noop;
  
  		switch(isEnabled)
  		{
  			case true:
  			case 'inf':
  			case 'yes':
  			case 'on':
  				isEnabled = true;
  				break;
  			default:
  				isEnabled = false;
  				break;
  		}
  
  		if(
  			isEnabled && previous.repeat
  			|| !isEnabled && !previous.repeat
  		)
  			return cb(null);
  
  		this.command(['pl_repeat'], cb);
  	},
  
  	cycleVideo: function(cb)
  	{
  		cb = cb || noop;
  
  		if(!streams.video || !streams.video.length)
  			return cb(new Error('No video tracks'));
  
  		var value = this._cycleStream('video');
  		this.command(['video_track', `val=${value}`], cb);
  	},
  
  	cycleAudio: function(cb)
  	{
  		cb = cb || noop;
  
  		if(!streams.audio || !streams.audio.length)
  			return cb(new Error('No audio tracks'));
  
  		var value = this._cycleStream('audio');
  		this.command(['audio_track', `val=${value}`], cb);
  	},
  
  	cycleSubs: function(cb)
  	{
  		cb = cb || noop;
  
  		if(!streams.subs || !streams.subs.length)
  			return cb(new Error('No subtitles tracks'));
  
  		var value = this._cycleStream('subs');
  		this.command(['subtitle_track', `val=${value}`], cb);
  	},
  
  	_cycleStream: function(streamType)
  	{
  		if(!currStreams[streamType])
  			currStreams[streamType] = 0;
  
  		currStreams[streamType]++;
  
  		if(currStreams[streamType] > streams[streamType].length - 1)
  			return currStreams[streamType] = -1;
  
  		return streams[streamType][currStreams[streamType]];
  	},
  
  	addSubs: function(subsPath, cb)
  	{
  		cb = cb || noop;
  
  		if(!subsPath)
  			return cb(new Error('No subtitles path specified'));
  
  		var initialSubsLength = streams.subs.length;
  
  		const setAddedSubs = () =>
  		{
  			if(this.addSubsTimeout)
  			{
  				clearTimeout(this.addSubsTimeout);
  				this.addSubsTimeout = null;
  			}
  
  			if(!streams.subs.length)
  				return cb(new Error('No subtitles tracks'));
  			else if(initialSubsLength === streams.subs.length)
  				return cb(new Error('Subtitles track was not added'));
  
  			var lastSubs = Math.max.apply(null, streams.subs);
  			this.command(['subtitle_track', `val=${lastSubs}`], cb);
  		}
  
  		this.addSubsTimeout = setTimeout(() =>
  		{
  			this.addSubsTimeout = null;
  			this.removeListener('streams-changed', setAddedSubs);
  
  			cb(new Error('Add subtitles timeout'));
  		}, 5000);
  
  		this.once('streams-changed', setAddedSubs);
  
  		this.command(['addsubtitle', `val=${subsPath}`], (err) =>
  		{
  			/* When no error wait for "streams-changed" event */
  			if(!err) return;
  
  			this.removeListener('streams-changed', setAddedSubs);
  
  			if(this.addSubsTimeout)
  			{
  				clearTimeout(this.addSubsTimeout);
  				this.addSubsTimeout = null;
  			}
  
  			cb(err);
  		});
  	},
  
  	setFullscreen: function(isEnabled, cb)
  	{
  		cb = cb || noop;
  
  		switch(isEnabled)
  		{
  			case false:
  			case 'no':
  			case 'off':
  				isEnabled = false;
  				break;
  			default:
  				isEnabled = true;
  				break;
  		}
  
  		if(
  			isEnabled && previous.fullscreen
  			|| !isEnabled && !previous.fullscreen
  		)
  			return cb(null);
  
  		this.command(['fullscreen'], cb);
  	},
  
  	cycleFullscreen: function(cb)
  	{
  		cb = cb || noop;
  		this.command(['fullscreen'], cb);
  	},
  
  	keepOpen: function(value, cb)
  	{
  		cb = cb || noop;
  
  		/* VLC always uses keep open */
  		cb(new Error('VLC does not support keep open command'));
  	},
  
  	_playerQuit: function(cb)
  	{
  		cb = cb || noop;
  
  		this.cleanup();
  		cb(new Error('VLC does not support remote quit command'));
  	}
  }
  