/*
 * Copyright (C) 2007 Sheepshank.
 * All rights reserved.
 * $Revision: 1040 $
 */

if (typeof SHSH == 'undefined') {
  throw new Error("Must include SHSH core first.");
}


(function() {
  /**
   * SHSH.components.Stats
   * 
   * A component that displays one of a number of passed statistics,
   * updating it dynamically.
   * 
   * @constructor
   *
   * @param config {Object} Object containing properties configuring
   * this instance.
   * 'id' (mandatory) sets the ID of an element, within
   * which we render the component.
   * 'arStat' (mandatory) sets the array of Stat objects that we can
   * render.
   * 'ixStatCurrent' (optional, default 0) selects which array member
   * to render initially.
   * 'ctDigit' (optional, default 6) defines the number of digits used
   * for the value. This is prefixed with zeroes as necessary.
   *
   * @public
   */
  SHSH.components.Stats = function(config) {
    // private instance vars
    var self = this;

    /**
     * SHSH.components.Stats._elt
     * The element within which we render the component.
     * @type {HTMLElement}
     * @private
     */
    var _elt = document.getElementById(config.id);

    /**
     * SHSH.components.Stats._arStat
     * An array of Stat objects, one of which is rendered at any time.
     * @type {Array of Stat}
     * @private
     * @default as passed in config, or SHSH.components.Stats.arStatDefault
     */
    var _arStat = (config.arStat && config.arStat.slice
		   && config.arStat.slice())
                  || SHSH.components.Stats.arStatDefault;

    /**
     * SHSH.components.Stats._statCurrent
     * The Stat we're currently rendering. One of the members of _arStat.
     * Set by setCurrentStatIx.
     * @type {Stat}
     * @private
     * @default null
     */
    var _statCurrent = null;

    /**
     * SHSH.components.Stats._timer
     * Holds the opaque interval ID, returned by setInterval and
     * passed to clearInterval, used to refresh the display.
     * @type {HTMLElement}
     * @private
     */
    var _timer = null;

    /**
     * SHSH.components.Stats._ctDigit
     * The number of digits rendered.
     * @type {Number, positive}
     * @private
     * @default as passed in config, or 6
     */
    var _ctDigit = (config.ctDigit > 0) ? config.ctDigit : 6;


    // private privileged methods.

    /**
     * SHSH.components.Stats._renderValue
     * Renders the current value of the current Stat (which always
     * exists when this is called).
     * @private
     */
    var _renderValue = function() {
      if (!_eltValue) {
	return;
      }

      var value = _statCurrent.valueCalculate();

      // handy storage rounded to nearest single decimal point.
      _eltValue.value = Math.round(value * 10) / 10;

      // The decimal part will affect one or more of the integer digits.
      var integer = Math.floor(value);
      var decimal = value - integer; 

      // The decimal part always affects the next most significant digit.
      // If that digit is 9, the following digit is also affected, and
      // so on until we hit non-9.
      // This triggers this behaviour for the LSD of the integer.
      var units = 9;
      
      for (var ix = _ctDigit - 1; ix >= 0; ix--) {
	decimal = (units == 9) ? decimal : 0;

	units = integer % 10;
	integer = (integer - units) / 10;

	var dy = (units + decimal) * _dyPerDigit;
	_eltValue.childNodes[ix].style.backgroundPosition = '0px -' + dy + 'px';
      }
    };


    /**
     * SHSH.components.Stats._handleClick
     * Event handler that switches the current Stat when you click
     * the parent element.
     * @param event {Event} The event object.
     * @private
     */
    var _handleClick = function(event) {
      for (var ix = 0, ct = _arStat.length; ix < ct; ix++) {
	if (_statCurrent == _arStat[ix]) {
	  self.setCurrentStatIx(ix + 1);
	  return;
	}
      }
    }


    // public privileged methods.

    /**
     * SHSH.components.Stats.setCurrentStatIx
     * Identifies the Stat to render, and renders it.
     * @param ix {Number, array index} Which Stat to render. If no
     * Stats, does nothing. If outside the range 0..number of Stats,
     * is reduced to be within the range.
     * @public
     */
    this.setCurrentStatIx = function(ix) {
      if (_arStat.length == 0 || isNaN(ix)) {
	return;
      }

      _statCurrent = _arStat[ix % _arStat.length];


      if (_timer) {
	clearInterval(_timer);
      }

      // the label is constant
      if (_eltLabel) {
	_eltLabel.innerHTML = _statCurrent.sLabel;
      }

      _renderValue();

      // Refresh only when we need to shift a digit in the strip,
      // but never more than 50 times a second.
      var msPerDy = Math.max(_statCurrent.msPerValue / _dyPerDigit,
			     20);
      _timer = setInterval(_renderValue, msPerDy);
    };

    /**
     * SHSH.components.Stats.destroy
     * Resets the page, removing the statistic, stopping updates.
     * @public
     */
    this.destroy = function() {
      YAHOO.util.Event.purgeElement(_elt);

      if (_timer) {
	clearInterval(_timer);
      }

      if (_elt) {
	_elt.innerHTML = '';
      }
    };


    // initialisation activities

    var _eltValue = document.createElement('div');
    _eltValue.className = 'value';

    for (var ix = 0; ix < _ctDigit; ix++) {
      var elt = document.createElement('div');
      elt.className = 'digit';

      _eltValue.appendChild(elt);
    }

    var _eltLabel = document.createElement('div');
    _eltLabel.className = 'label';

    _elt.appendChild(_eltValue);
    _elt.appendChild(_eltLabel);

    // need to know how big each digit is. We don't measure the image;
    // we assume the CSS defined for the digit defines it.
    var _dyPerDigit = SHSH.utils.Dom.dxdyIncPadding(_eltValue.firstChild).dy;

    this.setCurrentStatIx(config.ixStatCurrent || 0);

    YAHOO.util.Event.addListener(_elt, 'click', _handleClick,
                                 this, true);

  };


  // some private static vars

  /**
   * SHSH.components.Stats._dateStartOfDay
   * Midnight at the start of the current day at time of page load.
   * @type {Date}
   * @private
   * @static
   */
  var _dateStartOfDay = new Date();
  _dateStartOfDay.setHours(0, 0, 0, 0);

  /**
   * SHSH.components.Stats._dateStartOfYear
   * Midnight at the start of January 1 of the current year at time of
   * page load.
   * @type {Date}
   * @private
   * @static
   */
  var _dateStartOfYear = new Date();
  _dateStartOfYear.setMonth(0, 1);
  _dateStartOfYear.setHours(0, 0, 0, 0);

  /**
   * SHSH.components.Stats._Stat
   * Simple class representing a single statistic. 
   *
   * @private
   * @constructor
   */
  var _Stat = function(sLabel, dateZero, msPerValue) {
    /**
     * SHSH.components.Stats._Stat.sLabel
     * The description for this statistic.
     * @type {String}
     * @private
     */
    this.sLabel = sLabel;

    /**
     * SHSH.components.Stats._Stat.msPerValue
     * How many milliseconds it takes to increase the value of the
     * statistic by one.
     * @type {Number}
     * @public
     */
    this.msPerValue = msPerValue;

    /**
     * SHSH.components.Stats._Stat.valueCalculate
     * Calculates and returns the value for the statistic.
     * @returns {Number} The calculated value.
     * @public
     */
    this.valueCalculate = function() {
      var dateNow = new Date();

      // NB we don't reset to zero with rollovers into next day/year.
      // Left as an exercise for the reader :-)
      var ms = dateNow - dateZero;

      return ms / this.msPerValue;
    };
  };

  /**
   * SHSH.components.Stats._arStatData
   * Raw data used in the construction of _Stat objects.
   * Each member is an array [property name, label, zero point, msPerValue]
   * for the statistic.
   * @type {Array of Array}
   * @private
   * @static
   */
  var _arStatData = [
    ['statExtinctThisYear', 'Number of plant, animal and insect species extinct so far this year', _dateStartOfYear, 630676],
    ['statRainforestToday', 'Acres of rainforest burned down so far today', _dateStartOfDay, 400],
    ['statCarsToday', 'Number of motor vehicles built so far today', _dateStartOfDay, 864],
    ['statRoadDeathsThisYear', 'Number of people killed so far this year on the world\'s roads', _dateStartOfYear, 119086],
    ['statRoadInjuriesToday', 'Number of people injured so far today on the world\'s roads', _dateStartOfDay, 3155.8]
    ];

  /**
   * SHSH.components.Stats.oStat
   * An object with Stat objects as properties, for easy access.
   * @type {Object}
   * @public
   */
  SHSH.components.Stats.oStat = {};
  
  for (var ix = 0, ct = _arStatData.length; ix < ct; ix++) {
    var statData = _arStatData[ix];

    SHSH.components.Stats.oStat[statData[0]] = new _Stat(statData[1], statData[2], statData[3]);
  }

  /**
   * SHSH.components.Stats.arStatDefault
   * An array of Stat objects, used by default if no custom Stats
   * are supplied to the SHSH.components.Stats constructor.
   * @type {Array of Stat}
   * @public
   */
  SHSH.components.Stats.arStatDefault = [
    SHSH.components.Stats.oStat.statExtinctThisYear,
    SHSH.components.Stats.oStat.statRainforestToday,
    SHSH.components.Stats.oStat.statCarsToday,
    SHSH.components.Stats.oStat.statRoadDeathsThisYear,
    SHSH.components.Stats.oStat.statRoadInjuriesToday,
  ];
  

})();
