/*!
 * jQuery.Countdown
 *
 * @projectDescription Simple css-based digital-like display for countdown usage for actions with end date in future.
 * @author Jiri Prokop
 * @version 0.1
 *
 * @id jQuery.countdown
 * @id jQuery.fn.countdown
 * @param {Object} settings Hash of settings, nothing is required.
 * @return {jQuery} Returns the same jQuery object, for chaining.
 *  
 * Notes:
 *  - The plugin requires jQuery.Display, jQuery.doTimeout.
 * 
 *  - Inspired by http://www.virgentech.com/blog/2009/10/building-object-oriented-jquery-plugin.html
 *     and http://flesler.blogspot.com/2008/02/jqueryserialscroll.html
 *  
 *  - Other resources:
 *  -- http://api.jquery.com/jQuery.data/
 *  -- http://api.jquery.com/data/
 *  -- http://stackoverflow.com/questions/1923278/best-way-to-add-metadata-to-html-elements    
 */

(function($) {
  var Countdown = function(element, options) {
    // constants
    // - time-unit koefs (seconds, minutes to seconds, hours to seconds, ..., miliseconds to seconds)
    var MS = 1000, S = 1, M2S = S * 60, H2S = M2S * 60, D2S = H2S * 24, MS2S = S / MS;
    // - digits definition for display plugin (separator is used as class for indication of ':' character)
    // -- there is 9 digits/places - it means days (3 places), hours, minutes and seconds (for each 2 places) - from left to right
    // -- separator is wanted between days/hours, hours/minutes and minutes/seconds
    var SEP_CLASS = 'separator';
    var DIGIT_COUNT = 9;
    var DIGIT_CLASSES = {3: SEP_CLASS, 5: SEP_CLASS, 7: SEP_CLASS};
    // private fields and shortcuts
    var $elem = $(element);
    var id = $elem.attr('id');
    var me = this;
    var settings = $.extend({
      interval: 1000,
      immediately: true,
      timestamp: 0
    }, options || {});
    var started = false;
    var invalidated = false;
    var finalTime = null;
    var display = null;
    
    // public methods
    this.start = function() {
      if (!started) {
        start_internal(counter, settings.interval, false);
        started = true;
      }
    };
    
    this.stop = function() {
      if (started) {
        $.doTimeout(id);
        started = false;
      }
    };
    
    // private methods
    var counter = function() {
      var currentTime = new Date();
      var diffTime = new Date(finalTime - currentTime);
      var diffSecs = Math.floor(diffTime.valueOf() * MS2S);
      
      // invalid state
      if (diffSecs <= 0) {
        start_internal(blink, settings.interval / 2, true);
        
        return false;
      } else {
        var secs  = diffSecs % M2S;
        var mins  = Math.floor(diffSecs / M2S) % (H2S / M2S);
        var hours = Math.floor(diffSecs / H2S) % (D2S / H2S);
        var days  = Math.floor(diffSecs / D2S);
        
        var displayValue = ''.concat(zeroPad(days, 3), zeroPad(hours, 2), zeroPad(mins, 2), zeroPad(secs, 2));
        for (var i = 0; i < displayValue.length; i++) {
          display.setValue(i, displayValue[i]);
        }
        
        return true;
      }
    };
    
    var blink = function() {
      for (var i = 0; i < DIGIT_COUNT; i++) {
        display.setValue(i, (invalidated ? -1 : 0));
      }
      invalidated = !invalidated;
      
      return true;
    }
    
    var start_internal = function(fnc, interval, override) {
      $.doTimeout(id, interval, fnc, override);
      $.doTimeout(id, true); // starts immediately after call this method
    }
    
    var zeroPad = function(num, count) {
      var numZeropad = num + '';
      while (numZeropad.length < count) {
        numZeropad = '0' + numZeropad;
      }
      return numZeropad;
    }
    
    var initialize = function() {
      finalTime = new Date(settings.timestamp * MS);
      
      $elem.display({
        digitCount: DIGIT_COUNT,
        digitClasses: DIGIT_CLASSES
      });
      display = $elem.data('display');
      
      if (settings.immediately) {
        me.start();
      }
    }
    
    // initialize
    initialize();
  };

  $.fn.countdown = function(options) {
    return this.each(function() {
      var $elem = $(this);
      var key = 'countdown';
      var data = $elem.data(key);
      var instanceExists = false;
      
      if (typeof data !== 'undefined' && data instanceof Countdown) {
        instanceExists = true;
      } else if (typeof data === 'object') {
        // options from html data-element are merged together with options of plugin when initializing
        options = $.extend(data, options || {});
      }
      
      if (!instanceExists) {
        $elem.data(key, new Countdown(this, options));
      }
    });
  };
})(jQuery);
