/*

(C)2008-2009 B-Lex IT

Flash based limitations:
  - netStream.seek() will resume playback when seekposition is in cache
    (even though playback is paused or stopped)
  - seeking is limited to video key frames
    (this also means seeking near the end of the movie can cause the movie to end directly)

FAVideo bugs:
  - Flash never returns video width/height and duration if the FLV doesn't contain a metatag
  - The FAVideo SWF sometimes fails to report the new duration of an FLV movie
  - when a video ends (due to end of video reached) OnStateChange is not called?

FAVideo oddities:
  - ready will not be fired in case a new video was loaded while playback was paused
    (Since the FAVideo SWF checks for NetStream.Play.Start&&!autoplay
     to fire ready (and then directly pause the video)
  - .stop() will fire OnStateChange 3x (with state seeking, paused and stopped)
  - (FIXED) If the duration of the video is unknown, the play() will fail due
    to it trying to pass the totalTime of the video as playback duration

*/

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

blexplaybackslaves.push({ name:      'blex FLV slave'
                        , mimetypes: ['video/x-flv']
                        , classref:  blexFLVslave
                        , initialize: function() {}
                        }
                       );



// these flags are defined in Tollium, but we also need to define
// them here so the player can be used outside of Tollium.

var toddUserAgent = navigator.userAgent.toLowerCase();
var toddUserAgentIsSafari = (toddUserAgent.indexOf('safari') != -1) && (toddUserAgent.indexOf('chrome') == -1);

var toddFixFlashCrash = (toddUserAgentIsSafari && (navigator.platform == 'Win32' || navigator.platform == 'Win64'));



function blexFLVslave()
{
  this.width = 320;
  this.height = 240;
}
blexFLVslave.prototype.name = 'FAVideo';

blexFLVslave.prototype.initialize = function blexFLVslave_initialize()
{
}

blexFLVslave.prototype.isabletoplay = function blexFLVslave_isabletoplay()
{
  var favideo_loaded = (typeof window.FAVideo != 'undefined');

  //  if (typeof window.FAVideoManagerInstance == 'undefined')
  if (!favideo_loaded)
    toddConsoleLog('FAVideo is not loaded. Video playback will not be possible!');

  return favideo_loaded;
}

blexFLVslave.prototype.isready = function blexFLVslave_isready()
{
  return true; // FIXME
}

blexFLVslave.prototype.flush = function blexFLVslave_flush()
{
  // TODO
}

blexFLVslave.prototype.deactivate = function blexFLVslave_deactivate()
{
  this.DeInit();
}

blexFLVslave.prototype.activate = function blexFLVslave_activate()
{
  if (this.playernode)
    this.playernode.style.display = 'block';
}

blexFLVslave.prototype.SetSize = function toddMP_FLVSlave_SetSize(width, height)
{
  if(width == this.width && height==this.height)
    return;

  this.width = width;
  this.height = height;
  this.ApplyCurrentSize();
}

blexFLVslave.prototype.ApplyCurrentSize = function toddMP_FLVSlave_ApplyCurrentSize()
{
  if(this.player)
    this.player.setSize(this.width, this.height);
}

blexFLVslave.prototype.setVolume = function blexFLVslave_setVolume(volume)
{
  this.player.setVolume(volume);
}

blexFLVslave.prototype.setMute = function blexFLVslave_setMute(mute)
{
  //toddConsoleLog('[FAVideo] mutestate: %i (volume is %i)', mute, this.playerinstance.volume);

  if (mute)
    this.player.setVolume(0);
  else
    this.player.setVolume(this.playerinstance.volume); // FIXME: remember volume
}

// initialize an FAVideo instance
/* WARNING NOT NECESSARILY THE FIRST FUNCTION TO ACTUALLY RUN - SETSIZE CAN BE EXECUTED BEFORE INIT */
blexFLVslave.prototype.init = function blexFLVslave_init(settings)
{
  //FIXME WHat should initial sizes be ?

  this.callback = {};
  this.callback.onready       = settings.onready;
  this.callback.onendofmedia  = settings.onendofmedia;
  this.onmeta       = settings.onmeta;
  this.whileplaying = settings.whileplaying;
  this.whileloading = settings.whileloading;

  this.playernode   =  document.createElement('div');
  this.playernode.style.height = '100%';

  this.duration  = 0;
  this.isplaying = false;
  this.autoload  = settings.autoload;
  this.autoplay  = settings.autoplay;
  this.volume    = settings.volume
  this.player = new FAVideo( this.playernode
                           , settings.baseurl // path to FAVideo .swf/skin
                           , settings.url
                           , this.width
                           , this.height
                           , { autoLoad:       this.autoload
                             , autoPlay:       this.autoplay
                             , skinVisible:    false
                             , videoScaleMode: 'maintainAspectRatio'
                             , videoAlign:     'center' /* noScale / exactFit */
                             , volume:         this.volume
                             });

  this.player.addEventListener("ready",          this,  this.extOnVideoReady);

  /*
  Depending on the 'state' either change or playheadUpdate is fired.
  To catch all changes in videowidth/videoheight/totalTime we need to listen to both.

  All events:
  - init
  - screenMode
  - progress
  - playheadUpdate      - fires during video playback
  - stateChange
  - change              - should only fire when state!='playing' (but can fire once after state has changed to 'paused'
                          and before the first occurance of an playheadUpdate
  - complete
  - ready
  - metaData
  - cuePoint
  - skinLoaded (Fired by FAVideo.swf, but not used by FAVideo.js
  */

  this.player.addEventListener("change",         this, this.extOnUpdate);
  this.player.addEventListener("playheadUpdate", this, this.extPlayheadUpdate);

  this.player.addEventListener("stateChange",    this, this.extOnStateChange);

  this.player.addEventListener("screenMode",     this, this.extScreenModeChange);

  this.player.addEventListener("metaData",       this, this.extLoadedMetaData);

  this.player.addEventListener("progress",       this, this.extProgress);               // load progress
  this.player.addEventListener("complete",       this, this.extComplete);               // complete == onendofmedia

  this.player.addEventListener("ready",          this, this.extOnVideoReady);

  this.mustforcepause = false;
}

blexFLVslave.prototype.DeInit = function blexFLVslave_DeInit()
{
  // push DeInit() back in the JS queue so it executes after handling of Flash callbacks
  // to prevent a crash in Safari 3 for Windows
  if (toddFixFlashCrash && !this.delayeddeinit)
  {
    var self = this;
    this.delayeddeinit = true;
    setTimeout( function() { self.DeInit(); }, 1 );
    return;
  }

  // exit in case we didn't even init (no playnode available)
  if (!this.playernode)
    return;

  // since hiding and showing later will reload the Flash in Firefox
  // and mess up all eventListeners we need to destroy the flash movie.
  this.playernode.style.display = 'none';

  // IE might continue playback even though we removed the Flash movie from the DOM
  // so destroy the Flash movie before removing it.
  this.playernode.innerHTML = '';

  this.playernode.parentNode.removeChild(this.playernode);

  delete this.playernode;

  // will cleanup and prevent the Flash-plugin from calling no longer existing instances
  if (this.player)
    this.player.destroy();

  this.delayeddeinit = false; // reset flag needed for fixing 'Safari 3 for Win32' Crash
}

// this is called to give updated info, when playstate is not 'playing'
// (bug in FAVideo: it can be called once after the playstate switched to 'paused' and before the first playheadUpdate)
blexFLVslave.prototype.extOnUpdate = function blexFLVslave_extOnUpdate(info)
{
  this.extCheckForChanges();

  //toddConsoleLog('[FAVideo] OnUpdate');
  this.extCheckForChanges();
}

blexFLVslave.prototype.extPlayheadUpdate = function blexFLVslave_extPlayheadUpdate(info)
{
  /*
  playheadUpdate
  state = playing
  playheadTime = 1.071
  totalTime = 244.898
  */

  /*
  FAVideo/Flash fires playheadUpdate after start, so during playback it'll return playheadTime>0
  When a video has stopped we don't get a statechange event, but we will get playheadTime=0
  */

  //toddConsoleLog('Position: '+this.position+' / Duration: '+this.duration);

  this.position = info.playheadTime * 1000;

  if (typeof info.totalTime == 'undefined')
    this.duration = 0;
  else
    this.duration = info.totalTime    * 1000;

  if (this.position == 0)
  {
    this.isplaying = false;

    if (this.onstatechange)
      this.onstatechange();

    return; // we only get position=0 when we aren't playing
  }

  if (this.whileplaying)
    this.whileplaying();

  this.extCheckForChanges();
}

blexFLVslave.prototype.onstatechange = function blexFLVslave_onstatechange()
{
  //toddConsoleLog('FLVslave internal isplaying '+this.isplaying);
  this.playerinstance.isplaying = this.isplaying;

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

// checks for changes in videosize/length
blexFLVslave.prototype.extCheckForChanges = function blexFLVslave_extCheckForChanges()
{
  if (typeof this.player.totalTime == 'undefined')
    var durationtest = 0;
  else
    var durationtest = this.player.totalTime * 1000;

  //if (this.duration != durationtest)
  //  alert('New duration is '+durationtest);

  // prevent resending for every change event by returning if none of the important variables have changed
  if (  (this.videowidth  == this.player.videowidth)
     && (this.videoheight == this.player.videoheight)
     && (this.duration    == durationtest))
    return;

//  console.log("%i = %i\n %i = %i\n %i %i",this.videowidth, this.player.videowidth, this.videoheight, this.player.videoheight, this.duration, durationtest);

//  console.log("Updating videosize / duration\nNew size: %i x %i (Old: %i x %i)\nDuration: %i (was %i)", this.player.videowidth, this.player.videoheight, this.videowidth, this.videoheight, this.durationtest, this.duration);

  this.videowidth  = this.player.videowidth;
  this.videoheight = this.player.videoheight;
  this.duration    = durationtest;

  if (this.onmeta)
    this.onmeta();

  /* due to a bug in FAVideo (aspect ratio is only calculated at the time the scaleMode is switched
     to maintainAspectRatio) we have to force maintainAspectRatio again for every new movie.

     Since doing the update right after playVideo often fails (videosize not known yet),
     we also have to do it here. However when it's done here, you'll see the video resizing
     during playback.
     */
  this.player.callMethod("update", { videoScaleMode: this.player.videoScaleMode });

//  this.app.QueueEvent(this,'videochange', this.windowroot.screenname + '.' + this.name, 'videochange '+metaasjson);
}

blexFLVslave.prototype.extProgress = function blexFLVslave_extProgress(info)
{
  /*
  progress
  bytesLoaded = ...
  bytesTotal  = ... (-1 is unknown)
  */

  this.bytesLoaded = info.bytesLoaded;
  this.bytesTotal  = info.bytesTotal;

  // FAVideo doesn't always call the ready event, so try to detect it ourself
  if (info.bytesLoaded == info.bytesTotal)
  {
    //toddConsoleLog('Video fully loaded. Forcing OnVideoReady.');
    this.onVideoReady();
  }

  if (this.whileloading)
    this.whileloading();
}

blexFLVslave.prototype.extOnVideoReady = function blexFLVslave_extOnVideoReady(info)
{
  // our own autoPlay, since FAVideo's autoplay is broken
  //toddConsoleLog('OnVideoReady recieved from FAVideo.');

  //toddConsoleLog('[FAVideo] event "ready" fired');

  this.onVideoReady();

//  if (this.callback.onready)
//    this.callback.onready();
}

blexFLVslave.prototype.onVideoReady = function blexFLVslave_onVideoReady()
{
  if (this.autoplay && !this.isplaying)
  {
    //toddConsoleLog('calling playVideo with URL %s and playduration %i', this.player.videoPath, this.player.totalTime);

    this.player.callMethod("playVideo", this.player.videoPath, this.player.totalTime);

    /* due to a bug in FAVideo (aspect ratio is only calculated at the time the scaleMode is switched
       to maintainAspectRatio) we have to force maintainAspectRatio again for every new movie.
       If we are lucky this update will be work directly after the play, in which case
       we won't see the video resizing while playing.*/
    this.player.callMethod("update", { videoScaleMode: this.player.videoScaleMode });
  }
}

blexFLVslave.prototype.extOnStateChange = function blexFLVslave_extOnStateChange(info)
{
  // state = loading/playing/buffering/seeking/paused/stopped/connectionError

  //toddConsoleLog('[FAVideo] OnStateChange - new state = ' + info.state);

  var state = info.state;

  var oldplaystate = this.isplaying;

  // for these states assume we aren't playing (anymore)
  if (state=='paused' || state=='stopped' || state=='connectionError')
    this.isplaying = false;

  if (state=='playing')
    this.isplaying = true;

  this.player.isplaying = this.isplaying;
  this.onstatechange();
}

blexFLVslave.prototype.extScreenModeChange = function blexFLVslave_extScreenModeChange(info)
{
//  alert( info.mode );
}

//**************************************************************
// FAVideo doesn't seem to correctly report the following events:

blexFLVslave.prototype.extLoadedMetaData = function blexFLVslave_extLoadedMetaData(info)
{
  //toddConsoleLog('[FAVideo] event "MetaData" fired');

// TODO: check if this works with an video which actually contains metadata
//  this.app.QueueEvent(this,'loadedmetadata', this.windowroot.screenname + '.' + this.name, 'loadedmetadata '+info.objectType);
}

blexFLVslave.prototype.extComplete = function blexFLVslave_extComplete(info)
{
  toddConsoleLog('[FAVideo] event "complete" fired');

  if (this.callback.onendofmedia)
    this.callback.onendofmedia();
}

//**************************************************************

blexFLVslave.prototype.onvideoinit = function blexFLVslave_onvideoinit()
{
}

blexFLVslave.prototype.loadmedia = function blexFAVslave_loadmedia(media)
{
  this.mediaid = media.rowkey;

  var _player = this.playerinstance;

  if (typeof this.playernode == 'undefined')
  {
    this.init({
        id:            this.mediaid //media.rowkey
      , url:           media.url
      , autoload:      _player.autoload
      , autoplay:      _player.autoplay

      , volume:        _player.muted ? 0 : _player.volume

      , baseurl:       _player.baseurl

      , initialWidth:  _player.dom.playbackcontainer.clientWidth
      , initialHeight: _player.dom.playbackcontainer.clientHeight

      , onready:       function()
                       {
//                           console.warn('Ready to start playback.');
                       }

      , onmeta:        function()
                       {
                         _player.videowidth  = this.videowidth;
                         _player.videoheight = this.videoheight;
                         _player.duration    = this.duration;

                         if (_player.onmetadata) // .event.
                           _player.onmetadata();
                        }
      , whileloading: function()
                      {
                        //  console.log('sound '+this.sID+': '+this.bytesLoaded+' of '+this.bytesTotal+' bytes loaded.');
                        if (_player.whileloading)
                          _player.whileloading(this.bytesLoaded, this.bytesTotal);
                      }
      , whileplaying: function()
                      {
                      // Notes:
                      // - FAVideo will return position 0 when video has ended
                        _player.position  = this.position;
                        _player.duration  = this.duration;

                        if (this.mustforcepause)
                          this.player.pause(true);

                        _player.updatetime();
                      }

      , onendofmedia:  function()
                       {
                         _player.onendofmedia();
                       }
    });

    _player.dom.playbackcontainer.appendChild(this.playernode);
  }
  else
  {
    this.player.setVolume(_player.volume);
    this.player.autoLoad = true;
    this.player.autoPlay = true;
    this.player.load(media.url); // load / play
  }
}

blexFLVslave.prototype.setplaystate = function blexFLVslave_setplaystate(play)
{
  if (play)
  {
    this.paused=false;
    this.player.play();
  }
  else
  {
    this.paused=true;
    this.player.pause(true);
  }
}

blexFLVslave.prototype.play = function blexFLVslave_play(itemID)
{
  this.player.pause(false);
}

blexFLVslave.prototype.stop = function blexFLVslave_stop()
{
  this.player.stop();
}

blexFLVslave.prototype.pause = function blexFLVslave_pause()
{
  if (this.player.state == 'playing')
  {
    this.paused=false;
    this.player.pause(true);
  }
  else if (this.player.state == 'paused')
  {
    this.paused=true;
    this.player.pause(false);
  }
}

blexFLVslave.prototype.setPosition = function blexFLVslave_setPosition(position)
{
  this.player.seek( position/1000 );
}

blexFLVslave.prototype.setVideoAlign = function blexFLVslave_setVideoAlign(alignment)
{
  this.player.setVideoAlign(alignment);
}

blexFLVslave.prototype.setVideoScaleMode = function blexFLVslave_setVideoScaleMode(scalemode)
{
  this.player.setVideoAlign(scalemode);
}

blexFLVslave.prototype.setAutoPlay = function blexFLVslave_setAutoPlay(autoplay)
{
  this.autoplay = autoplay;
//  this.player.setAutoPlay(autoplay);
}

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