var HTML5Player = function(data){
	$.extend(true, this, new Player(data));
	var _this = this,
		FIRST_TIME_LOADED = false,
		LOADER_EFFECT_DURATION = 400,
		TIMES_TO_RETRY = 20,
		timeout = null,
		watchDog = null,
		interval = null, fas
		pingTimeout = null,
		publishedStreamCheckTimeout = null,
		streamName = null,
		streamDomain = null,
		streamURL = null,
		requestId = null,
		keyType = "2",
		watchDog = null,
		WATCHDOG_TIMEOUT = 32*1000;

	this.commandXHR = null;
	this.pingXHR = null;
	this.streamSource = null;
	this.retries = TIMES_TO_RETRY;
	TIME_STAMPS = {
		T_STREAM_REQUEST_START : 0,
		T_KAPI_RETURNED_DATA : 1,
		T_WOWZA_STREAM_READY : 2,
		T_PLAY_COMMAND_ISSUED : 3,
		T_STREAM_STARTED : 4
	}

	this.requestDuration = {
		s: null,
		timeStamps : new Array(4),
		durations : new Array(4),
		e: null,
		freshDispatch : null,
		sendReport : function(duration){
			var ssd;
			if (!this.freshDispatch){
				return false;
			}
			
			if (!duration){
				duration = this.processDurations();
			} 

			ssd = JSON.stringify({ duration : duration, cameraId : _this.camera.cid, requestId : requestId, viewerType : 2 });
			console.log(ssd);

			$.get("/health/", {ssd : ssd});

			this.freshDispatch = false;
		},
		start : function(){
			this.s = new Date();
			this.freshDispatch = true;
		},
		catchTime : function(key){
			this.timeStamps[key] = new Date();	
		},
		processDurations : function(){
			for (var i = 1, len = this.timeStamps.length; i < len; i++){
				this.durations[i-1] = this.timeStamps[i] - this.s;
			}
			return this.durations;	
		},
		end : function(){
			this.e = new Date();
		},
		duration : function(){
			return this.e - this.s
		}
	};
	
	
	this.userMessage = this.div.find(".user-messages");
	// var DEBUG = false;
	var keycheck = null;
	var debug = this.div.find(".debug-messages").toggle(DEBUG);
	function debugMessage(text, type){
		var e;
		if (DEBUG){
			e = $("<div></div>").text(text).prependTo(debug);
			if (type){
				e.addClass(type);
			}
		}
			
		else if (DEVELOPMENT)
			console.log(text);
	}
	function startWatchDog(){
		console.log("watchDog started");
		watchDog = setTimeout(function(){
			console.log("watchDog ACTION!");
			_this.showUserMessage(MESSAGES.HTML5_PLAYER_FINAL_ERROR);	
		}, WATCHDOG_TIMEOUT);
	}

	function killWatchDog(){
		console.log("watchDog dead");
		clearTimeout(watchDog);
	}

	this.showUserMessage = function(text){
		this.loader.showWithoutSpinner();
		this.userMessage.text(text).fadeIn(LOADER_EFFECT_DURATION);
	};


	// Play HTML5 video
	this.openCallback = function(response){
		_this.streamSource = response.sss;
		streamDomain = response.surl;
		streamURL = response.surl+"/"+response.streamName;
		streamName = response.streamName;

		_this.checkForPublishedStream();
	};

	this.checkForPublishedStream = function(){
		var start = 7,
			end = streamDomain.indexOf("/", start),
			target = streamDomain.slice(0, end);
	
		debugMessage("Checking for Published Stream");		
		debugMessage("sd: "+streamDomain);

		if (streamName.indexOf("playlist") > -1){
			streamName = streamName.slice(0, streamName.indexOf("playlist") -1);
		}

		debugMessage(streamName)

		target = target + "/published?name="+ streamName +"&app=live";

		$.get(target, function(response){
			debugMessage(response+ " ");

			if (response == "PUBLISHED"){

				playStream();
			}
			else if (response.startsWith("REDIRECT")) {
				var new_stream_name = response.split(',')[1]
					playlistSufix = "/playlist.m3u8";
				
				if (new_stream_name.indexOf(playlistSufix) < 0){
					// In case there is no neccessary playlist ext
					new_stream_name += playlistSufix;
				}

				streamName = new_stream_name;
				streamURL = streamDomain + '/' + new_stream_name;
				
				// try same request with new stream name
				debugMessage('changing stream name to: ' +streamName);
				playStream();
			}
			else {
				if (_this.retries > 0){
					publishedStreamCheckTimeout = setTimeout(function(){
						_this.checkForPublishedStream();
					}, 500);
				}
				else {
					showErrorAndStopTrying();
				}
			}
		}, "text").error(playStream);

	};

	this.ping = function(key, time){
		var pingUrl = "/site/ping?key=";
		if (!time){
			time = key.time;
			key = key.key;
		}
		debugMessage("key: "+key +" -- time : "+time);
 		
		pingTimeout = setTimeout(function(){
			// Appending key for query string 
			pingUrl += key;

			// Appending forcehtml5
			pingUrl += "&forcehtml5=1";

			debugMessage("pinging for "+_this.camera.serialNo);
			this.pingXHR = $.get(pingUrl, function(response){
				if (response.timeOut){
					debugMessage("ping:: key %s : %s ms".printf([key, time]));
					_this.ping(response.key, response.timeOut);
				}
				else if (response.streamName){
					debugMessage("stream:: opening stream at %s".printf(response.streamURL));
					_this.openCallback(response);
				}
			});
		}, time);
	};

	function playStream(){
		var video = _this.el.videoDiv[0];
		debugMessage("Playing stream : "+ streamURL);
		
		_this.requestDuration.catchTime(TIME_STAMPS.T_WOWZA_STREAM_READY);
		
		video.src = streamURL;
		video.load();
		video.play();
		killWatchDog();
		
		_this.requestDuration.catchTime(TIME_STAMPS.T_PLAY_COMMAND_ISSUED);

		if ($.isFunction(_this.cameraCallback)){
			_this.cameraCallback();
			_this.cameraCallback = null;
		}
	}

	function clearRequests(){
		if (_this.commandXHR){
			_this.commandXHR.abort();
			_this.commandXHR = null;
		}

		if (_this.pingXHR){
			_this.pingXHR.abort();
			_this.pingXHR = null;
		}

		if (timeout){
			clearTimeout(timeout);
			timeout = null;
		}

		if (interval){
			clearInterval(interval);
			interval = null;

		}

		if (pingTimeout){
			clearInterval(pingTimeout);
			pingTimeout = null;
		}

		keycheck = null;

		if (publishedStreamCheckTimeout){
			clearTimeout(publishedStreamCheckTimeout);
			publishedStreamCheckTimeout = null;
		}

		debugMessage("Player > clearing requests and timeouts");
	}

	this.showStream = function(cameraToStream, callback, widthLimits){
		var OPEN_CAMERA_URL = _this.START_CAMERA_URL_BASE.printf(cameraToStream.serialNo, keyType),
			openIdentifier;
		
		_this.requestDuration.start();
		_this.requestDuration.catchTime(TIME_STAMPS.T_STREAM_REQUEST_START);

		startWatchDog();
		keycheck = null;   
		if (typeof cameraToStream == "string"){
			openIdentifier = cameraToStream; 
			OPEN_CAMERA_URL = "/site/cameras/opencamerabyserialno/";

			_this.camera = {serialNo : cameraToStream}
		}
		else {
			this.setStreamInfo(cameraToStream, callback, widthLimits);
			this.loader.showWithSpinner();
			this.userMessage.fadeOut(LOADER_EFFECT_DURATION);
 			
 			openIdentifier = _this.camera.cid;
		}
		
		// Clearing events, intervals, timeouts and other crap from the last streaming attempt

		_this.el.videoDiv[0].pause();
		clearRequests();
		
		debugMessage("open camera "+cameraToStream.serialNo +" command ");
		
		this.commandXHR = $.get(OPEN_CAMERA_URL, function(response){
			_this.requestDuration.catchTime(TIME_STAMPS.T_KAPI_RETURNED_DATA);

			if(response.errCode  && response.errCode > 0){
				if (response.errCode == 5){
					_this.showUserMessage(MESSAGES.HTML5_PLAYER_CAMERA_GOT_OFFLINE);
				}
				else if (response.errCode == 25){
					_this.showUserMessage(MESSAGES.HTML5_PLAYER_UNEXPECTED_ERROR.printf("Cannot open camera"));
				}
				else {
					_this.showUserMessage(MESSAGES.HTML5_PLAYER_UNEXPECTED_ERROR.printf(response.errCode.toString()));
				}
				killWatchDog();
				return false;
			}

			requestId = response.key;
			if (response.key && response.timeOut){
				_this.ping(response.key, response.timeOut);
			}
			else {
				_this.openCallback(response);
			}
		}, "json");
	};

	this.setStreamInfo = function(camera, callback, widthLimits){
		if (this.camera){
			this.camera.isNowPlaying = false;
		}
		this.camera = camera;
		this.camera.isNowPlaying = true;
		
		this.cameraCallback = callback;
		this.updateStreamInfo(widthLimits);
	};

	this.updateStreamInfo = function(widthLimits){
		this.streamInfoUpdate(widthLimits);
		this.camera.thumb.putImage(this.el.fastThumb, false);
		this.el.videoDiv[0].poster = this.camera.thumbSrc;
	};


	this.loader = this.div.find(".loader");
	this.loader.spinner = this.div.find(".spinner");
	this.loader.showWithoutSpinner = function(){
		_this.loader.spinner.hide();
		_this.loader.fadeIn(LOADER_EFFECT_DURATION);
	};
	
	this.loader.showWithSpinner = function(){
		_this.loader.spinner.show();
		_this.loader.fadeIn(LOADER_EFFECT_DURATION);
	};


	// Fast thumb mechanism is consisted of image div element the size of the video element, wrapped by this.loader
	this.el.fastThumb = this.div.find(".fast-thumb");
	function playing(e){
		console.log("Video :: Playing");
		debugMessage("Video :: Playing");
		if (FIRST_TIME_LOADED){
			killWatchDog();
			_this.requestDuration.catchTime(TIME_STAMPS.T_STREAM_STARTED);
			_this.requestDuration.sendReport();

			_this.loader.fadeOut(LOADER_EFFECT_DURATION);
		}
		else {
			_this.loader.showWithSpinner();
		}
	}
	
	function showErrorAndStopTrying(){
		_this.loader.showWithoutSpinner();
		_this.showUserMessage(MESSAGES.HTML5_PLAYER_FINAL_ERROR);
		_this.retries = TIMES_TO_RETRY;
		killWatchDog();
		clearRequests();
	}

	function error(e){
		debugMessage("Video :: ERROR code " + e.target.error.code);
		switch(e.target.error.code){
			case 3: 
			case 4:
				// if (!keycheck){
				// 	break;
				// }
				debugMessage("------------- ERROR ------------");
				if (_this.retries > 0){
					
					timeout = setTimeout(function(){
						debugMessage("Retrying stream "+_this.retries);
						try {
							_this.retries--;
							_this.checkForPublishedStream();
						}
						catch(e){
							debugMessage("CUSTOM ERROR: "+e);
						}
					}, 800);
				}
				else {
					showErrorAndStopTrying();
				}
				debugMessage(" " +_this.streamSource);
				break;

			default:
				/*
				1 = ("MEDIA_ERR_ABORTED");
				2 = ("MEDIA_ERR_NETWORK");
				3 = ("MEDIA_ERR_DECODE");
				 */
				_this.loader.showWithoutSpinner();
				_this.showUserMessage(MESSAGES.HTML5_PLAYER_UNEXPECTED_ERROR.printf(e.target.error.code));
				debugMessage("Camera currently unavailable ["+e.target.error.code+"]");
				debugMessage(_this.streamSource);
				break;
		}
	}

	function play(e){
		console.log("Video :: Play");
		debugMessage("Video :: Play");
		if (!FIRST_TIME_LOADED){
			FIRST_TIME_LOADED = true;
		}
		//killWatchDog();
	}

	function loadStart(e){
		console.log("Video :: Load Start");
		debugMessage("Video :: Load Start");
		if (!FIRST_TIME_LOADED){
			_this.loader.showWithSpinner();
		}

	}

	function canPlay(e){
		console.log("Video :: Can Play");
		debugMessage("Video :: Can Play");
	}

	function loadedData(e){
		console.log("Video :: Loaded Data");
		debugMessage("Video :: Loaded Data");
	}

	function loadedMetaData(e){
		console.log("Video :: Loaded Meta Data");
		debugMessage("Video :: Loaded Meta Data");
	}

	function waiting(e){
		console.log("Video :: Waiting");
		debugMessage("Video :: Waiting");
	}

	function stalled(e){
		console.log("Video :: Stalled");
		debugMessage("Video :: Stalled");
	}

	function progress(e){
		console.log("Video :: Progress");
		debugMessage("Video :: Progress");
	}

	function suspend(e){
		debugMessage("Video :: Suspend");
		debugMessage("First Time Loaded: "+FIRST_TIME_LOADED);
		if (!FIRST_TIME_LOADED){
			_this.requestDuration.catchTime(TIME_STAMPS.T_STREAM_STARTED);
			_this.requestDuration.sendReport();
			_this.loader.fadeOut(LOADER_EFFECT_DURATION);
			_this.retries = TIMES_TO_RETRY;
		}
		//killWatchDog();
	}

	function readyStateChange(e){
		console.log("Video :: Ready State Change");
		debugMessage("Video :: Ready State Change");
	}

	function click(e){
		debugMessage("OP, tap tap");
	}

	function pause(e){
		console.log("Video :: Pause");
		playStream();
	}

	function emptied(e){
		console.log("Video :: Emptied");	
		//playStream();
	}

	function ended(e){
		console.log("Video :: Ended");	
		playStream();
	}


	_this.el.videoDiv.bind("pause", pause);
	_this.el.videoDiv.bind("emptied", emptied);
	_this.el.videoDiv.bind("ended", ended);

	_this.el.videoDiv.bind("canplay", canPlay);
	_this.el.videoDiv.bind("progress", progress);
	_this.el.videoDiv.bind("loadstart", loadStart);
	_this.el.videoDiv.bind("loadeddata", loadedData);
	_this.el.videoDiv.bind("loadedmetadata", loadedMetaData);
	_this.el.videoDiv.bind("playing", playing);
	_this.el.videoDiv.bind("play", play);
	_this.el.videoDiv.bind("error", error);
	_this.el.videoDiv.bind("stalled", stalled);
	_this.el.videoDiv.bind("readystatechange", readyStateChange);
	_this.el.videoDiv.bind("suspend", suspend);
	_this.el.videoDiv.bind("waiting", waiting);
	_this.el.videoDiv.bind("click", click);
	_this.el.videoDiv.bind("touchstart", click);

};

