/*

(C)2008-2009 B-Lex IT

TODO:
- better event handling
- nicer initializing of variables

events:
- onloadmedia()
- ontimechange(position)
- oncuepointchange()
- onstatechange()
- onmutechange()
- onmetadata()             we got some metadata or videowidth, videoheight or duration has changed

*/

if (typeof blexplaybackslaves == 'undefined')
  blexplaybackslaves = [];

function toddMP_Player(options)
{
  this.name = 'toddMP_Player';

  // internal lists & states
  this.effects  = {};   // fade ins/outs
  this.handlers = {};   // references for mimetypes to loaded playback slaves
  this.callbacks = {};
  this.slave    = null; // current playback slave
  this.controls = options.controls;

  this.baseurl  = options.baseurl;

  this.supportrgba     = capableofRGBA(); // FIXME: move to cuepointhandler for NRO
  this.prevlayeractive = false;
  this.currentcuepoint = null;

  this.dom = {}; // references to DOM elements
  this.dom.container  = options.playercontainer;

  // events
  this.onloadmedia    = options.onloadmedia;
  this.oncuepoint     = options.oncuepoint;
  if (options.onendofmedia)
    this.callbacks.onendofmedia   = options.onendofmedia;

  this.media       = null;

  this.initializeslaves();

  this.playlist    = new blexplaylist();
  this.playlist.playerinstance = this; // fixme

  if (typeof options.playlist != 'undefined')
    this.playlist.setlist(options.playlist);

  // audio
  this.volume        = (typeof options.volume        != 'undefined') ? options.volume        : 50;
  this.muted         = false;

  // visual
  this.width         = options.width;
  this.height        = options.height;
  this.visible       = (typeof options.visible       != 'undefined') ? options.visible       : true;

  this.autoload      = (typeof options.autoload      != 'undefined') ? options.autoload      : true;
  this.autoplay      = (typeof options.autoplay      != 'undefined') ? options.autoplay      : true;
  this.autonext      = (typeof options.autonext      != 'undefined') ? options.autonext      : true;

  this.showsubtitles = true;

  if (this.controls)
  {
    // the controls needs to have a reference to this player before being able to initialize
    this.controls.player = this;
    this.controls.init();
  }

  this.resetmediainfo();
}

toddMP_Player.prototype.resetmediainfo = function toddMP_Player_resetmediainfo()
{
  this.videoWidth  = 0;
  this.videoHeight = 0;
  this.bytesLoaded = 0;
  this.bytesTotal  = 0;

  this.position    = 0;
  this.duration    = 0;

  this.currentcuepoint = null;

  this.isplaying = false;
  this.ispaused  = false;

  if (this.controls)
    this.controls.updateposition(this.position);
}

// container can either be the elementid or an reference
toddMP_Player.prototype.InsertInto = function toddMP_Player_InsertInto(container)
{
  //this.dom.container = container;

  var visualcontainer = this.createElement('div','playerarea');
  this.dom.playbackcontainer = visualcontainer;

  container.appendChild(visualcontainer);

/*  if (typeof this.width == 'undefined')
    this.width = this.dom.container.clientWidth;

  if (typeof this.height == 'undefined')
    this.height = this.dom.container.clientHeight;
*/
  if (this.visible)
  {
    if (this.width || this.height)
      this.SetSize(this.width, this.height);
  }
}

toddMP_Player.prototype.DeInit = function toddMP_Player_DeInit()
{
  // ask the controls to clean up
  if (this.controls && this.controls.destroy)
    this.controls.destroy();

  //this.slave.onload = null; // sorry, it's too late, you're not needed anymore

  // ask all slaves to deinitialize
  var slaveid;
  for(slaveid in this.handlers) // FIXME: might give problems on sites which use Prototype.JS
  //for(var slaveid=0; slaveid<this.handlers.length; slaveid++)
  {
    //toddConsoleLog('Asking slave '+this.handlers[slaveid].name+' to DeInit()');
    this.handlers[slaveid].DeInit();
  }

  //this.slave = null;

  this.dom = {};
}

toddMP_Player.prototype.createElement = function toddMP_Player_createElement(tag, classname, callfunc)
{
  var elem = document.createElement(tag);
  elem.className = classname;
  if (callfunc)
    elem.onclick = callfunc;

  return elem;
}

toddMP_Player.prototype.showControls = function toddMP_Player_showControls(visible)
{
  this.controls.setVisibility(visible);
  this.ApplyCurrentSize();
}

toddMP_Player.prototype.SetSize = function toddMP_Player_SetSize(width, height)
{
  this.width = width;
  this.height = height;
  this.ApplyCurrentSize();
}

toddMP_Player.prototype.ApplyCurrentSize = function toddMP_Player_ApplyCurrentSize()
{
  // don't apply sizes if we don't have a container yet
  if(!this.dom.playbackcontainer)
    return;

  if (this.controls && this.controls.visible)
  {
    var space = this.controls.getspacerequirement();
    this.setdisplayareasize( this.width  - space.width
                           , this.height - space.height);
    this.controls.setsize(this.width, this.height);
  }
  else
  {
    this.setdisplayareasize(this.width, this.height);
  }
}

// Only change the size of the display area (which shows the pictures & video).
toddMP_Player.prototype.setdisplayareasize = function toddMP_Player_setdisplayareasize(width, height)
{
  if (!this.visible)
    return;

  this.dom.playbackcontainer.style.width  = width+'px';
  this.dom.playbackcontainer.style.height = height+'px';

  if (this.slave)
    this.slave.SetSize(width, height);
/*
  if (!toddUserAgentIsIE)
    return;

//  this.cuepointhandler_setsize(width, height);
*/
}

toddMP_Player.prototype.updatetime = function toddMP_Player_updatetime()
{
  if (this.ontimechange) // .event.
    this.ontimechange(this.position);

  this.updatecuepoint(true);
}

toddMP_Player.prototype.updatecuepointslist = function toddMP_Player_updatecuepointslist(cuepoints)
{
  this.media.cuepoints = cuepoints;
  this.currentcuepoint = -1;        // invalidate previous cuepoint to force update
//  this.updatecuepoint(false);

  // send notification
  if (this.oncuepointsupdate)
    this.oncuepointsupdate(cuepoints);
}

toddMP_Player.prototype.findcuepointfortime = function toddMP_Player_findcuepointfortime(timecode)
{
  var cuepoints = this.media.cuepoints;
  for (var i = cuepoints.length-1; i>=0; --i) //cuepoints are sorted in ascending time order
    if(cuepoints[i].starttime <= timecode)
      return i;
  return -1;
}

toddMP_Player.prototype.updatecuepoint = function toddMP_Player_updatecuepoint(withfade)
{
  var position = this.position;

  if (  this.media == null                    // no media loaded
     || this.media.cuepoints == undefined     // no quepoints array
     || this.media.cuepoints.length == 0)     // no quepoints set
    var newcuepoint = -1; // the media doesn't have any quepoint (anymore)
  else
    var newcuepoint = this.findcuepointfortime(position);

  // quit if there is no cuepoint change
  if (newcuepoint == this.currentcuepoint)
    return;
  this.currentcuepoint = newcuepoint;

  if (this.oncuepoint) // .event.
    if (newcuepoint!=-1)
      this.oncuepoint(this.media.cuepoints[newcuepoint].data);
    else
      this.oncuepoint(null);
}

///Goto a specific position on the playlist by rowkey
toddMP_Player.prototype.GotoPlaylistItem = function toddMP_Player_GotoPlaylistItem(mediaid)
{
  var itempos = this.playlist.getitemposition(mediaid);
  if(!itempos)
    return; //not found
  this.GotoPlaylistPos(itempos);
}

///Goto a specific position on the playlist. itempos: item to play, 1-based
toddMP_Player.prototype.GotoPlaylistPos = function toddMP_Player_GotoPlaylistPos(itempos)
{
  this.playlist.playitem(itempos);
}

toddMP_Player.prototype.findSlaveInfoFor = function toddMP_Player_findslaveinfofor(mimetype)
{
  //toddConsoleLog('checking mimetype '+mimetype+' for '+blexplaybackslaves.length+' slaves');
  for(var slaveid = 0; slaveid < blexplaybackslaves.length; ++slaveid)
  {
    slave = blexplaybackslaves[slaveid];
    //toddConsoleLog('slave '+slaveid+' accepting mimetypes '+slave.mimetypes.join(','));

    if (toddSearchElement(slave.mimetypes, mimetype) != -1)
      return slave;
  }

  return null;
}

toddMP_Player.prototype.getSlave = function toddMP_Player_getSlave(mimetype)
{
  if (typeof this.handlers[mimetype] == 'undefined')
  {
    // find the slave that can handle the given mimetype
    var slaveinfo = toddMP_Player.prototype.findSlaveInfoFor(mimetype);

    // if we didn't get any result, than no slave is able to play files of this mimetype
    if (!slaveinfo)
      return null;

    var slave = new slaveinfo.classref();

    // make all mimetypes this slave can handle reference to our new slave instance
    for(var tel=0; tel<slaveinfo.mimetypes.length; tel++)
      this.handlers[slaveinfo.mimetypes[tel]] = slave;

    return slave;
  }
  else
    return this.handlers[mimetype];
}

// initialize all slaves, call this when the DOM is available (interactive state, use onload for IE)
toddMP_Player.prototype.initializeslaves = function toddMP_Player_initializeslaves()
{
  for(var slaveid = 0; slaveid < blexplaybackslaves.length; ++slaveid)
  {
    blexplaybackslaves[slaveid].initialize(this.baseurl);
  }
}

toddMP_Player.prototype.loadnewmedia = function toddMP_Player_loadnewmedia(media)
{
  if (media == null)
  {
    toddConsoleLog('new media data is empty.');
    return;
  }

  if (typeof media.rowkey == 'undefined')
  {
    toddConsoleLog('rowkey is not defined.');
    return;
  }

  // ignore if the same media was requested
  // FIXME: maybe restart the music/video (without reloading) if it was requested again?
  if (this.media != null && media.rowkey == this.media.rowkey)
    return;


  toddConsoleLog('New media requested:'+"\n"
                +'rowkey: '   + media.rowkey+"\n"
                +'URL: '      + media.url+"\n"
                +'mimetype: ' + media.mimetype);


  //Stop the currently playing media (make sure the user knows we heard him)

  // cancel all our fades
  // FIXME: should be moved to cuepoints handler, which should hook onloadmedia
  for(effectname in this.effects)
    this.effects[effectname].cancel();

//  this.prevmedia = this.media;

  // stop & unload previous loaded/playing media
  if (this.media)
    this.slave.flush(this.media.rowkey);

  var newslave = this.getSlave(media.mimetype);
  if (!newslave)
  {
    toddConsoleLog("No playback slave found for mimetype '"+media.mimetype+"'");
    return;
  }
  else
  {
    toddConsoleLog("Using playback slave '", newslave.name, "'");
  }

  //Should we unload the old slave?
  if(this.slave && newslave.name != this.slave.name)
  {
    this.slave.onload = null; // sorry, it's too late, you're not needed anymore
    this.slave.deactivate();
    this.slave = null;
  }

  // if another item was selected, call the 'onloadmedia'-event
  if (this.onloadmedia)
    this.onloadmedia(media);

  var _playerinstance = this;

  // will the slave be able to play anything?
  if (!newslave.isabletoplay())
  {
    toddConsoleLog('Unable to play.');
    return;
  }

  // if the slave isn't ready delay until it is
  if (!newslave.isready())
  {
    toddConsoleLog('slave is not ready. new media will be loaded upon slave.onload().');

    // delay loading until slave is ready for playback
    var _media=media;
    newslave.onload = function()
                   {
                     toddConsoleLog('slave.onload(); called.');
                     _playerinstance.loadnewmedia(_media);
                   }
    return;
  }
  else
  {
    // clear any delayed media (in case we changed media in the mean time)
    newslave.onload = function() { };
  }

  // TODO: prevent slaves which should have been deactivated from doing any
  //       more callbacks.

  this.slave = newslave;
  this.media = media;
  this.resetmediainfo();
  this.slave.playerinstance = this;
  this.slave.activate();

  this.ApplyCurrentSize(); //new slave needs to know the set dimensions

  toddConsoleLog('Asking slave to load media.');

  this.slave.loadmedia(media);
}

toddMP_Player.prototype.onendofmedia = function toddMP_Player_onendofmedia()
{
  if (this.callbacks.onendofmedia)
    this.callbacks.onendofmedia();

  if (this.autonext)
    this.playlist.next();
  else
  {
    //this.slave.setPosition(0);
    this.slave.stop();
  }
}

/*

   Playback

*/

toddMP_Player.prototype.play = function toddMP_Player_play()
{
  if (this.slave == null)
    return;

  this.slave.play();

  this.isplaying = true;

  if (this.onstatechange) // .event.
    this.onstatechange();
}

toddMP_Player.prototype.stop = function toddMP_Player_stop()
{
  if (this.slave == null)
    return;

  this.slave.stop();

  this.isplaying = false; // assume we are stopping, until we hear otherwise from our slave

  if (this.onstatechange) // .event.
    this.onstatechange();
}

toddMP_Player.prototype.setPosition = function toddMP_Player_setPosition(milliseconds)
{
  this.slave.setPosition(milliseconds);
  this.position = milliseconds; // for now assume the seeking was succesfull
  this.updatetime();
}

toddMP_Player.prototype.setplaystate = function toddMP_Player_setplaystate(play)
{
  if (this.slave == null || this.media == null)
    return;

  this.slave.setplaystate(play);

  this.isplaying =  play;
  this.ispaused  = !play;

  if (this.onstatechange) // .event.
    this.onstatechange();
}

toddMP_Player.prototype.toggleSubtitles = function toddMP_Player_toggleSubtitles()
{
  this.setSubtitles(!this.showsubtitles);
}

toddMP_Player.prototype.togglepause = function toddMP_Player_togglepause()
{
  //this.pause(!this.ispaused);
  this.setplaystate(!this.isplaying);
}

toddMP_Player.prototype.setAutoPlay = function toddMP_Player_setAutoPlay(autoplay)
{
toddConsoleLog('setAutoPlay');

  // autoplay settings is passed to the slave upon requesting a file
  // this means changing the autoplay will have effect on the next media to be requested
  this.autoplay = autoplay;

  if (this.slave)
    this.slave.setAutoPlay(autoplay);
}

toddMP_Player.prototype.setAutoNext = function toddMP_Player_setAutoNext(autonext)
{
  // we handle autonext ourselves
  this.autonext = autonext;
}

/*

   Audio related methods

*/

toddMP_Player.prototype.toggleMute = function toddMP_Player_toggleMute()
{
  this.setMute(!this.muted);
}

toddMP_Player.prototype.setMute = function toddMP_Player_setMute(muted)
{
  if (muted == this.muted)
    return;

  if (this.onmutechange) // .event.
    this.onmutechange(muted);

  this.muted = muted;

  this.applymutestate();
}

// internal.
// used by setMute() but also when switching playback slave
toddMP_Player.prototype.applymutestate = function toddMP_Player_applymutestate()
{
  if (this.slave == null)
    return;

  this.slave.setMute(this.muted);
}

toddMP_Player.prototype.getVolume = function toddMP_Player_getVolume()
{
  return this.volume;
}

toddMP_Player.prototype.setVolume = function toddMP_Player_setVolume(volume)
{
  if (this.slave == null)
    return;

  this.volume = volume;
  this.slave.setVolume(this.volume); // FIXME: remember volume
}

/*

   Video related methods

*/

toddMP_Player.prototype.setVideoScaleMode = function toddMP_Player_setVideoScaleMode(scalemode)
{
  if (this.slave.setVideoScaleMode)
    this.slave.setVideoScaleMode(scalemode);
}

toddMP_Player.prototype.setVideoAlign = function toddMP_Player_setVideoAlign(alignmode)
{
  if (this.slave.setVideoAlign)
    this.slave.setVideoAlign(alignmode);
}


if (typeof toddCurrentLoads != 'undefined')
  toddCurrentLoads["blexmediaplayer/player.js"]=true;