// Copyright 2007 Sheepshank. All rights reserved.
// Item image viewer.
// Depends on YAHOO, THOR.

if (typeof THOR == "undefined") {
  throw new Error('Must include thor.js');
}

THOR.log.where = 'item-image-viewer.js';

/////////////// ItemImageViewer

if (!THOR.ItemImageViewer) {
  THOR.ItemImageViewer = {};
}

////////// ImageQuery

THOR.ItemImageViewer.ImageQuery = function() {
  this.data = undefined;
};

THOR.ItemImageViewer.ImageQuery.prototype = {
  load: function(aaSource) {
    this.rxCurrent = undefined;
    this.ctRx = undefined;

    this.fetcher = new aaSource.clsFetcher(aaSource);
    this.fetcher.addObserver(this);

    this.fetcher.fetchData();
    this.notifyObservers('Initialising');
  },

  setCurrentRx: function(rx) {
    this._throwIfInvalidRx(rx);
    this.rxCurrent = rx;

    this.notifyObservers('RxChanged');
  },

  startPeriodicUpdate: function(drx, secPerRx) {
    this.secPerRx = secPerRx;
    this.drx = drx;

    this.notifyObservers('PeriodicUpdateStart');

    // with immediate effect.
    this._doPeriodicUpdate();

    fn = THOR.utils.fnCurriedScoped(this._doPeriodicUpdate, this);
    this.timer = setInterval(fn, this.secPerRx * 1000);
  },

  _doPeriodicUpdate: function() {
      try {
	this.setCurrentRx(this.rxCurrent + this.drx);
      } catch(e) {
	this.stopPeriodicUpdate();
      }
  },

  stopPeriodicUpdate: function() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
      this.notifyObservers('PeriodicUpdateStop');
    }
  },

  _throwIfInvalidRx: function(rx) {
    if (!this.data) {
      throw new Error('Data not ready.');
    }

    if (isNaN(rx) || rx < 0 || rx >= this.ctRx) {
      throw new Error('Invalid position.');
    }
  },

  destroy: function() {
    this.stopPeriodicUpdate();
    this.fetcher && this.fetcher.destroy && this.fetcher.destroy();
    this.notifyObservers('Destroy');
  },

  onNewData: function(obj, data) {
    this.ctRx = data.images.length;
    this.rxCurrent = 0;
    this.meta = data.meta;
    this.data = data;

    this.notifyObservers('DataReady');
  },

  onConnectFailure: function(obj) {
    this.notifyObservers('ConnectFailure');
  }
};

THOR.bolt(THOR.ItemImageViewer.ImageQuery.prototype, THOR.utils.boltObservable);


////////// FullView

THOR.ItemImageViewer.FullView = function(query, idParent) {
  this.query = query;
  this.eltParent = document.getElementById(idParent);

  this.arElt = [];
  this.arImage = [];

  this.query.addObserver(this);
};

THOR.ItemImageViewer.FullView.prototype = {
  secPerAnim: 0.25,

  onDataReady: function(obj) {
    this._createElts();
    this.onRxChanged();
  },

  onDestroy: function(obj) {
    this._destroyElts();
  },

  onRxChanged: function(obj) {
    var rxNew = this.query.rxCurrent;

    if (this.eltCurrent) {
      this._showElt(this.eltCurrent, false);
    }

    this.eltCurrent = this.arElt[rxNew];
    this._showElt(this.eltCurrent, true);
  },

  _showElt: function(elt, bShow) {
    var x = bShow ? 0 : this.xInvisible;
    var anim = new YAHOO.util.Anim(elt, { 
      left: { to: x }
      },
      this.secPerAnim,
      YAHOO.util.Easing.easeBoth
      );
    
    anim.animate();
  },

  _createElts: function() {
    // What's my size?
    var dxdy = THOR.utils.Dom.dxdyIncBorderPadding(this.eltParent);
    // 'off the screen'
    this.xInvisible = -dxdy[0];

    var arDataImage = this.query.data.images;

    for (var rx = 0; rx < arDataImage.length; rx++) {
      var elt = document.createElement('div');
      elt.id = 'full' + rx;
      elt.className = 'image';
      elt.style.left = this.xInvisible + 'px';

      this.eltParent.appendChild(elt);
      this.arElt.push(elt);

      this.arImage.push(new THOR.utils.ImageLoader(arDataImage[rx].urlImageFULL,
						   elt, false));
    }
  },

  _destroyElts: function() {
    while (this.arImage.length > 0) {
      this.arImage.pop().destroy();
    }

    while (this.arElt.length > 0) {
      var elt = this.arElt.pop();

      if (elt.parentNode) {
	elt.parentNode.removeChild(elt);
      }
    }
  }
};


////////// ThumbView

THOR.ItemImageViewer.ThumbView = function(query, idParent) {
  this.query = query;
  this.eltParent = document.getElementById(idParent);

  this.arElt = [];
  this.arImage = [];

  this.query.addObserver(this);
};

THOR.ItemImageViewer.ThumbView.prototype = {
  secPerAnim: 0.5,
  sClassCSS: 'image',

  onDestroy: function(obj) {
    this._destroy();
  },

  onDataReady: function(obj) {
    this._createElts();
    this._registerEvents();
    this._setSizeAndPosition();
    this.onRxChanged();
  },

  onRxChanged: function(obj) {
    // top-most thumb should be the first on a 'page'
    var rxFirst = this.query.rxCurrent - (this.query.rxCurrent % this.query.ctRxPerPage);

    var y = -this.dxdyThumb[1] * rxFirst;

    if (this.secPerAnim > 0) {
      var anim = new YAHOO.util.Anim(this.eltThumbs, { 
	top: { to: y }
	},
	this.secPerAnim,
	YAHOO.util.Easing.easeBoth
	);

      anim.animate();
    } else {
      this.eltThumbs.style.top = y + 'px';
    }

    // Highlight the current image.
    if (this.eltCurrent) {
      YAHOO.util.Dom.removeClass(this.eltCurrent, 'current');
    }
    this.eltCurrent = this.arElt[this.query.rxCurrent];
    YAHOO.util.Dom.addClass(this.eltCurrent, 'current');
  },

  _createElts: function() {
    // This contains all the thumbs, sliding along inside the parent elt.
    var elt = document.createElement('div');
    elt.className = 'thumbs';
    elt.style.visibility = 'hidden';
    this.eltParent.appendChild(elt);
    this.eltThumbs = elt;

    var arDataImage = this.query.data.images;

    for (var rx = 0; rx < arDataImage.length; rx++) {
      var elt = document.createElement('div');
      elt.id = 'thumb' + rx;
      elt.className = this.sClassCSS;

      this.eltThumbs.appendChild(elt);
      this.arElt.push(elt);

      this.arImage.push(new THOR.utils.ImageLoader(arDataImage[rx].urlImageTHUMB,
						   elt, true));
    }
  },

  _setSizeAndPosition: function() {
    // Now determine dimensions and set positions etc.
    this.dxdyThumb = THOR.utils.Dom.dxdyIncBorderPadding(this.eltThumbs.firstChild);

    // eltThumbs extends as far as its children
    this.eltThumbs.style.width = this.dxdyThumb[0] + 'px';
    this.eltThumbs.style.height = (this.arElt.length * this.dxdyThumb[1]) + 'px';

    for (var rx = 0; rx < this.arElt.length; rx++) {
      var elt = this.arElt[rx];
      elt.style.top = (rx * this.dxdyThumb[1]) + 'px';
      elt.style.left = '0px';
    }

    this.eltThumbs.style.visibility = 'visible';

    // And the 'canvas' size, assuming no padding.
    var dyParent = parseInt(YAHOO.util.Dom.getStyle(this.eltParent, 'height'));

    this.query.ctRxPerPage = Math.floor(dyParent / this.dxdyThumb[1]);
  },

  _registerEvents: function() {
    YAHOO.util.Event.addListener(this.eltParent,
				 'click',
				 this.handleEvent,
				 this, true);
  },

  handleEvent: function(event, obj) {
    try {
      // could be an image or a blank part of eltThumbs/eltParent.
      // the ID we want is the parent of the image.
      var elt = YAHOO.util.Event.getTarget(event);

      while (elt != this.eltParent && !elt.id && elt.id.slice(0, 5) != 'thumb') {
	elt = elt.parentNode;
      }

      if (elt == this.eltParent) {
	return;
      }

      var id = elt.id;
    } catch(e) {
      // testing only.
      var id = event;
    }

    if (id.slice(0, 5) != 'thumb') {
      return;
    }

    var rx = parseInt(id.slice(5));

    this.query.stopPeriodicUpdate();
    this.query.setCurrentRx(rx);
  },
  

  _destroy: function() {
    YAHOO.util.Event.purgeElement(this.eltParent);

    while (this.arImage.length > 0) {
      this.arImage.pop().destroy();
    }

    while (this.arElt.length > 0) {
      var elt = this.arElt.pop();

      if (elt.parentNode) {
	elt.parentNode.removeChild(elt);
      }
    }
    if (this.eltThumbs) {
      if (this.eltThumbs.parentNode) {
	this.eltThumbs.parentNode.removeChild(this.eltThumbs);
      }
      this.eltThumbs = null;
    }
  }
};




////////// ControlView

THOR.ItemImageViewer.ControlView = function(query, idParent) {
  this.query = query;
  this.eltParent = document.getElementById(idParent);

  this._initElts();
  this._registerEvents();

  this.bPlaying = false;

  this.query.addObserver(this);
};

THOR.ItemImageViewer.ControlView.prototype = {
  secPerRx: 1.5,

  onInitialising: function(obj) {
    this._setDisabledStates();
  },

  onDestroy: function(obj) {
    YAHOO.util.Event.purgeElement(this.eltParent);
  },

  onDataReady: function(obj) {
    this._setDisabledStates();
  },

  onRxChanged: function(obj) {
    this._setDisabledStates();
  },

  onPeriodicUpdateStart: function(obj) {
    this._togglePlaying();
    this._setDisabledStates();
  },

  onPeriodicUpdateStop: function(obj) {
    this._togglePlaying();
    this._setDisabledStates();
  },

  _initElts: function() {
    var self = this;
    this.aaInfoByCls = {
      'prevPage': {
	'bDisabled': function() { 
	  return !self.query.data || self.query.rxCurrent < self.query.ctRxPerPage;
	},
	dPage: -1
      },
      'nextPage': {
	'bDisabled': function() { 
	  return !self.query.data || self.query.rxCurrent - (self.query.rxCurrent % self.query.ctRxPerPage) + self.query.ctRxPerPage >= self.query.ctRx;
	},
	dPage: 1
      },
      'play': { 
	'bDisabled': function() { 
	  return !self.query.data || self.query.rxCurrent == self.query.ctRx - 1;
	}
      }
    };

    for (var cls in this.aaInfoByCls) {
      this.aaInfoByCls[cls].elt = YAHOO.util.Dom.getElementsByClassName(cls, null, this.eltParent)[0];
      this.aaInfoByCls[cls].elt.cls = cls; // sneaky annotation
    }

    this._setDisabledStates();
  },

  _setDisabledStates: function() {
    for (var cls in this.aaInfoByCls) {
      var elt = this.aaInfoByCls[cls].elt;

      var sFn = (this.aaInfoByCls[cls].bDisabled()) ? 'addClass' : 'removeClass';
      YAHOO.util.Dom[sFn](elt, 'disabled');
    }
  },

  _togglePlaying: function() {
    this.bPlaying = !this.bPlaying;

    var elt = this.aaInfoByCls.play.elt;

    var sFn = (YAHOO.util.Dom.hasClass(elt, 'playing')) ? 'removeClass' : 'addClass';
    YAHOO.util.Dom[sFn](elt, 'playing');
  },

  bDisabled: function(cls) {
    return YAHOO.util.Dom.hasClass(this.aaInfoByCls[cls].elt, 'disabled');
  },

  _registerEvents: function() {
    YAHOO.util.Event.addListener(this.eltParent,
				 'click',
				 this.handleEvent,
				 this, true);
  },

  handleEvent: function(event, obj) {
    // the '|| event's are for testing purposes.
    var elt = YAHOO.util.Event.getTarget(event) || event;
    var cls = elt.cls || event;

    if (!this.aaInfoByCls[cls] || this.bDisabled(cls)) {
      return;
    }

    if (cls == 'play') {
      if (this.bPlaying) {
	this.query.stopPeriodicUpdate();
      } else {
	this.query.startPeriodicUpdate(1, this.secPerRx);
      }
    } else {
      // not play
      if (this.bPlaying) {
	this.query.stopPeriodicUpdate();
      }
      var dPage = this.aaInfoByCls[cls].dPage;
      var rxNew = Math.max(0, Math.min(this.query.rxCurrent + dPage * this.query.ctRxPerPage, this.query.ctRx - 1));
      this.query.setCurrentRx(rxNew);
    }
    
  }

};
