
// Namespace object. //////////////////////////////////////////////////////
pcst3pis = {};

// Imagefader control. ////////////////////////////////////////////////////
pcst3pis.ImageFader = Class.create({

  // Host DOM - element. Lookuped for during
  // initialization process.
  oHostElt: null,

  // The objects state (i.e., fresh, inited, running, paused, stopped)
  eObjStat: 0,  
  
  // Image change interval ( time during fading up 
  // and down an image-pair ) in milliseconds. This 
  // value can be configured by clients and gets 
  // set during initialization process. Default is
  // lazy 3000 ms.
  uFadingItv: 0,
  
  // Fading resolution. The actual value of the fading 
  // resolution may differ from this, since the execution 
  // callback interval may not fall short of 10 ms. Value 
  // defaults to 100 ( see initialization process).
  uFadingRes: 0,
  
  // Fading pause. The pause between to separate fading 
  // periods. Defaults to 0.
  uFadingPause: 0,
  
  // Callback inverval, used when registering callback
  // handler via window.setInterval. This value gets 
  // calculated during the initialization process.
  uExcCBItv: 0,
  
  // Callback counter. This value gets incremented during
  // one period of up and 
  uExcCBCtr: 0,
  
  // The image DOM - elments to be faded. These are created
  // while initialization process.
  oImg1Elt: null,
  oImg2Elt: null,
  
  // The image element opacities.
  uImg1Opc: 100,
  uImg2Opc: 0,
  
  // The images array as received during 
  // initialization process.
  aImageObjs: null,
  
  // The interval resource handle got 
  // and passed from and to the native
  // interval functions window.*Interval
  rExcItv: null,
  
  // Pointer in the images array.
  uImgIdx: 0,
  
  // Debug message element id
  sDbgMsgEltId: null,
  
  initialize: function(sHostEltId, oInit) {
    // Return if in improper state.
    if(this.eObjStat == pcst3pis.ImageFader.STAT_PAUSED ||
       this.eObjStat == pcst3pis.ImageFader.STAT_RUNNING) {
      return;
    }
    if(sHostEltId) {
      this.oHostElt = $(sHostEltId); 
      if(this.oHostElt) {
        if(typeof(this.oHostElt.pcst3pis)!='object')
          this.oHostElt.pcst3pis = {}
        this.oHostElt.pcst3pis.ImageFader = this;
      }
    } 
    if(oInit)
      this.init(oInit);   
  },
  
  
  /********************************************************
  **************** Object oInit members *******************
  *********************************************************
  *
  * string hostEltId 
  *
  *   The id of the DOM Element, where the images reside.
  *
  * array imageObjs
  *
  *   The array of images to be faded up and down. Must be
  *   at least two items.
  *
  * unsigned objWidth
  *
  *   The width of the object i.e. image. All images get 
  *   scaled to this width.
  *
  * unsigned objHeight
  *
  *   The heigth of the object i.e. image. All images get 
  *   scaled to this height.
  *
  * unsigned fadingPeriod
  *
  *   Image change interval ( time during fading up 
  *   and down an image-pair ) in milliseconds.
  *
  * unsigned fadingResolution
  *
  *   See description of correspondent object member. 
  *
  * unsigned fadingPause
  *
  *   Fading pause. The pause between to separate fading periods. 
  *   
  * string dbgMsgEltId
  *
  *   The document-id of an element, where the control can 
  *   write debug messages to. 
  *
  ********************************************************/
  init: function(oInit) {
  
    // Possible initialization errors.
    var aErr = [];
    
    do {
    
      // Break if host element was not already obtained.
      if(!this.oHostElt) {
        aErr.push("host element not set.");
        break;
      }
      
      // Break if object in improper state.
      if(this.eObjStat == pcst3pis.ImageFader.STAT_PAUSED ||
         this.eObjStat == pcst3pis.ImageFader.STAT_RUNNING) {
        aErr.push("array in improper state for initialization");
      }
      
      // Break if improper type.
      if(typeof(oInit) != 'object') {
        aErr.push("empty init object");
        break;
      }
      
      // Break if images array doesn't meets init-criteria.
      if(typeof(oInit.imageObjs)!='object') {
        aErr.push("images array empty.");
        break;
      }
      
      // Populate image-object array.
      this.aImageObjs = [];
      for(var sIdx in oInit.imageObjs) {
        var oImg = new Image();
        oImg.src = oInit.imageObjs[sIdx];
        this.aImageObjs[this.aImageObjs.length] = oImg;         
      }
      
      if(this.aImageObjs.length < 2) {
        aErr.push("images array to small. "+
          "must be at least two images!");
        break;
      }
      
      // Hide object temporarily
      this.setVisible(false);
      
      // Remove all child elements from the host element first
      // and the create our two image elements.
      var oTmp = this.oHostElt;
      while(oTmp.firstChild)
        oTmp.removeChild(oHostElt.firstChild);
      
      // Initialize width and height to proper values.
      var uW = 100, uH = 100; 
      if(Object.isNumber(oInit.objWidth))
        uW = Math.abs(Math.floor(oInit.objWidth));
      if(Object.isNumber(oInit.objHeight))
        uH = Math.abs(Math.floor(oInit.objHeight));                    
      
      // Prepare and set element style.
      var oEltStyle = {
      	position:	'relative',
        width:    uW+'px',
        height:   uH+'px',
      	border:   '0px',
      	padding:  '0px'
      };
      this.oHostElt.setStyle(oEltStyle);
      
      // Create our image elements.
      this.oImg1Elt = $(document.createElement('img'));
      this.oHostElt.appendChild(this.oImg1Elt);
      this.oImg2Elt = $(document.createElement('img'));
      this.oHostElt.appendChild(this.oImg2Elt);
      
      // Set image elements to first two image object members. 
      this.oImg1Elt.src = this.aImageObjs[0].src;
      this.oImg2Elt.src = this.aImageObjs[1].src;
      
      // Prepare and set image element styles.
      oEltStyle = {
        display:  'block',
        position: 'absolute',	
      	left:			'0px',
      	top:			'0px',
        width:    uW+'px',
        height:   uH+'px',      	
        margin:   '0px',
        border:   '0px',
        padding:  '0px', 
        opacity:  '',
        filter:		'alpha(opacity=100)'
      }    
      this.oImg1Elt.setStyle(oEltStyle);
      
      oEltStyle.opacity = 0;
      oEltStyle.filter = 'alpha(opacity=0)'; 
      this.oImg2Elt.setStyle(oEltStyle);
      
      // Initialize fading period/interval to a proper value. 
      this.uFadingItv = 3000;
      if(Object.isNumber(oInit.fadingPeriod)) {
        this.uFadingItv = Math.abs(Math.floor(oInit.fadingPeriod));
        if(this.uFadingItv < 100)
          this.uFadingItv = 100;    
      }
      
      // Initialize fading resolution to a proper value.
      this.uFadingRes = 100;
      if(Object.isNumber(oInit.fadingResolution)) {
        this.uFadingRes = Math.abs(Math.floor(oInit.fadingResolution));
        if(this.uFadingRes < 10)
          this.uFadingRes = 10;    
      }         
      
      // Initialize, probably adjust execution callback 
      // interval to a proper value.
      this.uExcCBItv = Math.floor(this.uFadingItv/this.uFadingRes); 
      if(this.uExcCBItv < 10) {
        this.uExcCBItv = 10;
        this.uFadingRes = Math.floor(
          this.uFadingItv/this.uExcCBItv);
      }

      // Initialize fading pause to a proper value.
      this.uFadingPause = 0;
      if(Object.isNumber(oInit.fadingPause))
        this.uFadingPause = Math.abs(Math.floor(oInit.fadingPause));
      
      // Initialize optional debug message element id.
      if(Object.isString(oInit.dbgMsgEltId) &&
          oInit.dbgMsgEltId != this.oHostElt.id) {
        this.sDbgMsgEltId = oInit.dbgMsgEltId;
      }
      
      // Set object visible again.
      this.setVisible(true);      
    
    } while(false);
  
    if(aErr.length) {
      var sErr = "";
      for(var i=0; i<aErr.length; i++)
        sErr += aErr[i]+"\r\n";
      alert("Errors occured: \r\n"+sErr);
      this.oHostElt = null;
      return;
    }
    
    this.eObjStat = pcst3pis.ImageFader.STAT_INITED;
  },
  
  setVisible: function(b) {
    if(!this.oHostElt)
      return;
    this.oHostElt.setStyle({visibility:(b?"visible":"hidden")});
  },
  
  setDisplay: function(b) {
    if(!this.oHostElt)
      return;
    this.oHostElt.setStyle({display:(b?"block":"none")});
  },
  
  state: function() {
    return this.eObjStat;
  },
  
  dbgMsg: function(sMsg) {
    sMsg = "Debug Message: "+sMsg;
    if(!this.sDbgMsgEltId)
      alert(sMsg);
    else {
      var oElt = $(this.sDbgMsgEltId);
      if(!oElt)
        return;
      oElt.innerHTML = sMsg;
    }
  },
  
  start: function() {
    if(this.eObjStat == pcst3pis.ImageFader.STAT_FRESH ||
       this.eObjStat == pcst3pis.ImageFader.STAT_RUNNING) {
      return;
    }
  
    this.uExcCBCtr = 0;
    
    this.uImg1Opc = this.uFadingRes;
    this.uImg2Opc = 0;
    
    this.eObjStat = pcst3pis.ImageFader.STAT_RUNNING
    this.rExcItv = window.setInterval(
     this.execCB.bind(this),this.uExcCBItv);
  },
  
  execCB: function() {
    if(this.eObjStat != pcst3pis.ImageFader.STAT_RUNNING)
      return;
  
    var sImg1NewSrc = null;
    var sImg2NewSrc = null;
    
    var uImg1Opc = this.uFadingRes - this.uExcCBCtr;
    var uImg2Opc = this.uExcCBCtr;        
    
    var bImgChange = false;
    
    if(uImg1Opc < 0 && uImg2Opc > this.uFadingRes) {
      this.uExcCBCtr  = 0;  
      this.uImg1Opc   = this.uFadingRes;
      this.uImg2Opc   = 0;
      this.uImgIdx++;
      if(this.uImgIdx >= this.aImageObjs.length)
        this.uImgIdx = 0;
      var bLastIdx = this.uImgIdx == this.aImageObjs.length-1;         
      sImg1NewSrc = this.aImageObjs[this.uImgIdx].src;
      sImg2NewSrc = this.aImageObjs[bLastIdx ? 0 : this.uImgIdx+1].src;    
      bImgChange = true;
    } 
  
    var dImg1Opc = (uImg1Opc<0?0:uImg1Opc)/this.uFadingRes;
    var dImg2Opc = (uImg2Opc>this.uFadingRes?this.uFadingRes:uImg2Opc)/this.uFadingRes;
    
    if(bImgChange) {
      this.oImg1Elt.setOpacity(1);
      this.oImg2Elt.setOpacity(0);          
      this.oImg1Elt.src = sImg1NewSrc;
      this.oImg2Elt.src = sImg2NewSrc;      
    } else {
      this.oImg1Elt.setOpacity(dImg1Opc);
      this.oImg2Elt.setOpacity(dImg2Opc);     
    }
    
    if(bImgChange && this.uFadingPause && 
       this.uFadingPause > this.uExcCBItv) {
      this.stop();
      window.setTimeout(this.start.bind(this),
        this.uFadingPause-this.uExcCBItv);
    } else {
      this.uExcCBCtr++;
    }
  },
  
  stop: function() {
    if(this.eObjStat != pcst3pis.ImageFader.STAT_RUNNING)
      return;
    window.clearInterval(this.rExcItv);
    this.eObjStat = pcst3pis.ImageFader.STAT_STOPPED;    
  }

});

pcst3pis.ImageFader.STAT_FRESH    = 0;
pcst3pis.ImageFader.STAT_INITED   = 1;
pcst3pis.ImageFader.STAT_RUNNING  = 2;
pcst3pis.ImageFader.STAT_PAUSED   = 3;
pcst3pis.ImageFader.STAT_STOPPED  = 4;

pcst3pis.ImageFader.Register = function(sHostEltId, oInit, bAutoStart) {
  var oObj = new pcst3pis.ImageFader;
  function cb() {
    oObj.initialize(sHostEltId,oInit);
    if(bAutoStart)
      oObj.start();
  }
  if(window.attachEvent) {
    window.attachEvent("onload",cb);
  } else if(window.addEventListener) {
    window.addEventListener("load",cb,[]);
  } else {
    alert("Couldn't register image fader");
  }
  return oObj;
};
