(function (factory) {
    if (typeof define === "function" && define.amd) {
        // AMD. Register as anonymous module.
        define(["jquery"], factory);
    } else {
        // Browser globals.
        factory(jQuery);
    }
})(function ($) {

    "use strict";

    var Countdown = function (element, options) {
            this.$element = $(element);
            this.defaults = $.extend({}, Countdown.defaults, this.$element.data(), $.isPlainObject(options) ? options : {});
            this.init();
        };

    Countdown.prototype = {
        constructor: Countdown,

        init: function () {
            var content = this.$element.html(),
                date = new Date(this.defaults.date || content);

            if (date.getTime()) {
                this.content = content;
                this.date = date;
                this.find();

                if (this.defaults.autoStart) {
                    this.start();
                }
            }
        },

        find: function () {
            var $element = this.$element;

            this.$days = $element.find("[data-days]");
            this.$hours = $element.find("[data-hours]");
            this.$minutes = $element.find("[data-minutes]");
            this.$seconds = $element.find("[data-seconds]");

            if ((this.$days.length + this.$hours.length + this.$minutes.length + this.$seconds.length) > 0) {
                this.found = true;
            }
        },

        reset: function () {
            if (this.found) {
                this.output("days");
                this.output("hours");
                this.output("minutes");
                this.output("seconds");
            } else {
                this.output();
            }
        },

        ready: function () {
            var date = this.date,
                decisecond = 100,
                second = 1000,
                minute = 60000,
                hour = 3600000,
                day = 86400000,
                remainder = {},
                diff;

            if (!date) {
                return false;
            }

            diff = date.getTime() - (new Date()).getTime();

            if (diff <= 0) {
                this.end();
                return false;
            }

            remainder.days = diff;
            remainder.hours = remainder.days % day;
            remainder.minutes = remainder.hours % hour;
            remainder.seconds = remainder.minutes % minute;
            remainder.milliseconds = remainder.seconds % second;

            this.days = Math.floor(remainder.days / day);
            this.hours = Math.floor(remainder.hours / hour);
            this.minutes = Math.floor(remainder.minutes / minute);
            this.seconds = Math.floor(remainder.seconds / second);
            this.deciseconds = Math.floor(remainder.milliseconds / decisecond);

            return true;
        },

        start: function () {
            if (!this.active && this.ready()) {
                this.active = true;
                this.reset();
                this.autoUpdate = this.defaults.fast ?
                    setInterval($.proxy(this.fastUpdate, this), 100) :
                    setInterval($.proxy(this.update, this), 1000);
            }
        },

        stop: function () {
            if (this.active) {
                this.active = false;
                clearInterval(this.autoUpdate);
            }
        },

        end: function () {
            if (!this.date) {
                return;
            }

            this.stop();

            this.days = 0;
            this.hours = 0;
            this.minutes = 0;
            this.seconds = 0;
            this.deciseconds = 0;
            this.reset();
            this.defaults.end();
        },

        destroy: function () {
            if (!this.date) {
                return;
            }

            this.stop();

            this.$days = null;
            this.$hours = null;
            this.$minutes = null;
            this.$seconds = null;

            this.$element.empty().html(this.content);
            this.$element.removeData("countdown");
        },

        fastUpdate: function () {
            if (--this.deciseconds >= 0) {
                this.output("deciseconds");
            } else {
                this.deciseconds = 9;
                this.update();
            }
        },

        update: function () {
            if (--this.seconds >= 0) {
                this.output("seconds");
            } else {
                this.seconds = 59;

                if (--this.minutes >= 0) {
                    this.output("minutes");
                } else {
                    this.minutes = 59;

                    if (--this.hours >= 0) {
                        this.output("hours");
                    } else {
                        this.hours = 23;

                        if (--this.days >= 0) {
                            this.output("days");
                        } else {
                            this.end();
                        }
                    }
                }
            }
        },

        output: function (type) {
            if (!this.found) {
                this.$element.empty().html(this.template());
                return;
            }

            switch (type) {
                case "deciseconds":
                    this.$seconds.text(this.getSecondsText());
                    break;

                case "seconds":
                    this.$seconds.text(this.seconds);
                    break;

                case "minutes":
                    this.$minutes.text(this.minutes);
                    break;

                case "hours":
                    this.$hours.text(this.hours);
                    break;

                case "days":
                    this.$days.text(this.days);
                    break;

                // No default
            }
        },

        template: function () {
            return this.defaults.text
                    .replace("%s", this.days)
                    .replace("%s", this.hours)
                    .replace("%s", this.minutes)
                    .replace("%s", this.getSecondsText());
        },

        getSecondsText: function () {
            return this.active && this.defaults.fast ? (this.seconds + "." + this.deciseconds) : this.seconds;
        }
    };

    // Default settings
    Countdown.defaults = {
        autoStart: true,
        date: null,
        fast: false,
        end: $.noop,
        text: "%s days, %s hours, %s minutes, %s seconds"
    };

    // Set default settings
    Countdown.setDefaults = function (options) {
        $.extend(Countdown.defaults, options);
    };

    // Register as jQuery plugin
    $.fn.countdown = function (options) {
        return this.each(function () {
            var $this = $(this),
                data = $this.data("countdown");

            if (!data) {
                $this.data("countdown", (data = new Countdown(this, options)));
            }

            if (typeof options === "string" && $.isFunction(data[options])) {
                data[options]();
            }
        });
    };

    $.fn.countdown.constructor = Countdown;
    $.fn.countdown.setDefaults = Countdown.setDefaults;

    $(function () {
        $("[countdown]").countdown();
    });

});
