/**
 * Creates a new video player connected to a specific WMP ActiveX-object in the HTML code
 * @class Represents a video player with core functionality
 * @constructor
 * @param {string} wmpElementId the HTML ID of the WMP object
 * @author Tor Erik Alræk
 * @version 2.6
 */



function WmvIECorePlayer(wmpElementId,log) {

	log.debug('WmvIECorePlayer created');
	// PRIVATE VARIABLES 
	var p; // player element
	this.p = p;
	var metaUrl = '';
	var progId;
	var hostProgId;
	var live = false;
	var timeBegin;
	var startPos;
	var posUpdateInterval = 500; //ms
	var me = this; // in WMP events, 'this' will not work

	var hUpdaterTimer; // Handles for setTimeouts/setIntervals
	var hLiveAttTimer;
	var hLiveRetryTimer;

	var reportWmpErrors = true;

	var bufferState;
	var packetsReceived = false;
	var oldPlayState;
	var oldOpenState;
	var lastProgress;
	var noProgressCounter;
	var lastErrorCode;
	var startAsPaused = false;
  var muted = false; // WMP forgets mute state when changing media, so we need to remember it
  var waitingForUser = 0;
  
  

	// for direktesendt video
	var retryCount = 0;
	var liveEvaluateCount = 0;

	var playStates = {
			0: 'Undefined',
			1: 'Stopped',
			2: 'Paused',
			3: 'Playing',
			4: 'ScanForward',
			5: 'ScanReverse',
			6: 'Buffering',
			7: 'Waiting',
			8: 'MediaEnded',
			9: 'Transitioning',
			10: 'Ready',
			11: 'Reconnecting'
		}

	var openStates = {
			0: 'Undefined',
			1: 'PlaylistChanging',
			2: 'PlaylistLocating',
			3: 'PlaylistConnecting',
			4: 'PlaylistLoading',
			5: 'PlaylistOpening',
			6: 'PlaylistOpenNoMedia',
			7: 'PlaylistChanged',
			8: 'MediaChanging',
			9: 'MediaLocating',
			10: 'MediaConnecting',
			11: 'MediaLoading',
			12: 'MediaOpening',
			13: 'MediaOpen',
			14: 'BeginCodecAcquisition',
			15: 'EndCodecAcquisition',
			16: 'BeginLicenseAcquisition',
			17: 'EndLicenseAcquisition',
			18: 'BeginIndividualization',
			19: 'EndIndividualization',
			20: 'MediaWaiting',
			21: 'OpeningUnknownURL'
		}

	// CONSTRUCTOR OPERATIONS
	try {
		var elm = $(wmpElementId);

		var newCode = '<object id="' + wmpElementId + '" class="' + elm.className + '" classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6">';
		newCode += '<param name="stretchToFit" value="true"><param name="uiMode" value="none"><param name="url" value="http://webtv.tv2.no/webtv/webtv/images/pixel.gif"><param name="autoStart" value="false">';
		newCode += '<param name="enableContextMenu" value="true"><param name="volume" value="85"><param name="windowlessVideo" value="false">';	
		newCode += '</object>';
		elm.outerHTML = newCode;

		p = $(wmpElementId);
		p.attachEvent('OpenStateChange', wmpOpenStChg);
		p.attachEvent('PlayStateChange', wmpPlayStChg);
		p.attachEvent('StatusChange', wmpStatusChange);
		p.attachEvent('Error', wmpError);
		p.attachEvent('Buffering', wmpBuffering);
		p.attachEvent('Click', wmpClick);

	} catch (e) {
		log.error('Player.constructor',e);
		log.error('Player.constructor','Could not access HTML element for WMP');
	}

	// PRIVATE METHODS
	// WMP event handlers
	function wmpOpenStChg(newState) {
		if (location.href.indexOf('debugStates=true')>0) log.debug('Player.wmpOpenStChg', newState + ': ' + openStates[newState]);
		if (newState == 14 || newState == 16 || newState == 18) {
			waitingForUser++;
		}
		if (newState == 15 || newState == 17 || newState == 19) {
			waitingForUser--;
		}
		if (me.openStateChange) {
			me.openStateChange(newState,oldOpenState);
		}

		if (newState==13 && p.currentMedia) {
			var itemType = p.currentMedia.getItemInfo('entry-type');
			if(itemType == 'error') {
				var errorStr = p.currentMedia.getItemInfo('title');
				var errorCode = p.currentMedia.getItemInfo('abstract');
				me.onStatusChange(me.MSG_PROGRAM_ERROR,errorStr,errorCode);
			}
			if (!itemType) itemType = 'program';
			log.debug('Player.wmpOpenStChg','Media changing: ' + p.currentMedia.getItemInfo('entry-type'));
			if (me.onVideoChange) {
				me.onVideoChange(p.currentMedia.name,itemType,p.currentMedia,p.URL);
			}
			if (muted) p.settings.mute = true;
		}
		oldOpenState = newState;
		/*
		if (newState==7 && !me.finished && me.started) { // Playlist changing = playlist ended - no, forget it!
			log.debug('Player.wmpOpenStChg','fire event onVideoEnd');
			me.finished = true;
			if (me.onVideoEnd) {
				me.onVideoEnd();
			}
		}
		*/
	}

	function wmpClick(button, shiftState, fX, fY) {
		//log.debug('Player.wmpPlayStChg','playState: ' + newState);
		if (me.onPlayerClick && button ==1) {
			me.onPlayerClick(button, shiftState, fX, fY);
		}
	}

	function wmpPlayStChg(newState) {
		if (location.href.indexOf('debugStates=true')>0) log.debug('Player.wmpPlayStChg', newState + ': ' + playStates[newState]);
		if (me.onPlayStateChange) {
			me.onPlayStateChange(newState,oldPlayState);
		}
		if (newState==3 && startPos && startPos > 0 && (progId == me.getActualProgId())) {
			me.gotoPos(startPos);
			startPos = null;
		}
		//log.debug('Player.wmpPlayStChg','playState: ' + newState + ' !me.finished: ' + !me.finished + ' me.started: ' + me.started);
		if ((newState==10 || newState==0) && !me.finished && me.started) {
			log.debug('Player.wmpPlayStChg','fire event onVideoEnd');
			me.finished = true;
			if (me.onVideoEnd) {
				me.onVideoEnd();
			}
		}
		
		if (muted) p.settings.mute = true;
		
		oldPlayState = newState;
	}

	function wmpStatusChange() {
		//log.debug('Player.wmpStatusChange','Status:  ' + p.status);
		if (me.onStatusChange && p.playState!=6) {
			me.onStatusChange(me.MSG_WMP_STATUS,p.status,p.playState);
		}
	}

	function wmpError() {
		var errorStr = '';
		var lastErrorCode = 0;
		for (var i=0; i < p.error.errorCount; i++) {
			var errItem = p.error.item(i);
			errorStr += errItem.errorDescription + ' / ';
			lastErrorCode = errItem.errorCode;
			log.error('Player.p (' + p.id + ') ',errItem.errorCode + ': ' + errItem.errorDescription);
		}
		errorStr = errorStr.substring(0,errorStr.length-4);
		p.error.clearErrorQueue();
		if (reportWmpErrors && me.onStatusChange) {
			me.onStatusChange(me.MSG_WMP_ERROR,errorStr,lastErrorCode);
		}
	}

	function wmpBuffering() {
		
	}

	// Live startup
	function tryLive() {
		log.debug('Player.tryLive','Attempt ' + retryCount);
		try {
			var max = 6;
			//reportWmpErrors = false;
			if (retryCount<max) { 
				if (retryCount==0) {
					p.URL = metaUrl;
					p.controls.play();
				} else {
					p.controls.play();
				}
				retryCount++;
				evaluateLiveAttempt();
			} else if (retryCount == max){
				reportWmpErrors = true;
				retryCount++;
				evaluateLiveAttempt();
			}
		} catch(e) {
			log.debug('CorePlayer.tryLive','Player element probably missing.');
			if (me.onCeased) {
				me.onCeased();				
			}
		}
	}

	function evaluateLiveAttempt() { //
		// Call when media url is opened.
		// This function checks if the stream really starts,
		// or if the broadcast is not started yet
		try {
			var hasError=false;
			var readyState;
			var errorCode = 0;
		
			liveEvaluateCount++;
			if (me.onStatusChange) {
				me.onStatusChange(me.MSG_CHECKS_IF_STARTED);
			}
			readyState = p.openState;
			hasError = (p.error.errorCount > 0);
			if (hasError) errorCode = p.error.item(0).errorCode + '';
		} catch (e) {
			log.debug('CorePlayer.evaluateLiveAttempt1','Player element probably missing.');
			return;
		}
		if (liveEvaluateCount > 20 && waitingForUser <=0) { // nothing seems to be happening, giving up for a while
			try {
				liveEvaluateCount = 0;
				retryLive();
				log.debug('Player.evaluateLive','Nothing seems to be happening, giving up for a while');
				if (me.onStatusChange) {
					me.onStatusChange(me.MSG_TRIES_AGAIN);
				}
			} catch (e) {
				log.error('CorePlayer.evaluateLiveAttempt2',e);
			}
		}
		else { // not given up yet
			//log.debug('Player.evaluateLive','openState: ' + readyState);
			if (readyState == 13) {// Media open
				try {
					// a stream is open
					// check for correct progId
					log.debug('Player.evaluateLive','Video opening');
					var streamProgId = parseFloat(p.currentMedia.getItemInfo("author"));
					if (!streamProgId) streamProgId = 0;
					if (!hostProgId) hostProgId = progId;
					if (streamProgId > 0 && !(progId == streamProgId || hostProgId == streamProgId)) { // progId mismatch
						log.debug('Player.evaluateLive','progId mismatch: ' + progId + '/' + hostProgId + '/' + streamProgId);
						if (me.onStatusChange) {
							me.onStatusChange(me.MSG_WRONG_PROGRAM);
						}
						// stop the stream and try again in a while
						me.stop();
						retryLive();		
					} else { // correct progId or no streamProgId
						log.debug('Player.evaluateLive','Correct progId or no streamProgId: ' + progId + '/' + hostProgId + '/' + streamProgId);
						hUpdaterTimer = window.setInterval(wmpStatusUpdater,posUpdateInterval);
						if (me.onBufferingStart) me.onBufferingStart();
						reportWmpErrors = true;
					}
				} catch (e) {
					log.error('CorePlayer.evaluateLiveAttempt3',e);
				}
			}	else {
				try {
					if (hasError && (errorCode == '-2147014835' || errorCode == '-1072885299') && p.controls.currentPosition == 0)	{
						// probably not started, try again
						log.debug('Player.evaluateLive','Error in player, show probably not started');
						p.error.clearErrorQueue();
						retryLive();
						if (me.onStatusChange) {
							me.onStatusChange(me.MSG_TRIES_AGAIN);
						}
					}
					else { // something is happening - just wait a bit more
						hLiveAttTimer = window.setTimeout(evaluateLiveAttempt,1000);
					}
				} catch (e) {
					log.error('CorePlayer.evaluateLiveAttempt4',e);
				}
			}
		}
	}

	function retryLive() {
		var delay = 5000;
		hLiveRetryTimer = window.setTimeout(tryLive,delay);
	}

	function resetTimers() {
		if (hUpdaterTimer) {
			window.clearInterval(hUpdaterTimer);
		}
		if (hLiveAttTimer) {
			window.clearInterval(hLiveAttTimer);
		}
		if (hLiveRetryTimer) {
			window.clearInterval(hLiveRetryTimer);
		}
	}

	// General playback methods
	//var xyz=0;

	function wmpStatusUpdater() {
		//var xz = new Date();
		//debug(xyz + ' Start: ' +xz.getSeconds() + ':' + xz.getMilliseconds());
		if (!p) {
			if (me.onCeased) 
				me.onCeased();
			return;
		}
		if (p.openState == 13 && p.playState != 10) { // media open & no error
			if (me.onPosChange) {
				me.onPosChange(p.controls.currentPosition,p.currentMedia.duration,p.controls.currentPositionString,p.currentMedia.durationString,p.network.bufferingProgress); // double-phew
			}
			if (!p.fullScreen)
				p.uiMode = 'none';
			if (p.playState == 6 && me.onStatusChange && p.network.bufferingProgress < 100) {
				me.onStatusChange(me.MSG_WMP_BUFFERING,p.network.bufferingProgress);
			}
			if (isBufferingJustFinished()) {
				log.debug('Player.statusUpdater','First buffering finished');
				me.started = true;
				if (startAsPaused) {
					p.controls.pause();
				}
				if (me.onVideoStart) { // first time buffering when starting up
					me.onVideoStart();
				}
			}
			// kvalitetsmåling
		}
		
		if (muted) p.settings.mute = true;
				
		if (isPacketsBufferedYet() && me.onPacketsReceived) {
			me.onPacketsReceived();
			//log.debug('WmvGeckoCorePlayer.wmpStatusUpdater','Fired event for packets arrived.');
		}
		//var yz = new Date();
		//debug(xyz + ' End: ' +yz.getSeconds() + ':' + yz.getMilliseconds());
		//xyz++;
	}
	
	function isBufferingJustFinished() {
	  if (p.controls.currentPosition == 0) {
			if (bufferState == 0 && p.network.sourceProtocol != 'http' && p.network.bufferingProgress == 100) {
	    		bufferState = 1;
				return true;
			} else {
				return false;
			}
	  } else {
	   	if (bufferState == 0) {
	   		bufferState = 1;
	   		return true;
	   	} else {
	    	return false;
	   	}
	  }
	}

	function isPacketsBufferedYet() {
		if (!packetsReceived && p.network.receivedPackets >0) {
			packetsReceived = true;
			return true;
		} else
			return false;
	}


	function checkVideoFinished() {
		// 'media ended' play state change is not done correctly for live streams being stopped.
		// Instead, finish can be detected when buffering occurs with no progress (but the same state might occur for other reasons, too)
		
		/*
		window.status = 'playState: ' + p.playState + ' lastErrorCode: ' + lastErrorCode + ' finished: ' + this.finished + ' started: ' + this.started;
		if (live && (p.playState == 6 || p.playState == 10) && lastErrorCode == 0 && !this.finished && this.started) {
			var progress = p.network.bufferingProgress;
			if (p.playState == 10 || (progress < 100 && progress == lastProgress)) {
				noProgressCounter++;
				log.debug('Player.isVideoFinished','no buffering progress, counting: ' + noProgressCounter);
			}
			if ((noProgressCounter * posUpdateInterval) / 1000 >  10) { // seconds
				// assume media is ended
				log.debug('Player.isVideoFinished','Video finished, probably');
				this.finished = true;
				if (this.onVideoEnd) {
					this.onVideoEnd();
				}
			}
			
			
		}*/
	}

	// PUBLIC CONSTANTS
/**
 * Indicates a "status message" type
 * @type int
 */
	this.MSG_WMP_STATUS = 0;
/**
 * Indicates a buffering progress message
 * @type int
 */
	this.MSG_WMP_BUFFERING = 1;
/**
 * Indicates a error message from Windows Media Player
 * @type int
 */
	this.MSG_WMP_ERROR = 2;
/**
 * Status message: The media/stream is being opened
 * @type int
 */
	this.MSG_OPENING = 101;
/**
 * Status message: Got no connection when trying to open media
 * @type int
 */
	this.MSG_NO_CONNECTION = 102;
/**
 * Status message: Opened wrong stream (based on progId)
 * @type int
 */
	this.MSG_WRONG_PROGRAM = 103;
/**
 * Status message: Checks if live video stream has started
 * @type int
 */
	this.MSG_CHECKS_IF_STARTED = 104;
/**
 * Status message: Trying to open the live stream again
 * @type int
 */
	this.MSG_TRIES_AGAIN = 105;
/**
 * Status message: To be extracted from ASX
 * @type int
 */
	this.MSG_PROGRAM_ERROR = 106;


	// PUBLIC PROPERTIES

/**
 * ID of the currently playing program
 * @type int
 */
	this.progId = progId;

/**
 * Set to true if video playback is really started (buffering completed)
 * @type boolean
 */
	this.started = false; // i betydningen bufring fullført, videobilde vises

/**
 * Set to true if video playback is finished (end of video reached)
 * @type boolean
 */
	this.finished = false;

	// PUBLIC METHODS
/**
 * Starts video playback from the given url
 * @param {string} url The url pointing to the media file, to be used by Windows Media Player.
 * @param {int} pProgId the video program's id, used for identifying current stream, or to get correct stream in live programs
 * @param {int} pHostProgId optional, as props.progId, but used for sub programs having a real host program
 * @param {boolean} pLive indicates that stream is live if included, and set to true
 * @param {int} startPos when starting playback, jumps playing position to the number of seconds from the start. startPos is optional.
 */
	this.start = function(url,pProgId,pHostProgId,pLive,pTimeBegin,pStartPos,paused) {	
		if (!p) {
			if (me.onCeased) 
				me.onCeased();
			return;
		}
		try {
			resetTimers();
			me.finished = false; 
			me.started = false;
			bufferState = 0;
			packetsReceived = false;
			oldPlayState = -1;
			oldOpenState = -1;
			lastProgress = 100;
			noProgressCounter = 0;
			lastErrorCode = 0;
			retryCount = 0;
			waitingForUser = 0;

			metaUrl = url;
			progId = pProgId;
			hostProgId = pHostProgId;
			live = pLive;
			timeBegin = pTimeBegin;
			startPos = pStartPos;
			if (live==null) {
				live = false;
			}
			if (me.onStatusChange) {
				me.onStatusChange(me.MSG_OPENING);
			}
			if (live) {
				tryLive();
			} else {
				hUpdaterTimer = window.setInterval(wmpStatusUpdater,posUpdateInterval);
				p.URL = metaUrl;
  			//p.closedCaption.SAMIFileName = "http://bgo3521.tv2asa.no/webtv/dido/framework/samitest.smi";
				p.controls.play();
				if (paused && !live) {
					startAsPaused = true;
				} else {
					startAsPaused = false;
				}
				if(me.onBufferingStart) me.onBufferingStart();
		}
			log.debug('Player.start','Started');
    } catch (e) {log.error('CorePlayer.start',e);}
	};

/**
 * Resets the core player
 * @param {boolean} resetWMP if true, also stops video play back
 */
	this.reset = function(resetWMP) {
		/*
			p.detachEvent('OpenStateChange', wmpOpenStChg);
			p.detachEvent('PlayStateChange', wmpPlayStChg);
			p.detachEvent('StatusChange', wmpStatusChange);
			p.detachEvent('Error', wmpError);
			p.detachEvent('Buffering', wmpBuffering);		
		*/
		
		// stop timeouts/intervals
		resetTimers();

		if (resetWMP && p) {
			p.close();
		}
		
	}

/**
 * Starts playback after pause or stop (requires open media)
 */
	this.play = function() {
    	try {
    		if (p.openState == 13) p.controls.play(); // 13 = MediaOpen
    		me.finished = false;
    	} catch (e) {log.error('Player.play',e)}
	};

/**
 * Pauses playback (requires open media)
 */	
	this.pause = function() {
    	try {
	    	if (p.openState == 13 && !live) p.controls.pause();
    	} catch (e) {log.error('Player.pause',e)}
	};

/**
 * Stops playback (requires open media) or attempt for playback, and resets playback position to 0
 */	
	this.stop = function(close) {
    try {
			window.clearInterval(hLiveAttTimer);
    } catch(e) {}
		try {
			window.clearInterval(hLiveRetryTimer);
    } catch(e) {}
   	try {
    	p.controls.stop();
    	if (close) p.close();
    } catch(e) {}
	};


/**
 * Toggles between playback or pause (or stop for live streams)
 * @return new state - true if paused, false if playing
 * @type boolean
 */	
	this.togglePlay = function() {
    	try {
    		log.debug('Player.togglePlay',p.openState + '/' + p.playState);
    		if (p.openState == 13) { // 13 = MediaOpen
		      	if (live) {
				    if (p.playState == 3) { // 3 = Playing
				    	p.controls.stop();
				      	return true;
				    } else if (p.playState==1) { // 1 = Stopped, 2 = Paused
				        p.controls.play();
				        return false;
				    }
		      	} else {
				    if (p.playState == 3) { // 3 = Playing
				    	p.controls.pause();
				      	return true;
				    } else if (p.playState==2 || p.playState==1) { // 1 = Stopped, 2 = Paused
				        p.controls.play();
				        return false;
				    }
			  	}
		    } else if (p.openState == 6) { // 6 = PlaylistOpenNoMedia, which means finished
		        p.controls.stop();
		        p.controls.play();
		    } else {
					return false;
				}
    	} catch (e) {log.error('Player.playToggle',e); return false}
	};

/**
 * Makes video playback go fullscreen
 */		
	this.gotoFullscreen = function() {
    	try {
	    	if (p.playState == 3) {
	    		//if (p.closedCaption.SAMIFileName.length>0)
	    		p.uiMode = 'full';
	    		p.fullScreen = true;
	    	}
    	} catch (e) {log.error('gotoFullscreen',e)}
	};

/**
 * Checks if video is playing back in fullscreen
 * @return true if fullscreen, otherwise false
 * @type boolean
 */	
	this.isFullscreen = function() {
		return p.fullScreen;
	}

/**
 * Gets the current bitrate of the video playback
 * @return bitrate, in kbit/s.
 * @type int
 */	
	this.getBitrate = function() {
    try {
			return p.network.bitRate;
    } catch (e) {log.error('CorePlayer.getBitrate',e)}
	};


/**
 * Increases the sound level of the video playback
 * @param {int} step the amount to increase. The volume has a range of 0 to 100.
 * @return new volume setting, between 0 to 100.
 * @type int
 */	
	this.volumeUp = function(step) {
    	try {
			if (!step) step = 10;
			me.unmute();
			var newVol = p.settings.volume + step;
			if (newVol > 100) newVol = 100;
			p.settings.volume = newVol;
			return newVol;
    	} catch (e) {log.error('volumeUp',e)}
	};

/**
 * Sets the sound level of the video playback
 * @param {int} level The new level, in the range 0 - 100.
 */	
	this.setVolume = function(level) {
    	try {
			p.settings.volume = level;
    	} catch (e) {log.error('setVolume',e)}
	};

/**
 * Gets the sound level of the video playback
 * @return The volume, in the range 0 - 100.
 * @type int
 */	
	this.getVolume = function() {
    	try {
			return p.settings.volume;
    	} catch (e) {log.error('getVolume',e)}
	};

/**
 * Decreases the sound level of the video playback
 * @param {int} step the amount to decrease. The volume has a range of 0 to 100.
 * @return new volume setting, between 0 to 100.
 * @type int
 */	
	this.volumeDown = function(step) {
    	try {
			if (!step) step = 10;
			me.unmute();
			var newVol = p.settings.volume - step;
			if (newVol < 0) newVol = 0;
			p.settings.volume = newVol;
			return newVol;
    	} catch (e) {log.error('volumeDown',e)}
	};

/**
 * Toggles the mute state of the video player. If sound is muted, it will be unmuted, otherwise it will be muted.
 * @return the new mute state: True if muted, otherwise false
 * @type boolean
 */		
	this.toggleMute = function() {
    	try {
			p.settings.mute = !p.settings.mute;
			muted = !muted;
			log.debug('Player.toggleMute',p.settings.mute);
			return p.settings.mute;
    	} catch (e) {log.error('toggleMute',e)}
	};

/**
 * Mutes the video player.
 * @return previous mute state
 * @type boolean
 *  */		
	this.mute = function() {
    try {
			var prevState = p.settings.mute;
			p.settings.mute = true;
			muted = true;
			return prevState;
    } catch (e) {log.error('mute',e)}
	};

/**
 * Unmutes the video player.
 */		
	this.unmute = function() {
    try {
			var prevState = p.settings.mute;
			p.settings.mute = false;
			muted = false;
			return prevState;
    } catch (e) {log.error('unmute',e)}
	};

/**
 * Returns the mute state of the video player.
 * @return true if muted, otherwise false
 * @type boolean
 */		
	this.isMuted = function() {
    	try {
			return p.settings.mute;
    	} catch (e) {log.error('ismuted',e)}
	};

/**
 * Returns the position of the video player.
 * @return position in seconds
 * @type float
 */		
	this.getPos = function() {
    try {
			return p.controls.currentPosition;
    } catch (e) {log.error('Coreplayer.getPos',e)}
	};

/**
 * Returns the length of the video.
 * @return length in seconds
 * @type float
 */		
	this.getDuration = function() {
    try {
			return p.currentMedia.duration;
    } catch (e) {log.error('Coreplayer.getDuration',e)}
	};

/**
 * Moves playback position to a given time (absolute)
 * @param {int} position the new position in seconds from the start
 */		
	this.gotoPos = function(position) {
    	try {
		    p.controls.currentPosition = position;
		    //this.play();
    	} catch (e) {
    		log.error('gotoPos',e);
    		 if(!me.started) 
    		 	p.close();
    	}
	};
	
/**
 * Moves playback position by interval (relative)
 * @param {int} delta the interval in seconds
 */		
	this.shiftPos = function(delta) {
  	try {
	    var newPos = p.controls.currentPosition + delta;
	    if (newPos + delta/2 < p.currentMedia.duration) {
		    p.controls.currentPosition = newPos;
	    }
  	} catch (e) {
  		log.error('shiftPos',e);
		 	if(!me.started) 
		 		p.close();
  	}
	};
	
	
/**
 * Returns the playback state of the video player.
 * @return true if video is playing, otherwise false
 * @type boolean
 */		
	this.isPlaying = function() {
		try {
		  return !(p.playState < 2 || p.playState == 8 || p.playState == 10);
		} catch (e) {
			log.error('CorePlayer.isPlaying',e);
			return false;
		}
	}

/**
 * Returns the stream mode for the video playing, as given in start
 * @return true if video is a live stream, otherwise false
 * @type boolean
 */		
	this.isLive = function() {
		return live;
	};
	
/**
 * Returns the actual progId for the current clip, as reported from WMP, not the one specified in the start method
 * @return current clip's progId
 * @type integer
 */		
	this.getActualProgId = function() {
		if (p.currentMedia)
			return p.currentMedia.getItemInfo('progId')
		else
			return false;		
	};
	

	// EVENTS
/**
 * [Callback hook]
 * Fires when status of the video player changes.
 * @param {int} type evaluates to one of the constants listed under
 * @param {string} message the message details. When type is MSG_WMP_STATUS, message contains a status text from Windows Media Player.
 * When type is MSG_WMP_BUFFERING, message contains the buffering progress given in percent.
 * When type is MSG_WMP_ERROR, message contains the error description from Windows Media Player.
 * Otherwise, message is null
 * @param {string} code only given when type is MSG_WMP_ERROR. Contains error code from Windows Media Player.
 * @type function
 */
	this.onStatusChange = null; // (type, message, code)

/**
 * [Callback hook]
 * Fires in a regular interval after video has started (first buffering complete), supplying the time position and duration of the video.
 * @param {int} position current position expressed as number of seconds after the start of the video
 * @param {int} duration the duration of the video file expressed in seconds
 * @param {string} posFormatted current position relative to the start of the video expressed in HH:mm:ss format
 * @param {string} posFormatted duration of the video file expressed in HH:mm:ss format
 * @type function
 */
	this.onPosChange = null; // (position, duration, posFormatted, durFormatted)

/**
 * [Callback hook]
 * Fires in a regular interval during video buffering
 * @param {int} progress the percentage of buffering completeness
 * @type function
 */
	this.onBufferProgress = null; // (position, duration, posFormatted, durFormatted)

/**
 * [Callback hook]
 * Fires when Windows Media Player changes it's play state
 * @param {int} newState the updated state as defined in the WMP SDK.
 * @param {int} oldState the old state before the change, as defined in the WMP SDK.
 * @type function
 */
	this.onPlayStateChange = null; // (newState, oldState)

/**
 * [Callback hook]
 * Fires when Windows Media Player changes it's open state
 * @param {int} newState the updated state as defined in the WMP SDK.
 * @param {int} oldState the old state before the change, as defined in the WMP SDK.
 * @type function
 */
	this.onOpenStateChange = null; // (newState, oldState)

/**
 * [Callback hook]
 * @ignore
 * @type function
 */
	this.onQualityChange = null; // (newQuality)

/**
 * [Callback hook]
 * Fires when a mouse button is clicked with the cursor within the WMP object.
 * For details, See the WMP SDK documentation for the click event
 * @param {int} button which mouse button being pressed
 * @param {int} shiftState the state of the shift button
 * @param {int} fX the horizontal cursor position
 * @param {int} fY the vertical cursor position
 * @type function
 */
	this.onPlayerClick = null;

/**
 * [Callback hook]
 * Fires when video starts buffering for the first time
 * @type function
 */
	this.onBufferingStart = null;

/**
 * [Callback hook]
 * Fires when video starts playing for the first time, when the first buffering is finished
 * @type function
 */
	this.onVideoStart = null;

/**
 * [Callback hook]
 * Fires once when the first stream packets are received successfully
 * @type function
 */
	this.onPacketsReceived = null;


/**
 * [Callback hook]
 * Fires when video playback reaches the end. This occurs when an on demand file is finished, or a live stream is being shut down by the publisher.
 * @type function
 */
	this.onVideoEnd = null;

/**
 * [Callback hook]
 * Fires when a new item in the playlist starts playing.
 * @type function
 */
	this.onVideoChange = null;

/**
 * [Callback hook]
 * Fires when video object is removed from the DOM (HTML)
 * @type function
 */
	this.onCeased = null;

}
