// @author Tim Major
// @customized Ben Loveridge
// @revision 20071217-0955
//   -changed references to PrimaryURL() to GetSource() -- this works with Publish

//epg.js
//This contains functions useful when you want to have an Electronic Programming Guide for content - 'content' being defined
//as a group of one or more qvts.  A live channel is where this will most often be used, however, whenever any number of qvts
//are chained together by 'prev' and 'next' links, these functions can be used. If you have one qvt url, this EPG allows you 
//to obtain the urls of any qvts linked to it.  You can specify which direction you want to search for the linked qvts - by 
//the 'prev' or 'next' links, or both - and how many you want.
//Ultimately, the EPG returns an array of valid qvt urls.  The EPG actually goes out and acquires qvt objects (via 
//MN.QVT.AcquireQVT), which are then cached, so reacquiring that qvt is immediate and only a url is really needed.  
//The EPG has the following functions:
//  Load(options) - starts loading the EPG with the options submitted.
//  IsDone() - returns true if the EPG is done loading
//  InitialLoadStarted() - returns true if the user has started loading the EPG
//  Pause() - pauses the EPG loading.  The EPG will finish loading what it has already made requests for, and then pause
//  Continue() - continues the EPG loading after it has been paused
//You can also access data in the EPG with the following variables:
//  urlList - the list of urls that have been loaded
//  name - the name associated with the EPG
//The EPG fires an event(use MN.Event.Observe to catch it):
//  urlLoaded(urlList, isDone, name) - Fired when a valid url has been loaded in the EPG.  The isDone flag will be set to
//  true if the EPG is done loading
 
//The MN.EPG class represents an electronic programming guide.
MN.EPG = MN.Class(MN.EventSource);

//Constructor for an EPG.  ContentType is used in conjuction with a lot of the other widgets that we use for the template
//player and is optional - it will default to "vod" - which is then set as a piece of metadata, "mn_content_type" for 
//the qvts loaded by this EPG.  The second parameter, name, is passed back with the events fired by the EPG, and can
//be used to identify a unique instance of the EPG.
MN.EPG.prototype.initialize = function(startURL, name, contentType){
    MN.EventSource.prototype.initialize.apply(this);
	if(name)
		this.name = name;
	this.startURL = startURL;
	this.urlList = new Array(startURL);
	this._contentType = contentType || 'vod';
	
	log('EPG construct done\n');
}

//Starts obtaining the valid urls for this EPG.  The parameter, options, is used to define in what direction ('forward' to 
//go forward in the chain of qvts, 'back' to go backward) and how many pieces of content to load, as well as how much delay
//to allow before grabbing a new qvt. The options parameter is an object with the following properies available to define:
//  1.  'back' - This property should have a numerical value (i.e. "{'back' : 3}"), and tells the epg to load qvts previous 
//  to the start qvt, the number defining how many OTHER qvts besides the original to load.
//  2.  'forward' - This property should have a numerical value just like 'back', and tells the epg how many qvts after the 
//  start qvt to load (EXCLUDING the start qvt, like 'back').
//  FOR 'BACK' OR 'FORWARD', A VALUE OF -1 WILL CAUSE THE EPG TO LOAD ALL AVAILABLE CONTENT.    
//  3.  'delay' - How many seconds to wait between loading content.  This should be a number.
//You can define multiple properties. For example, "{'back' : 3, 'forward' : 4, 'delay' : .5}" will load 3 previous and 
//4 following qvts, with a half second delay between loads.  
//The options parameter is, well, optional.  If you do not include it, the EPG will load all valid qvts in the 'back'
//direction, with a one second delay between loads. Also, if you do not include a 'back' or 'forward', but 'delay', it will load 
//backwards as many as it can with that delay.
MN.EPG.prototype.Load = function(options){
	//refresh the internal data.  This will allow the user to call this function multiple times if, for example, they decided 
	//they needed more qvts loaded than they did the first time
	this.urlList = new Array(this.startURL);
	//how many to load in this direction
	this._forwardLimit = 0; 
	this._backLimit = 0;
	//the number of valid urls we have loaded so far
	this._forward = 0; 
	this._back = 0;                   
	//For the delay, we use the setTimeout function, but cannot pass along data, so we need variables
	this._nextURL;
	this._prevURL;
	this._forwardDone = true;
	this._backDone = true;
	this._isDone = false;
    this._paused = false;
	
	//Parse the options
	if(typeof options == "object"){
		this._forwardLimit = options.forward >= -1 ? options.forward : 0;
		this._backLimit = options.back >= -1 ? options.back : options.delay ? -1 : 0;
		this._delay = options.delay * 1000 || 1000;
	}	
	else if(typeof options == "undefined"){
		this._forwardLimit = 0;
		this._backLimit = -1;
		this._delay = 1000;
	}
	else{
		logError('EGP: StartInitLoad options not valid data type');
		this._isDone = true;		
		return;
	}
	
	this._forwardDone = this._forwardLimit == 0 ? true : false;
	this._backDone = this._backLimit == 0 ? true : false;
	log('EPG: options parsed:\n  _forwardLimit: ', this._forwardLimit, '\n  _backLimit: ', this._backLimit, '\n  delay: ', this._delay);	
	this._Start();	
}


MN.EPG.prototype._Start = function(){
	var qvt = MN.QVT.AcquireQVT(this.startURL);
	if(qvt.IsLoading())
		MN.Event.Observe(qvt, 'TimelineLoaded', this._StartQVTLoaded);
	else
		this._StartQVTLoaded(qvt);
}

MN.EPG.prototype._StartQVTLoaded = function(qvt){
	log('EPG: _StartQVTLoaded');
    MN.Event.StopObserving(qvt, 'TimelineLoaded', this._StartQVTLoaded);
    qvt.meta.mn_content_type = this._contentType;
	if(!this._forwardDone || !this._backDone){
		if(!this._forwardDone){
			log('EPG: starting forward');
			var nextURL = qvt.NextURL();
			if(nextURL){
				var next = MN.QVT.AcquireQVT(nextURL);
				if(next.IsLoading())
					MN.Event.Observe(next, 'TimelineLoaded', this._OnNextQVTLoaded);
				else
					this._OnNextQVTLoaded(next);	
			}
			else{
				log('EPG: no initial nextURL');
				this._forwardDone = true;
				if(this._backDone){
					log('EPG: no init nextURL, done loading');
					this._isDone = true;
					this.FireEvent('urlLoaded', this.urlList, true, this.name);
				}
			}
		}
		if(!this._backDone){
			log('EPG: starting backward');
			var prevURL = qvt.PrevURL();
			if(prevURL){
				var prev = MN.QVT.AcquireQVT(prevURL);
				if(prev.IsLoading())
					MN.Event.Observe(prev, 'TimelineLoaded', this._OnPrevQVTLoaded);
				else
					this._OnPrevQVTLoaded(prev);	
			}
			else{
				log('EPG: no initial prevURL');
				this._backDone = true;
				if(this._forwardDone){
					log('EPG: no init prevURL, done loading');
					this._isDone = true;
					this.FireEvent('urlLoaded', this.urlList, true, this.name);
				}
			}
		}
	}
	else{
		logError('EPG: no additional qvts were requested to load');
		this._isDone = true;
		this.FireEvent('urlLoaded', this.urlList, true, this.name);
	}
	

	MN.QVT.ReleaseQVT(qvt);
}

MN.EPG.prototype._OnNextQVTLoaded = function(qvt){
	log('EPG: _OnNextQVTLoaded');
	MN.Event.StopObserving(qvt, 'TimelineLoaded', this._OnNextQVTLoaded);
	qvt.meta.mn_content_type = this._contentType;
	
	// this.urlList.push(qvt.PrimaryURL());
	this.urlList.push(qvt.GetSource());
	this._nextURL = qvt.NextURL();
	if(this._nextURL && (++this._forward != this._forwardLimit)){
    	//Check for paused and don't load any more urls if it is true.  Note: this
        //doesn't come at the beginning of the function because we need _nextURL (and _prevURL)
        //for Continue(), as well as to set _forward to the proper value (so we don't load
        //an extra url)
        if(this._paused){
            log('EPG: pausing forward');
            return;
        }
        this.FireEvent('urlLoaded', this.urlList, false, this.name);
        setTimeout(this._GetNextQVT, this._delay);
	}
	else{
		log('EPG: done loading forward');
		this._forwardDone = true;
		this._nextURL = null; //Prevents Continue() from doing anything (potentially creating a duplicate url)
		this._forwardTimer = null;
		if(this._backDone){
			logError('EPG: done loading \'', this.name, '\'');
			this._isDone = true;
			this.FireEvent('urlLoaded', this.urlList, true, this.name);
		}	
	}
	
	MN.QVT.ReleaseQVT(qvt);
}

MN.EPG.prototype._GetNextQVT = function(){
	var qvt = MN.QVT.AcquireQVT(this._nextURL);
	if(qvt.IsLoading())
		MN.Event.Observe(qvt, 'TimelineLoaded', this._OnNextQVTLoaded);
	else
		this._OnNextQVTLoaded(qvt);
}

MN.EPG.prototype._OnPrevQVTLoaded = function(qvt){
	log('EPG: _OnPrevQVTLoaded');
	MN.Event.StopObserving(qvt, 'TimelineLoaded', this._OnPrevQVTLoaded);
	qvt.meta.mn_content_type = this._contentType;
	
	// this.urlList.push(qvt.PrimaryURL());
	this.urlList.push(qvt.GetSource());
	this._prevURL = qvt.PrevURL();
	if(this._prevURL && (++this._back != this._backLimit)){
	    if(this._paused){
            log('EPG: pausing backward');
            return;
        }
        this.FireEvent('urlLoaded', this.urlList, false, this.name);
		setTimeout(this._GetPrevQVT, this._delay);
	}
	else{
		log('EPG: done loading backward');
		this._backDone = true;
		this._prevURL = null;
		this._backTimer = null;
		if(this._forwardDone){
			logError('EPG: done loading \'', this.name, '\'');
			this._isDone = true;
			this.FireEvent('urlLoaded', this.urlList, true, this.name);
		}	
	}
	
	MN.QVT.ReleaseQVT(qvt);
}

MN.EPG.prototype._GetPrevQVT = function(){
	var qvt = MN.QVT.AcquireQVT(this._prevURL);
	if(qvt.IsLoading())
		MN.Event.Observe(qvt, 'TimelineLoaded', this._OnPrevQVTLoaded);
	else
		this._OnPrevQVTLoaded(qvt);
}

//"Global" functions
MN.EPG.prototype.IsDone = function(){
    return this._isDone;
}

MN.EPG.prototype.InitialLoadStarted = function(){
    return !(this._isDone == null);
}

MN.EPG.prototype.Pause = function(){
    log('EPG: Pause()');
    this._paused = true;
}

MN.EPG.prototype.Continue = function(){
    log('EPG: Continue()');
    this._paused = false;
    if(this._nextURL){
        log('EPG: continuing forward');
        this._GetNextQVT();
    }
    if(this._prevURL){
        log('EPG: continuing backward');
        this._GetPrevQVT();
    }
}