dojo.declare("swx.QuotePlayer", null, {
  
/*************************************************************************************************
 * Constructor                                                                                   *
 *************************************************************************************************/
  constructor: function(movie, userUpdateFunction, userTickFunction, reloadURL) { 
  	                               // all arrays and complex objects should be declared here
    // save the movie
    this._moviePart = movie;
    
    // Start time of movie
    this._t0 = new Date().getTime();

    // creates the array containing the updates
    this._updateEvents = [];
    
    // the time in ms after which the server should reload data
    this._reloadTime = null;

    // The next reload url given by the server
    this._nextReloadURL = null;
    
    if( reloadURL != null){
        this._nextReloadURL = reloadURL;
        /* get directly a movie */
        this._askForNextMoviePart();
    }
    
    /* put it to true in the object instance within the browser to display logs */
    this.debug = false;

    // timer rate
    this._rate = 100;

    // start with 0 ajax errors!
    this._ajaxErrors = 0;
    
    // This timer (used to stop the playing)
    this._timer = null;
    
    // The function to call when an update is triggered
    this._userUpdateFunction = userUpdateFunction;
    
    // The function to call on every tick
    this._userTickFunction = userTickFunction;
  },
  
/*************************************************************************************************
 * Static members ( You have to use this.statics.XXXX )                                          *
 *************************************************************************************************/
  statics: {
    // the two colors used for flashing
    GREEN_COLOR  : "#00FF00",
    RED_COLOR    : "#FF0000",
    
    // src for the up and down images
    UP_IMG_SRC   : '/resources/images/dojo/arrow_up_black.gif',
    DOWN_IMG_SRC : '/resources/images/dojo/arrow_down_black.gif',
    
    // Ajax problem limtations
    AJAX_ERROR_LIMIT : 7,   // try 6 times before requesting less frequently
    AJAX_ERROR_TOOMANY_RETRYTIME : 600000, // 10 minutes to retry after AJAX_ERROR_LIMIT errors reached 
    AJAX_ERROR_DELAY_LOWER_LIMIT : 5000,
    AJAX_ERROR_DELAY_UPPER_LIMIT : 15000,
    AJAX_ERROR_DELAY_STEP : 10000,
    
    ERROR_RETURNTEXT : "{errorText:\"error\"}",
    
    // Flashing columns - used only in marketpulse product explorer (FIXME: move it there!)
    FLASH_COLIDS : ["BidPrice", "AskPrice", "LastPrice"]
  },
  
/*************************************************************************************************
 * Public play function. Used to start the playing of the movie                                  *
 *************************************************************************************************/
  play: function() {
    // first call by hand to prevent it to waix x milliseconds
    this.timerTick();
    // Then start a timer (every x milliseconds seconds)
    this._timer = setInterval(dojo.hitch(this, this.timerTick), this._rate);
  },
  
/*************************************************************************************************
 * Public stop function. Used to stop the playing of the movie                                   *
 *************************************************************************************************/
  stop: function() {
    // kill the timer
    clearInterval(this._timer);
    this._timer = null;
  },
  
/*************************************************************************************************
 * Public clearUpdateEvents function. Used to clear the update events (to abort other updates)   *
 *************************************************************************************************/
  clearUpdateEvents: function() {
    this._updateEvents = [];
  },
  
/*************************************************************************************************
 * This function is called every x (this._rate) milliseconds                                     *
 *************************************************************************************************/
  timerTick: function() {
    // the time now in ms since 1970 (used to know if we update cells 
    //                                and if we ask for a new movie part)
    var actualTime = new Date().getTime();

    // See if we have new data from a ajax response
    if (this._moviePart != null) {
      this._fillUpdateEventsAndMoreFromMovie();
      // this method resets this._moviePart to null when it is finished
    }
    
    // Is it time to reload new data from the server?
    if (this._reloadTime != null && this._reloadTime < actualTime) {
      this._askForNextMoviePart();  // asks for new update events 
      // <!!> sets _reloadTime to null so we don't ask more than once at a time
      //      it will be reset by the _fillUpdateEventsAndMoreFromMovie();
    }

    // Update if needed
    this._update(actualTime);
    
    if (this._userTickFunction) this._userTickFunction(actualTime);

  },
  
/*************************************************************************************************
 * Takes the _moviePart and fills the update event queue, then resets the _moviePart to null     *
 *************************************************************************************************/
  _fillUpdateEventsAndMoreFromMovie: function() {
    // Set the next reloadTime
    this._reloadTime = this._t0 + parseInt(this._moviePart.reloadTime);
    this._nextReloadURL = this._moviePart.reloadURL;

    if (this._moviePart.updateEvents != null) {
      for (var i = 0; i < this._moviePart.updateEvents.length; i++) {
        if (this._moviePart.updateEvents[i] != null) {
          var updateEvent = this._createUpdateEvent(this._moviePart.updateEvents[i]);
          this._updateEvents.push(updateEvent);
        }
      }
    }
    
    this._moviePart = null;  // Ok, we are ready to recieve a new part
  },
 
  /*******************************************************
   * Small function to create a 'updateEvent object'     *
   * from the updateEvent coming from the JSON file      *
   *******************************************************/
  _createUpdateEvent: function(updateEvent) {
    var update = {};  // new object
    update.deltatime = this._t0 + parseInt(updateEvent[0]);
    update.rowid     = this._moviePart.rowIds[updateEvent[1]];
    update.columnid  = this._moviePart.colIds[updateEvent[2]];
    update.newValue  = updateEvent[3].replace(/&amp;/g,"\&").replace(/&nbsp;/g," ").replace(/&lt;/g,"<").replace(/&gt;/g,">");
    if (update.newValue == "") {
    	update.newValue = "\u00a0"; // nbsp but unicoded for javascript (otherwise it shows &nbsp; in the cell)
    }
    update.delta = updateEvent[4];
    update.doFlash = false;
    for (var i=0; i<this.statics.FLASH_COLIDS.length; i++) {
      if (this.statics.FLASH_COLIDS[i] == update.columnid) {
        update.doFlash = true;
        break;
      }
    }
    return update;
  },
  
/*************************************************************************************************
 * This method sets the reloadTime to null and asks for the next movie part with an ajax request *
 *************************************************************************************************/
  _askForNextMoviePart: function() {
    this._reloadTime = null; // now we will not update again before a ajax response comes (or error)

    // do the ajax call
    dojo.xhrGet( { // 
      url: this._nextReloadURL, 
      handleAs: "text",
      timeout: 10000, // Time in milliseconds
      preventCache: true,

      // The LOAD function will be called on a successful response.
      load: dojo.hitch(this, function(response, ioArgs) { // 
        this._ajaxErrors = 0;  // got a good response, reset ajax errors to 0
        if(response != this.statics.ERROR_RETURNTEXT ) {
                this._moviePart = eval( '('+response+')' );  // save the new move part
        }
        /* the movie is dead */
        else{
          this.stop();    
          /* we can just reload the page, it should be enough and transparent for the user */
          /* because this will happen after the server restart (near 6 a.m.) */
          /* get a random value between 60000 and 300000 (1 and 5 minutes) */
          if ( window['reloadDeadMoviePage'] == null){
                var rndReload = Math.floor(Math.random() * 240000) + 60000;
                this._log("The movie is now dead. The page will be reloaded in " + rndReload + "ms") ;
                window['reloadDeadMoviePage'] = true;
                setTimeout(function(){
                        window['reloadDeadMoviePage'] = null;
                        location.reload();
                        }
                        ,rndReload);
          }
        }
        return response; //

      }),

      // The ERROR function will be called in an error case.
      error: dojo.hitch(this, function(response, ioArgs) { //
        if (++this._ajaxErrors >= this.statics.AJAX_ERROR_LIMIT) {
          // we have had too many errors, wait a bit more now
          this._log("AJAX Error (error count "+this._ajaxErrors+"; next retry in "+(this.statics.AJAX_ERROR_TOOMANY_RETRYTIME)+"ms): "+response.dojoType);
          // "HTTP status code: "+ ioArgs.xhr.status);
          this._reloadTime = new Date().getTime() + this.statics.AJAX_ERROR_TOOMANY_RETRYTIME;
        } else {
          /*  error number : waiting time range £(seconds)
          * 1 : 5->15
          * 2 : 15->25
          * 3 : 25->35
          * 4 : 35->45
          * 5 : 45->55
          * 6  : 55->65
          * 7+ : 10 minutes
          */
          var rnd =  Math.floor( Math.random() * (this.statics.AJAX_ERROR_DELAY_UPPER_LIMIT - this.statics.AJAX_ERROR_DELAY_LOWER_LIMIT + 1)) + this.statics.AJAX_ERROR_DELAY_LOWER_LIMIT;
          /* put some random time to avoid everyone reloading at the same time */
          this._reloadTime = new Date().getTime() + (rnd + (this.statics.AJAX_ERROR_DELAY_STEP * (this._ajaxErrors-1)));
          this._log("AJAX Error (error count "+this._ajaxErrors+"; next retry in "+(this._reloadTime -  new Date().getTime())+"ms): "+response.dojoType);           
        }
        return response; // 
      })
    } );
  },
  
/*************************************************************************************************
 * Updates the cell values and put the animations in the animation queue                         *
 *************************************************************************************************/
  _update: function(actualTime) {
    while (this._updateEvents.length > 0) {     // if there are some update events
      var updateEvent = this._updateEvents[0];
      if (updateEvent.deltatime < actualTime) {
        this._updateEvents.shift();         // remove first object from the array (this one!);
        // really update the cell infos (and stock the animations)
        this._userUpdateFunction(updateEvent);
      } else {  // nothing more to do for now
        break; // this updateEvent is not yet happening... nothing more to do
      }
    }
  },

/*************************************************************************************************
 * 2 Simple log (alert or silent or anything...)                                                 *
 *************************************************************************************************/
  _log: function(stringToLog) {
  	if ( this.debug ) console.log("Quoteplayer: "+stringToLog);
  }, 
  
  _error: function(stringToLog) {
	console.error("Quoteplayer: "+stringToLog);
  }
  
});    // end of QuotePlayer page

  
  