﻿// Generic download manager facade. Responsible for adding the
// required object/embed tags to pages and wrapping the DM API
// with more JS-friendly interfaces, e.g. converting XML to JSON etc.

// Should be customer-agnostic and have no dependency on any JS libs e.g. JQuery

// Does assume that Function.prototype.bind is defined (and maybe some string / array prototypes)

function DownloadManagerVersionCheck(custId, callback)
{
	this.custId = custId;
	this.win = window.top;
	this.doc = window.top.document;
	this.callback = callback;
	if (document.all)
		this.isMozilla = false;
	else
		this.isMozilla = true;
	this.elementId = '__dmVersion';	
	if (!this.doc.getElementById(this.elementId)){
		var hasPlugin = !this.isMozilla;
		for (var i = 0; i < navigator.plugins.length; i++) {
			// if no plugin present, assume no DM installed
			if (navigator.plugins[i].filename.toLowerCase().indexOf("npentriqversioncheckmozillaplugin") >= 0){
				hasPlugin = true;
				break;
			}
		}
		if (hasPlugin) this.embedTag();
		else FiveDownloadPlayer.redirectToInstall();
	}
	this.waitUntilApiReady();
}

DownloadManagerVersionCheck.prototype = {
	
	embedTag: function()
	{
		var obj = this.doc.createElement('object');
		obj.setAttribute('id', this.elementId);
		obj.setAttribute('width', '0');
		obj.setAttribute('height', '0');
		if (this.isMozilla)
		{
			// <object id="__dmVersion" type="application/entriq-version-check-npruntime" width="0" height="0" VIEWASTEXT ></object>
			obj.setAttribute('type', 'application/entriq-version-check-npruntime');	
		}
		else
		{
			// <object id=\"__dmVersion\" classid=\"clsid:FC7F24C5-7DC3-48DF-A00D-F57DE89EADC5\" width=\"0\" height=\"0\" VIEWASTEXT ></object>
			obj.setAttribute('classid', 'clsid:FC7F24C5-7DC3-48DF-A00D-F57DE89EADC5');
		}
		this.doc.body.appendChild(obj);	
	},
	
	waitUntilApiReady: function()
	{
		// this.api = this.isMozilla ? this.doc.getElementById(this.elementId) : this.doc.getElementById(this.elementId).object;
		this.api = this.doc.getElementById(this.elementId);
		var isReady = this.api && (this.isMozilla == false || this.api.GetDMExtVersion);
		if (isReady)
			this.onReady();
		else
			this.win.setTimeout(this.waitUntilApiReady.bind(this), 100); // wait
	},
	
	onReady: function()
	{
		var v = false;
		try
		{
			v = this.api.GetDMExtVersion("CONFIG", this.custId).split("=")[1].split('.');
			for (var i = 0; i < v.length; i++)
				v[i] = parseInt(v[i]);
		}
		catch (e)
		{
			v = false;
		}
		this.callback(v);
	}
}

DownloadManagerVersionCheck.compareVersions = function(x, y)
{
	// expects version number arrays
	if (x == y)
		return 0;
	if (!x)
		return -1;
	if (!y)
		return 1;
	if (x.length != y.length)
		throw new Error("Cannot compare version numbers of different sizes");
	for (var i = 0; i < x.length; i++)
	{
		if (x[i] > y[i])
			return 1;
		else if (x[i] < y[i])
			return -1;
	}
	return 0;
}

function DownloadManager(custId, onReady)
{
	//this.addListener(new ConsoleLogger());
	this.custId = custId;
	this.onReady = onReady.bind(this);
	if (document.all)
		this.isMozilla = false;
	else
		this.isMozilla = true;
	this.init();
}

DownloadManager.checkInstalledVersion = function(custId, callback)
{
	new DownloadManagerVersionCheck(custId, callback);
}

DownloadManager.prototype = {

	init: function()
	{
		this.win = window.top;
		this.doc = window.top.document; 
		this.elementId = '__dm';
		
		if (!this.doc.getElementById(this.elementId))
			this.embedTag();
			
		this.waitUntilApiReady();
	},
	
	embedTag: function()
	{
		var api;
		if (this.isMozilla)
		{
			// <embed id="__dm" name="__dm" type="application/mozilla-npruntime-entriq-plugin" hidden="true"></embed>
			api = this.doc.createElement('embed');
			api.setAttribute('type', 'application/mozilla-npruntime-entriq-plugin');
			api.setAttribute('hidden', 'true');
			this.doc.body.appendChild(api);
		}
		else
		{
			// <object id="__dm" classid="clsid:CE7D2BF2-D173-4CE2-9DAF-15EA153B5B43" VIEWASTEXT></object>
			api = this.doc.createElement('object');
			api.setAttribute('classid', 'clsid:CE7D2BF2-D173-4CE2-9DAF-15EA153B5B43');
		}
		api.setAttribute('id', this.elementId);
		api.setAttribute('name', this.elementId);
		this.doc.body.appendChild(api);
	},
	
	waitUntilApiReady: function()
	{
		this.api = this.isMozilla ? this.doc.embeds[this.elementId] : this.doc.getElementById(this.elementId);
		var isReady = this.api && (this.isMozilla == false || this.api.QueryAllItemsAttributeEquals);
		if (isReady)
		{
			if (this.isMozilla)
			{
				this.api.SetCustId(this.custId);
			}
			else
			{
				this.api.CustId = this.custId;
				if (!this.isMozilla)
					DownloadManagerEventSource.registerInternetExplorerCallbacks(this.elementId);				
			}
			this.onReady();
		}
		else {
			this.win.setTimeout(this.waitUntilApiReady.bind(this), 100); // wait
		}
	},
	
	queryJobs: function()
	{
		try
		{
			var xml = this.api.QueryAllItemsAttributeEquals("*", "*", "*");
		}
		catch(err)
		{
			return [];		
		}
		
		if (xml.indexOf("<QueryItems/>") > 0)
			return [];
		
		var xotree = new XML.ObjTree();
		xotree.attr_prefix = '@';
		var json = xotree.parseXML(xml);
		if (json.parseerror)
			throw new Error("could not parse XML " + xml);
		
		var items = json.Query.QueryItems.Item;
		
		if (!items)
			throw  new Error("could not parse items: " + xml);
		if (items instanceof Array)
			return items;
		else
			return [items];
	},
	
	onReady: function()
	{
		// passed in via constructor
	},
	
	downloadItem: function(itemData)
	{
		try {
			var title = itemData.Title.replace("&", "&amp;");
		
			var xml = '<DownloadItems GetLicense="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Valid="true">';
			xml += '<Item Overwrite="true" UseKeyId="true" AccountId="' + this.custId + '" CrmId="' + this.custId + '" LicenseUrl="' + itemData.LicenseUrl +
						'" SessionId="' + itemData.SessionId + '" Ticket="' + itemData.Ticket + '" ChannelId="' + itemData.ChannelId +
						'" ItemId="' + itemData.ItemId + '" Type="Television" Device="PC" Title="' + title + '" AdSupport="'+ itemData.HasAdverts +'" FileName="' + itemData.FileName +
						'" SourceUrl="' + itemData.Url + '" SamiFileSource="' + itemData.SamiFileSource + '" SamiFileType="' + itemData.SamiFileType + '">';
			for (var key in itemData)
			{
				if (key == "ChannelId" || key == "Title" || key == "FileName" || key == "Url" || key == "SamiFileSource" || 
					key == "SamiFileType" || key == "Ticket" || key == "SessionId" || key == "LicenseUrl" || 
					key == "HasAdverts" || key == "SponsorUrl" || key == "IdentUrl")
					continue;
				var value = itemData[key];
				if (value instanceof Date)
					value = value.toISO8601String();
				else if (value != null && value != undefined && value.toString)
					value = value.toString().replace("&", "&amp;").replace("'", "&apos;");
				xml += '<' + key + '>' + value + '</' + key + '>';
			}
			if (itemData.HasAdverts){
				xml += '<Ads>';
				var identId = '';
				var sponsorId = '';
				
				if (itemData.IdentUrl != ''){
					identId = itemData.IdentUrl.substring(itemData.IdentUrl.lastIndexOf('/')+1);
					xml += '<Ad Url="'+ itemData.IdentUrl +'" ID="'+ identId +'" />';
				}
				if (itemData.SponsorUrl != ''){
					sponsorId = itemData.SponsorUrl.substring(itemData.SponsorUrl.lastIndexOf('/')+1);
					xml += '<Ad Url="'+ itemData.SponsorUrl +'" ID="'+ sponsorId +'" />';
				}
				xml += '</Ads><PlayList><ASX version="3.0"><TITLE>Content</TITLE>';
				if (itemData.IdentUrl != ''){
					xml += '<ENTRY ClientSkip="no"><TITLE>Ident</TITLE><PARAM NAME="Prebuffer" VALUE="false" /><PARAM NAME="AdsArguments" VALUE="" />'+
						'<PARAM Name="AdsID" Value="'+ identId +'" /><REF HREF="'+ itemData.IdentUrl +'" /></ENTRY>';
				}
				if (itemData.SponsorUrl != ''){
					xml += '<ENTRY ClientSkip="no"><TITLE>Sponsor</TITLE><PARAM NAME="Prebuffer" VALUE="false" /><PARAM NAME="AdsArguments" VALUE="" />'+
						'<PARAM Name="AdsID" Value="'+ sponsorId +'" /><REF HREF="'+ itemData.SponsorUrl +'" /></ENTRY>';
				}
				xml += '<ENTRY ClientSkip="yes"><TITLE>Content</TITLE><PARAM NAME="Prebuffer" VALUE="false" />'+
					'<REF HREF="'+ itemData.Url +'" /></ENTRY></ASX></PlayList>';
			}
			xml += '</Item>';
			xml += '</DownloadItems>';
		}
		catch (e)
		{
			throw new Error("could not add download job: " + e);
		}
		//var added = false;
		//alert(xml);
		try {
			this.api.AddDownloadJobEx(xml);
			//added = true;
		}
		catch (e)
		{
			this.handleDownloadError(e);
		}
		//if (added)
		//	this.launchUserInterface();
	},
	
	handleDownloadError: function(e)
	{
		var msg = this.isMozilla ? e : e.message;
		alert("error adding download job: " + msg);
	},
	
	launchUserInterface: function(Page, PlayItem)
	{
		if ((typeof(Page) == 'undefined') || (typeof(PlayItem) == 'undefined') || (Page == null) || (PlayItem == null))
		{
			//this.api.LaunchUserInterface("");
			this.api.LaunchUserInterface("Mode=*&CustId=" + this.custId);
		}
		else 
		{
			this.api.LaunchUserInterface("Mode=*&CustId=" + this.custId + "&libid=" + PlayItem + "&page=" + Page);
		}		
	},
	
	suspendItem : function(libId)
	{
	    this.api.SuspendJob(libId);
	},
	
	resumeItem : function(libId)
	{
	    this.api.ResumeJob(libId);
	},
	
	addListener: function(listener)
	{
		DownloadManagerEventSource.listeners.push(listener);
	}
}

// -------------------------------------
// UNDOING HIDEOUS GLOBAL EVENT-HANDLING
// -------------------------------------

function DownloadManagerEventSource() { }
DownloadManagerEventSource.listeners = [];
DownloadManagerEventSource.events = [
	"ItemRemoved",
	"ItemAdded",
	"DownloadStarted",
	"DownloadProgress",
	"DownloadComplete",
	"DownloadPaused",
	"DownloadResumed",
	"DownloadRetrying",
	"DownloadError",
	"OpenUserInterface"
];
DownloadManagerEventSource.dispatch = function(eventName, args)
{
	// console.log("DownloadManagerEventSource.dispatch(" + eventName + ", [" + args.length + " args])");
	for (var i = 0; i < args.length; i++)
	{
		var arg = args[i];
		if (arg && arg.indexOf && arg.indexOf('<') == 0) // if it starts with an open angle bracket, it MUST be xml :o)
		{
			var xotree = new XML.ObjTree();
			xotree.attr_prefix = '@';
			var json = xotree.parseXML(arg);
			if (json.parseerror)
			{
				console.log("could not parse XML argument in event " + eventName + ". XML was: " + arg);
				throw new Error("could not parse XML argument in event " + eventName + ". XML was: " + arg);
			}
			args[i] = json;
		}
	}
	for (var i = 0; i < DownloadManagerEventSource.listeners.length; i++)
	{
		var listener = DownloadManagerEventSource.listeners[i];
		if (!listener)
			continue;
		eval("var f = listener.on" + eventName);
		if (f)
		{
			// listener attached to specific event (onXyz)
			try {
				f.apply(listener, args);
			}
			catch (e) {
				//throw "listener " + listener + " raised error on " + eventName + ":" + e;
			}
		}
		if (listener.onDownloadManagerEvent)
		{
			// listener attached to catch all event
			try {
				listener.onDownloadManagerEvent(eventName, args);
			}
			catch (e) {
				//throw new Error("listener " + listener + " raised error on catch all (onDownloadManagerEvent)" + ":" + e);
			}
		}
	}
}
DownloadManagerEventSource.registerInternetExplorerCallbacks = function(dmElementId)
{
	for (var i = 0; i < DownloadManagerEventSource.events.length; i++)
	{
		var evt = DownloadManagerEventSource.events[i];
		var ieHandlerDefinition = "function " + dmElementId + "::" + evt + "() { DownloadManagerEventSource.dispatch('" + evt + "', arguments) }";
		eval(ieHandlerDefinition);
	}
}

for (var i = 0; i < DownloadManagerEventSource.events.length; i++)
{
	var evt = DownloadManagerEventSource.events[i];
	if (this[evt] && this[evt] instanceof Function)
		console.log("Cannot attach event handler: global function " + evt + " is already defined:\n" + this[evt]);
	else
	{
		var globalHandlerDefinition = "function " + evt + "() { DownloadManagerEventSource.dispatch('" + evt + "', arguments) }";
		eval(globalHandlerDefinition);
	}
}

i = undefined; // unpollute global scope

// -------------------------------------

function ConsoleLogger()
{
}

ConsoleLogger.prototype =
{
	onDownloadManagerEvent: function(eventName, args)
	{
		if (console && console.log)
		{
			var argMsg = '';
			for (var i = 0; i < args.length; i++)
			{
				if (argMsg.length > 0)
					argMsg += '\n';
				argMsg += args[i];
			}
			console.log("DM EVENT:" + eventName + ' (' + argMsg + ')');
		}
	}
}


// ------------------------------------

Date.prototype.toISO8601String = function (format, offset) {
    /* accepted values for the format [1-6]:
     1 Year:
       YYYY (eg 1997)
     2 Year and month:
       YYYY-MM (eg 1997-07)
     3 Complete date:
       YYYY-MM-DD (eg 1997-07-16)
     4 Complete date plus hours and minutes:
       YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
     5 Complete date plus hours, minutes and seconds:
       YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
     6 Complete date plus hours, minutes, seconds and a decimal
       fraction of a second
       YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)
    */
    if (!format) { var format = 6; }
    if (!offset) {
        var offset = 'Z';
        var date = this;
    } else {
        var d = offset.match(/([-+])([0-9]{2}):([0-9]{2})/);
        var offsetnum = (Number(d[2]) * 60) + Number(d[3]);
        offsetnum *= ((d[1] == '-') ? -1 : 1);
        var date = new Date(Number(Number(this) + (offsetnum * 60000)));
    }

    var zeropad = function (num) { return ((num < 10) ? '0' : '') + num; }

    var str = "";
    str += date.getUTCFullYear();
    if (format > 1) { str += "-" + zeropad(date.getUTCMonth() + 1); }
    if (format > 2) { str += "-" + zeropad(date.getUTCDate()); }
    if (format > 3) {
        str += "T" + zeropad(date.getUTCHours()) +
               ":" + zeropad(date.getUTCMinutes());
    }
    if (format > 5) {
        var secs = Number(date.getUTCSeconds() + "." +
                   ((date.getUTCMilliseconds() < 100) ? '0' : '') +
                   zeropad(date.getUTCMilliseconds()));
        str += ":" + zeropad(secs);
    } else if (format > 4) { str += ":" + zeropad(date.getUTCSeconds()); }

    if (format > 3) { str += offset; }
    return str;
}


// <script type="text/javascript" event="ItemAdded(LibId, strXml)" for="oDMControl">
//	 oDMControl_ItemAdded(arguments[0], arguments[1]);
// </script>