/*
**  ImgLoader load of a list of images while shows a loading animation
**  and runs a callback at the end.
**  Copyright (C) 2009 COLIVRE <contato@colivre.coop.br>
**
**  This program is free software: you can redistribute it and/or modify
**  it under the terms of the GNU Affero General Public License as
**  published by the Free Software Foundation, either version 3 of the
**  License, or (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU Affero General Public License for more details.
**
**  You should have received a copy of the GNU Affero General Public License
**  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

var IMGLOADER_DEBUG = false;

function ImgLoader( conf ) {
  try {
    ////////// Sanitizing the configuration //////////
    // attempts: how muth tryes for each image if the load fails?
    if ( ! conf.attempts ) { conf.attempts = 3 }
    // parent: where to append the loading animation?
    if ( ! conf.parent ) { conf.parent = document.body }
    if ( typeof( conf.parent ) == "string" ) {
      conf.parent = document.getElementById( conf.parent );
    }
    // timeout: time limit to wait by the images.
    if ( ! conf.timeout ) { conf.timeout = 0 }
    if ( typeof(conf.timeout) == "string" ) {
      var unit = conf.timeout.replace( /[0-9]+/, "" );
      var val = parseFloat( conf.timeout );
      switch ( unit ) {
        case "sec": conf.timeout = val * 1000; break;
        case "min": conf.timeout = val * 1000 * 60; break;
      }
    }
    this.conf = conf;
    // autoStart: It must to load the images in the moment of its born?
    if ( typeof( conf.autoStart ) == "undefined" ) { conf.autoStart = true }
    if ( conf.autoStart ) { this.start() }
  } catch(err) {
    this.debug( "constructor", err );
  }
}

ImgLoader.prototype.debug = function ( methodName, err ) {
  if ( IMGLOADER_DEBUG ) {
    alert( "ImgLoader."+methodName+"()\n" + ( err.description? err.description : err ) );
  }
}

ImgLoader.prototype.setBindMeTimeout = function( methodRef, msec ) {
  var obj = this;
  return setTimeout( function(){ return methodRef.apply(obj) }, msec );
}

ImgLoader.prototype.start = function () {
  try {
    if ( ! this.loadingBox ) { this.createLoadingBox() }
    this.setBindMeTimeout( this.writeHiddenImageTags, 50 );
    if ( this.conf.timeout > 0 ) {
      this.stopWaitingTimeout = this.setBindMeTimeout( this.stopWaiting, this.conf.timeout );
    }
  } catch(err) {
    this.debug( "start", err );
  }
}

ImgLoader.prototype.createLoadingBox = function () {
  try {
    if ( this.loadingBox ) { return this.loadingBox }
    this.loadingBox = document.createElement("div");
    this.conf.parent.appendChild( this.loadingBox );
    this.loadingBox.className = "image-loading-box";
    var html = '<div class="image-loading-tics">';
    for ( var i=1; i<=8; i++ ) {
      html += '<div class="image-loading-tic il-tic'+i+' il-light'+i+'"></div>';
    }
    this.loadingBox.innerHTML += html +'</div>';
    this.loadingBoxTic();
    return this.loadingBox;
  } catch(err) {
    this.debug( "createLoadingBox", err );
  }
}

ImgLoader.prototype.loadingBoxTic = function () {
  try {
    var tics = this.loadingBox.childNodes[0].childNodes;
    for ( var tic,i=0; tic=tics[i]; i++ ) {
      var lightNum = parseInt( tic.className.replace( /.*il-light(.)/, "$1" ) );
      lightNum++;
      if ( lightNum == 9 ) { lightNum = 1 }
      tic.className = tic.className.replace( /(.*il-light).*/, "$1"+lightNum );
    }
    this.ticTimeout = this.setBindMeTimeout( this.loadingBoxTic, 150 );
  } catch(err) {
    this.debug( "loadingBoxTic", err );
  }
}

ImgLoader.prototype.writeHiddenImageTags = function () {
  try {
    this.imagesBox = document.createElement("div");
    this.imagesBox.style.position = "absolute";
    this.imagesBox.style.left = "0";
    this.imagesBox.style.bottom = "0";
    //alert(document.body.insertBefore)
    //document.body.appendChild( this.imagesBox );
    document.body.insertBefore( this.imagesBox, document.body.firstChild );
    for ( var imgURL,i=0; imgURL=this.conf.imgs[i]; i++ ) {
      var img = document.createElement("img");
      img.width = 1; img.height = 1;
      img.imgLoader = this;
      img.attempts = this.conf.attempts;
      img.onload = function(){ this.imgLoader.regLoaded(this) }
      img.onerror = function(){ this.imgLoader.regLoadError(this) }
      img.style.opacity = 0.05;
      img.style.filter = "alpha(opacity=05)";
      this.imagesBox.appendChild( img );
      img.src = imgURL; // must be after load event sets or will fail on IE.
    }
  } catch(err) {
    this.debug( "writeHiddenImageTags", err );
  }
}

ImgLoader.prototype.successList = [];
ImgLoader.prototype.regLoaded = function ( img ) {
  try {
    img.parentNode.removeChild( img );
    this.successList.push( img.src );
    this.incLoadEnd();
  } catch(err) {
    this.debug( "regLoaded", err );
  }
}

ImgLoader.prototype.failList = [];
ImgLoader.prototype.regLoadError = function ( img ) {
  try {
    if ( img.attempts == 0 ) {
      // must not to try again  :-(
      img.parentNode.removeChild( img );
      this.failList.push( img.src );
      this.incLoadEnd();
    } else {
      // "Let me try ... again." -- Frank Sinatra
      img.attempts--;
      if ( img.src.indexOf("?") >= 0 ) {
        img.src += "&attempt-countdown-" + ( img.attempts + 1 );
      } else {
        img.src += "?attempt-countdown-" + ( img.attempts + 1 );
      }
    }
  } catch(err) {
    this.debug( "regLoadError", err );
  }
}

ImgLoader.prototype.countLoadEnd = 0;
ImgLoader.prototype.incLoadEnd = function () {
  this.countLoadEnd++;
  if ( this.countLoadEnd == this.conf.imgs.length ) {
    // Ok! That is the end of loading
    this.stopWaiting();
  }
}

ImgLoader.prototype.stopWaiting = function () {
  try{
    clearTimeout( this.ticTimeout )
    clearTimeout( this.stopWaitingTimeout )
  }
  catch(e){ /* nothing to do */ }
  this.loadingBox.parentNode.removeChild( this.loadingBox );
  if ( this.conf.onLoad ) {
    this.conf.onLoad();
  }
}
