/**
 * ASP Transcript Control
 * (c)2008 Alexander Street Press
 * All Rights Reserved
 */

//declaring the class with prototype's help
var TranscriptLive = Class.create();

//defining the rest of the class implementation
TranscriptLive.prototype = {

/**
 * Constructor - takes the element used to construct
 * the actual UI, either id or element object
 * @access public
 */
initialize: function(elem) {
    this.div=$(elem);
    this.lastHighlightTime = -1;
    this.autoScrollling = false;
	this.highlightedElems = new Array();
	this.TCIndex = 0;
	this.objCache = new Array();
	this.spanCache = new Array();
	this.tcCache = new Array();
    this.lastScrollTag = null;
    this.timecodes = null;
    this.entityId = $('video_entity_id').innerHTML;
	this.requestTimecodes();
},

requestTimecodes:function() {
	var url = '/Video/timecodes/' + this.entityId;
	new Ajax.Request(url, {
  		method: 'get',
  		evalJSON: 'true',
 		onSuccess:this.timecodeSuccessCallback.bind(this),
 		onFailure:this.timecodeFailCallback.bind(this)
	});

},

/*
 * This function loads the list of paragraph tags for  
 */
timecodeSuccessCallback:function(transport) {
	this.timecodes = transport.responseJSON;
},

/*
 * This function loads the 
 */
timecodeFailCallback:function(transport) {
	setTimout(this.requestTimecodes.bind(this),500);
},

/**
 * Subscript to an event. Transcript sends following events
 * transcript:newSelection
 * @access public
 */
observe: function(event, handler) {
	this.div.observe(event,handler);
},

/*
 * Resets internal position counters
 */
reset:function() {
    this.autoScrollling = false;
	this.TCIndex = 0;
    this.lastHighlightTime = -1;
    this.lastScrollTag = null;
},

/**
 * Highlight chunks of text which contain given position
 * 
 * Any chunks previously highlighted with this function will
 * be unhighlighted. Highlights appear over any highlight 
 * made with highlightPeriod method
 * @access public
 */
highlightTime: function(sec, pindexArr)
{
	/*
	 * to high a position, we need to highlight the tags with
	 * the timecode equal to or immediately preceding sec
	 * 
	 * the widget emits a global javascript array called
	 * timecodes which contains a sorted list of timecodes in
	 * the transcript
	 */
	
	//find large timecode before sec
	
	var minTC = 0;
	var maxTC = 0;
	var newHLElems = new Array();
	if(this.timecodes[this.TCIndex] > sec) {
		this.TCIndex = 0;
	}
	for(var TCIdx=this.TCIndex;TCIdx<this.timecodes.length;TCIdx++) {
		if(this.timecodes[TCIdx] > sec) {
			maxTC = this.timecodes[TCIdx];
			if((TCIdx-1) > 0) {
				minTC = this.timecodes[TCIdx-1];
			}
			break;
		}
	}
	this.TCIndex = TCIdx;
	if(this.TCIndex > 0) {
		this.TCIndex--;
	}
	var scrollText = false;
	
	var delta = Math.abs(sec - this.lastHighlightTime);
	//if(delta > 20) alert("tc index: " + this.TCIndex + " seconds: " + sec + " last highlight: " + this.lastHighlightTime + " delta: " + delta + " timecode val: " + timecodes[this.TCIndex]);	
	if(this.timecodes[this.TCIndex] != this.lastHighlightTime || (this.lastHighlightTime != -1 && delta > 20)) {
		this.lastHighlightTime=this.timecodes[this.TCIndex];
		for(var j=0;j<pindexArr.length;j++) {
			var paragraphElems = this._highlightChildrenInRange(pindexArr[j], minTC, maxTC, scrollText);
			if(!scrollText && paragraphElems.length > 0) {
				scrollText = true;
			}
			while(paragraphElems.length > 0) {
				newHLElems.push(paragraphElems.pop());
			}
		}
		if(newHLElems.length > 0) {
			this.scrollIntoView(sec,newHLElems[newHLElems.length-1]);
		}
		this._clearHighlights(newHLElems);
	}
},

/*
 * Functions to get and cache element references to speed up execution
 * Uses getElementById as the $('id') method doesn't perform well in IE
 */

_getElemTC:function(elemId) {
	if(typeof(this.tcCache[elemId]) == 'undefined') {
		this.tcCache[elemId] = this._getElem(elemId).getAttribute('tc');
	}
	return(this.tcCache[elemId]);	
},

_getPSpans:function(pId) {
	if(typeof(this.spanCache[pId]) == 'undefined') {
		this.spanCache[pId] = this._getElem(pId).getElementsByTagName('span');
	}
	return(this.spanCache[pId]);
},

_getElem:function(elemName) {
	if(typeof(this.objCache[elemName]) == 'undefined') {
		this.objCache[elemName] = document.getElementById(elemName);
	}
	return(this.objCache[elemName]);
},

/*
 * Clears cache of element positions for highlighting modified transcript text
 * does not clear timecode cache, as these should never change
 */
clearCache:function() {
	delete this.objCache;
	delete this.spanCache;
	this.objCache = new Array();
	this.spanCache = new Array();
},

clearAllHighlights:function() {
	var foo = new Array();
	this._clearHighlights(foo);
},

/*
 * Clears highlighted elemnts, given a list of newly highlighted elements
 */

_clearHighlights:function(newHLElems) {
	var idx = 0;
	var elem = null;
	var oldHighlights = this.highlightedElems;
	this.highlightedElems = new Array();
	while(oldHighlights.length > 0) {
		idx = -1;
		elem = oldHighlights.shift();
		idx = newHLElems.indexOf(elem);

		var elemObj =this._getElem(elem);
		if(elemObj) {
			if(idx == -1) {
				elemObj.style.backgroundColor = '';
			} else {
				newHLElems[idx] = null;
				this.highlightedElems.push(elem);
			}
		}
	}
	while(newHLElems.length > 0) {
		this.highlightedElems.push(newHLElems.shift());
	}
},

/**
 * ensure the first tag with the given timecode is visible in the
 * top two thirds of the page, otherwise, scroll the page so that
 * it is close to the top
 */
scrollIntoView: function(sec, tagId)
{

	//don't update if we're already animating
	if (this.autoScrollling)
		return;
	
	var start=0;
	//var tag = $(tagId);

	var tag =this._getElem(tagId);
	if (tagId != this.lastScrollTag)
	{
		//get x/y of where div is on the page
		var divpos=this.div.positionedOffset();
		
		//tag.offsetTop is where the top of the tag is on the page
		//(or where it would be if there was no scroll bar). We
		//subtract the position of the top of the transcript div
		//to make it relative
		var offset = tag.offsetTop - divpos[1];
		
		//get height accounting for horizontal scroll bar
		var visibleHeight=parseInt(this.div.style.height)-16;
		var visibleTop=this.div.scrollTop;
		var visibleBottom=visibleTop+visibleHeight;
		
		//if the offset is already visible in the top 2 thirds of
		//of the page, do nothing, otherwise, we scroll
		if ((offset>=visibleTop) && (offset<=(visibleBottom-visibleHeight/3)))
		{
			return;
		}
		else
		{
			this.autoScrollling=true;
			
			var from=this.div.scrollTop;
			var to=Math.max(offset-24,0);
			new Effect.Tween(this.div, from, to, 
				{
					afterFinish:this._onScrollAnimationComplete.bind(this)
				},
				'scrollTop');
		}
		this.lastScrollTag=tagId;
	}
	
},

_onScrollAnimationComplete:function()
{
	//release the lock on further updates
	this.autoScrollling=false;
},


/**
 * Recursive search for elements with timecode in
 * given range, applying cssClass to matching elements
 * and removing cssClass from nonmatching elements
 * @access private
 */
_highlightChildrenInRange:function(pindex,start,end,scrollText) {
	var highlighted = false;
	var highlightedIds = new Array();
	var childSpans = this._getPSpans(pindex);
	
	for(var i=0;i<childSpans.length;i++) {
		if(childSpans[i].className != 'snippet') {
			var id = childSpans[i].id;
			var tc = this._getElemTC(id);
			if(tc < end && tc >= start && id) {
				childSpans[i].style.backgroundColor = '#ffff66';
				highlightedIds.push(id);
				highlighted = true;
			}
		}
	}
	/*if(highlighted && scrollText && highlightedIds.length > 0) {
		this.scrollIntoView(start, highlightedIds[0]);
	}*/
	return(highlightedIds);
}

//end of Transcript class definition
};	



