/** 
 * flvChat connects to a FLV-player-instance and creates a timed messaging-window 
 * Messages are linked to timecodes
 * 
 * 
 * @author Ivo Toby, ivo@i-v-o.nl www.i-v-o.nl
 * @version 0.01a (ALPHA)
 * @requires JW FLV MEDIA PLAYER 3.16 (http://www.jeroenwijering.com/?item=JW_FLV_Media_Player) , Prototype.js by Sam Stephenson (http://www.prototypejs.org/) and Scriptaculous (http://script.aculo.us/) by Thomas Fuchs and a fairly decent browser 
 * @constructor initialize
 * 
 * THIS CLASS IS GPL, see licence.txt .
 * 
 */


	var flvChat = Class.create(eventStack, {
		events : $H({
			click:$H({}),
			blur:$H({}),
			change:$H({}),
			mouseout:$H({}),
			mouseover:$H({}),
			select:$H({}),
			keydown:$H({}),
			submit:$H({})
		}),
		
		initialize:function(playerID, container, serverURI){
			// TODO: clean this up..
			this.serverURI = serverURI;
			this.instID = 'flvChat_' + parseInt(Math.random()*1000000);
			$(playerID).chat = this; //  assign this object to the player so it can reference to it on a event
			this.player = $(playerID);
			if (!this.player){
				return false;
			} 
			this.container = $(container);
			if (!this.container) return false;
			this.messageInterval = 5;
			this.currFile = '';
			this.timeBefore = 0;
			this.timeAfter = 0;
			this.index = 0;
			this.effectDuration = 0.2;
			this.addNavigation();
			this.initEvents();
			this.setLockAlert();
			this.locked = false;
			this.playing = false;
			this.messagesLoaded = false;
			this.messagesLoading = false;
			this.drawing = false;
			this.parsedStart = false;
		},
		
		addNavigation:function(){
			// TODO: refactor this
			var currHTML = this.container.innerHTML
			this.container.innerHTML = '<div id="' + this.instID + '_navigation" class="navigation"></div><div class="msgList" id="' + this.instID + '_content"></div><div id="' + this.instID + '_footer" class="footer"></div>' + currHTML;
			this.container = $(this.instID + '_content');
			this.drawNavigation($(this.instID + '_navigation'));
			this.drawFooter($(this.instID+ "_footer"));
			
		},
		
		getUpdate: function(type,pr1,pr2){
			// pass on typ to a method:
			if (this[type]){
				this[type](pr1,pr2);
			}else{
				// type not found.. do something?
			}
		},
		
		time:function(before, after){
			this.timeBefore = before;
			this.timeAfter = after;
			if (!this.playing) return;
			if (this.locked) return;
			this.setLockAlert();
			if (before < 3){
				if (this.messagesLoading) return;
				if (!this.parsedStart) {
					this.actOnTime(0);
					this.parsedStart = true;
				}
			}else{
				if ( (before % this.messageInterval) == 0){ // use modulus, TODO: check if using (this.timeBefore - before) == this.messageInterval works better
					this.actOnTime(before);  
				}
				this.parsedStart = false;
			}
		},
		
		actOnTime : function(before){
			if (this.drawing) return;
			this.drawing = true;
			$(this.instID + '_waiter').show();
			this.currTime = before;
			var messages = this.getMessagesAtTimeCode(before, before + this.messageInterval); //  get messages within time code
			if (messages.length > 0){
				this.drawMessages('', messages);
			}else{
				this.container.update(' <div class="noMessages">no messages found!</div>' );
			}
			$('responder').update('respond to timeline at ' + this.getNiceTime(before));
			setTimeout((function(){
				$(this.instID + '_waiter').hide();
				this.drawing = false;
			}).bind(this), 800)
		},
		
		getNiceTime : function(seconds){
			var floaty = Math.round( ((   (seconds / 60) - parseInt((seconds/60) )  ) *60 ) *100)/100 ;
			if (floaty.toString().length < 2) floaty = '0'+floaty;
			return parseInt((seconds/60)) + ':' + floaty;
		},
		
		state:function(state){
			/*
			 * 0 = stop
			 * 1 = buffer
			 * 2 = play
			 */
			this.playerState = state;
			var currFile = this.player.itemData(this.index).file;
			if (currFile != this.currFile){
				// reset
				this.container.update('');
				this.messages = $A({});
				this.playing = true;
				this.parsedStart = false;
			}
			this.currFile = currFile;
			switch (state) {
				case 1: // buffer
					//  preload some messages
					this.parsedStart = false;
					this.playing = true;
					this.drawing = false;
					$(this.instID + '_waiter').show();
					this.getMessages();
					break;
				case 2: // play
					if (this.messagesLoading){
						// TODO; check if there are situations where buffering doesn't occur.
						// pause playback, messages are not loaded
						//this.pauseMedia();
						//this.messagesTimeout();
						return;
					}else{
						$(this.instID + '_waiter').hide();
					}
					this.playing = true;
					break;
				default: // 0 : stop
					// clear chat screen and in memory messages
					this.playing = false;		
					this.parsedStart = false;
					break;
			}
			this.setLockAlert();
		},
		
		item:function(idx){
			this.index = idx;
		},
		
		load:function(percentage){
		},
		
		getMessagesAtTimeCode : function(before, after){
			var output = $A({});
			for (msgID in this.messages){
				var message = this.messages[msgID];
				if ( (message.timeCode >= before) && (message.timeCode < after) ){
					message.id = msgID;
					output.push(message);
				}
			}
			return output;
		},
		
		getMessages : function(){
			this.messagesLoading = true;
			this.messagesLoaded = false;
			var req = new authenticatedJsonProxy(this.serverURI);
			//req.debug =true;
			req.set('track', this.currFile);
			req.set('chatAction', 'getMessages');
			req.doRequest(this.setMessages.bind(this));
		},
		
		setMessages : function(messages){
			this.messages = messages;
			this.messagesLoaded = true;
			this.messagesLoading = false;
		},
		
		messagesTimeout : function(){
			if (this.messagesLoading){
				setTimeout(this.messagesTimeout.bind(this), 200);
			}else{
				//setTimeout((function(){this.playMedia.bind(this)}).bind(this), 1000);
				this.playMedia();
				this.setLockAlert();
			}
		},
		
		drawMessages : function(container, messages){
//			this.isDrawing = true;
			if (!container) {
				this.container.update('');
				container = this.container;
			}
	
			$A(messages).each(
				(function(message){
					container.insert(this.messageTemplate(message)); // append to container.
				}).bind(this)
			)
//			this.isDrawing = false;			
			 
		},
		
		makeArray : function(objects){
			var output = $A({});
			for (objID in objects){
				var obj = objects[objID];
				obj.id = objID;
				output.push(obj);
			}
			return output;
		},

		lockChat : function(evnt){
			this.locked = true;
			$('locker').hide();
			$('unlocker').show();
			this.setLockAlert();
		},
			
		unlockChat : function(evnt){
			this.locked = false;
			$('locker').show();
			$('unlocker').hide();
			this.setLockAlert();
			//this.actOnTime(this.currTime);
		},
		
		respond:function(evnt){
			var elem = evnt.element();
			var responder = this.responder(elem.parentID);
			if (responder){
				this.pauseMedia(); 
				responder.hide();
				elem.parentNode.insert(responder);
				new Effect.Appear(responder);
				this.lockChat();
			}else{//cancelled
				this.unlockChat();
				this.playMedia();
			}
		},
		
		responder : function(parentID){
			// TODO: refactor this
			if ($(parentID + "_responderForm")) {
				$(parentID + "_responderForm").remove();
			}else{
				var div = new Element('div', {className:'responderForm', id:parentID + "_responderForm"});
				div.insert('<span class="closer" id="closer">[close]</span>');
				this.observe('closer', 'click', (function(){new Effect.Fade(div, {duration:this.effectDuration});this.playMedia();}).bind(this))
				var nameDiv = new Element('div');
				var nameInput = new Element('input', {value:"Your name : ", type:'text', maxlength:100, id:parentID + '_nameInput'});
				nameInput.onfocus = (function(){if (nameInput.getValue() == "Your name : ") nameInput.value =''; }).bind(this);
				nameDiv.insert(nameInput);
				div.insert(nameDiv);
				
				var messageDiv = new Element('div');
				
				var messageTextArea = new Element('textarea', {maxlength:'1024', className: "messageInput", id:parentID+"_messageTextArea"}).update("Enter a message");
				messageTextArea.parentID = parentID;
				messageTextArea.onfocus = (function(){if (messageTextArea.getValue() == "Enter a message") messageTextArea.value =''; }).bind(this);

				messageDiv.insert(messageTextArea);
				messageDiv.insert('<span id="counter" class="counter">1024 chars available</span>');
				this.observe(messageTextArea, 'keydown', this.countInput.bindAsEventListener(this));				
				
				var submit = new Element('button', {id: parentID + '_submitter'}).update('submit')
				submit.parentID = parentID;
				this.currCallBack =this.submit.bindAsEventListener(this) 
				this.observe(submit, 'click', this.currCallBack);
				messageDiv.insert(submit);

				div.insert(messageDiv);
				nameInput.focus();
				return div;
			}
		},
		
		submit : function(evnt){
			// TODO: refactor this; more checks on entered content
			var parentID = evnt.element().parentID;
			var ok = true;
			var nameValue = $(parentID + "_nameInput").getValue();
			if (!nameValue || nameValue == 'Your name : '){
				$(parentID + "_nameInput").style.borderColor = 'red';
				ok = false;	
			}
			var messageValue = $(parentID+"_messageTextArea").getValue();
			if ( (!messageValue) || (messageValue == 'Enter a message')  || (messageValue.length > 1024)){
				$(parentID + "_messageTextArea").style.borderColor = 'red';
				ok = false;	
			}
			if (!ok) return;
			if (this.timeBefore > 0){
				var time = this.timeBefore;
			}else{
				var time = 1;
			}
			this.deobserve(evnt.element(), 'click',  this.currCallBack);			// remove the callback so the event will not occur again.
			evnt.element().disabled = true;
			$(parentID + "_responderForm").remove();
			var req = new authenticatedJsonProxy(this.serverURI);
			//req.debug =true;
			req.set('track', this.currFile);
			req.set('chatAction', 'submitMessage');
			req.set('timeCode', time);
			req.set('poster_name', nameValue);
			req.set('message', messageValue);
			req.set('parentID', parentID);
			req.doRequest(this.resetMessages.bind(this));
		},
		
		resetMessages : function(obj){
			this.messages = obj;
			this.actOnTime(this.currTime);
			this.playMedia();
			setTimeout(this.unlockChat.bind(this), ((this.messageInterval -1) *1000 ) );
		},
		
		countInput : function(evnt){
			var amount = evnt.element().getValue().length;
			$('counter').update((1024-amount + ' chars available'));
		},
		
		pauseMedia:function(){
			if (this.playing){
				this.player.sendEvent('playpause'); 
			}
			this.setLockAlert();
			this.playing = false;
		},
		
		playMedia:function(){
			if (!this.playing){
				this.player.sendEvent('playpause'); 
			}
			this.setLockAlert();
			this.playing = true;
		},
		
		setLockAlert : function(){
			// TODO: change name into a general status-handling function-name
			if (this.timeBefore < 1){
				$('responder').hide();
			}else{
				$('responder').show();				
			}
			if (!this.playing){
				$('lockStatus').className = 'locked';
				$('lockStatus').update(' [playing stopped] ');
			}else{
				if (this.locked){
					$('lockStatus').className = 'locked';
					$('lockStatus').update('<blink> [messages locked] </blink> ');				
				}else{
					$('lockStatus').className = 'open';
					$('lockStatus').update(' [messages playing] ');
				}
			}
		},
		
		/** feel free to override this function to allow fancy makeup :D, just keep the ID's and the event-handlers */
		toggleHelp : function(){
			var elem = $('helpContainer');
			if (elem){
				if ( elem.visible()){
					new Effect.Fade(elem, {duration:this.effectDuration});
				}else{
					new Effect.Appear(elem, {duration:this.effectDuration});
				}
			}else{
				var container = $(this.instID + "_navigation");
				container.insert(helpText);
				var elem = $('helpContainer');
				new Effect.Appear(elem, {duration:this.effectDuration});
				this.observe('helpClose', 'click', this.toggleHelp.bind(this)); 
			}
		},

		/** feel free to override this function to allow fancy makeup :D, just keep the ID's and the event-handlers */
		messageTemplate : function(message){
			var postDate = new Element('span', {id:message.id+'_date', className:'postdate'}).update(message.dateAdded);
			var msgHead = new Element('div', {id:message.id + '_head', className:'head'}).update(postDate);
			var responder = new Element('span', {id:message.id + "_respond", className:'respond'}).update('respond');
			responder.parentID = message.id;
			this.observe(responder,'click', this.respond.bindAsEventListener(this));
			msgHead.insert(responder);
			msgHead.insert(message.poster_name);

			var div = new Element('div', {id:message.id + '_message', className:'actualMessage'} ).update(message.message);
			var msgContainer = new Element('div', {id:message.id + '_container', className:'message'});
			msgContainer.insert(msgHead);
			msgContainer.insert(div);
			if (message.hasChildren){
				var children = this.makeArray(message.children);
				this.drawMessages(div, children);
			}
			return msgContainer;
		},
		
		/** feel free to override this function to allow fancy makeup :D, just keep the ID's and the event-handlers */
		drawNavigation : function(container){
			var waiter = new Element('img', {src:'images/waiter.gif', id:this.instID + '_waiter', align:'left', className:'chatWaiter'});
			waiter.hide();
			container.insert(waiter);
			//waiter.absolutize();
			
			var lockStatus = new Element('span', {className:'locked', id:'lockStatus'}).update(' [not playing] ');
			container.insert(lockStatus);
			
			var locker = new Element('span', {className:'locker', id:'locker'}).update(' lock messages ');
			container.insert(locker);
			this.observe(locker, 'click', this.lockChat.bindAsEventListener(this));
			
			var unlocker = new Element('span', {className:'unlocker', id:'unlocker'}).update(' sync messages ');
			unlocker.hide();
			container.insert(unlocker);
			this.observe(unlocker, 'click', this.unlockChat.bindAsEventListener(this));
			
			var responder = new Element('span', {className:'responder', id:'responder'}).update(' respond to timeline at  ');
			responder.parentID = 0;
			container.insert(responder);
			this.observe(responder, 'click', this.respond.bindAsEventListener(this));
		},
		
		/** feel free to override this function to allow fancy makeup :D, just keep the ID's and the event-handlers */
		drawFooter : function(container){
			container.insert('<div style="float:right;display:inline;" ><a href="http://www.tliner.com" style="color:black;" target="_blank"> <img src="http://www.tliner.com/images/logo.png" alt="Timeliner: react as it happens!" border="0" /></a></div>');			
			var help = new Element('span', {className:'help', id:'help'}).update('  TimeLiner Help  ');
			container.insert(help);
			this.observe(help, 'click', this.toggleHelp.bindAsEventListener(this));

			var help = new Element('span', {className:'help', id:'ads'}).update('  <a href="http://www.tliner.com/" target="blank">Get your own TimeLiner-Widget!</a>  ');
			container.insert(help);
			this.observe(help, 'click', this.toggleHelp.bindAsEventListener(this));
		}

		
	})
	
	// this function must be in global scope; the flashplayer likes it , dunno if it can be put into the class.. :)
	 getUpdate = function (typ,pr1,pr2,swf){
		var obj = $(swf).chat;
		obj.getUpdate(typ,pr1,pr2);
	}