(function () {

var idPrefix = 'quiz-';

var DATA_CONTROLLER = idPrefix + 'controller';

var DEFAULT_OPTIONS = {
    name:'quiz',
    path:null,
    html:'template.html',
    scoreFormat:function (total, correct) {
        return correct + '/' + total;
    },
    progressFormat:function (total, correct) {
        return correct + '/' + total;
    }
};

function Controller(target, options) {
    var self = this;

    this.target = target;
    this.options = $.extend({}, DEFAULT_OPTIONS, options);
    this.totalQuestions = undefined;
    this.currentQuestion = undefined;
    this.answers = null;

    this.target.load(this.options.path + this.options.html, function () {
        self._init1();
    });
}

Controller.prototype._init1 = function () {
    var self = this;

    this.scrWelcome = this.target.find('[tag=welcome]');
    this.ctlStart = this.scrWelcome.find('[tag=start]');
    this.scrQuestions = this.target.find('[tag=questions]');
    this.questions = this.scrQuestions.find('> ul > li');
    this.scrResults = this.target.find('[tag=results]');
    this.ctlScore = this.scrResults.find('[tag=score]');
    this.divAnswers = this.scrResults.find('[tag=answers]');
    this.correctAnswerTemplate = this.divAnswers.find('[tag=template-correct]').remove();
    this.wrongAnswerTemplate = this.divAnswers.find('[tag=template-wrong]').remove();

    this.ctlStart.bind('click', function () {
        self.start();
    });

    this.questions.find('ul:last').each(function (i, ul) {
        $(this).find('> li').each(function (index) {
            $(this).bind('click', function () {
                if (typeof self.answers[self.currentQuestion] === 'number') {
                    return;
                }

                self.answers[self.currentQuestion] = index;

                self.answered(
                    index,
                    $(this).attr('tag') === 'correct'
                );
            });
        });
    });

    this.questions.find('blockquote:last, [tag=instant]').hide();

    this.totalQuestions = this.questions.length;

    this.init();
};

Controller.prototype.dispatch = function (method, arg0, arg1, arg2) {
    return this[method](arg0, arg1, arg2);
};

Controller.prototype.countCorrectAnswers = function () {
    var result = 0, self = this;

    $.each(this.answers, function (i, v) {
        if ($(self.questions.get(i)).find('ul:last > li:eq(' + v + ')').attr('tag') === 'correct') {
            result++;
        }
    });

    return result;
};

Controller.prototype.serializeState = function () {
    return (this.answers && this.answers.length ? this.answers : [ '-' ]).join(',');
};

Controller.prototype.unserializeState = function (str) {
    var tmp = [], self = this;

    if (str !== '-') {
        $.each(String(str).split(','), function (i, v) {
            v = parseInt(v, 10);
            if (i < self.totalQuestions && v >= 0 && v < $(self.questions.get(i)).find('ul:last > li').length) {
                tmp.push(v);
            } else {
                tmp = null;
                return false;
            }
        });
    }

    return tmp;
};

Controller.prototype.storeState = function () {
    this.setCookie(this.serializeState());
};

Controller.prototype.restoreState = function () {
    var tmp = this.unserializeState(this.getCookie());

    if (tmp) {
        this.answers = tmp;
        this.currentQuestion = tmp.length;
        return true;
    } else {
        return false;
    }
};

Controller.prototype.clearStoredState = function () {
    this.deleteCookie();
};

Controller.prototype.cookiesEnabled = function () {
    return typeof this.options.name === 'string';
};

Controller.prototype.getCookie = function () {
    return this.cookiesEnabled() ? $.jCookie(this.options.name) : null;
};

Controller.prototype.setCookie = function (value) {
    if (this.cookiesEnabled()) {
        $.jCookie(this.options.name, value, 7);
    }
};

Controller.prototype.deleteCookie = function (value) {
    if (this.cookiesEnabled()) {
        $.jCookie(this.options.name, null);
    }
};

Controller.prototype.init = function () {
    this.scrWelcome.show();
    this.scrQuestions.hide();
    this.scrResults.hide();
    this.currentQuestion = -1;
    this.answers = [];

    if (this.restoreState()) {
        this.scrWelcome.hide();
        this.questions.hide();
        this.scrQuestions.show();
        this._showQuestion(this.currentQuestion);
    }
};

Controller.prototype.renderLevel = function (selector) {
    $(selector).find('[tag=level]').find('[tag=pointer]').css('height', (this.countCorrectAnswers() / this.totalQuestions * 100) + '%');
};

Controller.prototype.renderProgress = function (selector) {
    $(selector).find('[tag=progress]').html(this.options.progressFormat(this.totalQuestions, this.currentQuestion + 1));
};

Controller.prototype.start = function () {
    this.scrWelcome.slideUp();
    this.questions.hide();
    this.scrQuestions.show();
    this.next();
};

Controller.prototype._showQuestion = function (index, animated) {
    this.questions.hide();

    if (this.questions[this.currentQuestion]) {
        $(this.questions[this.currentQuestion])[animated ? 'slideDown' : 'show']();
        this.renderProgress(this.scrQuestions);
        this.renderLevel(this.scrQuestions);
    } else {
        this.finish();
    }
};

Controller.prototype.answered = function (index, correct) {
    var self = this,
        ashdan = $(this.questions[this.currentQuestion]).find('[tag=asher]');
        options = $(this.questions[this.currentQuestion]).find('ul:last'),
        immediate = $(this.questions[this.currentQuestion]).find('[tag=instant]');

    if (!immediate.length) {
        this.next();
    } else {
        ashdan.slideUp();
        if (correct) {
            immediate.find('[tag=correct]').show();
            immediate.find('[tag=wrong]').hide();
        } else {
            immediate.find('[tag=correct]').hide();
            immediate.find('[tag=wrong]').show();
        }
        options.slideUp();
        immediate.slideDown();
        immediate.find('[tag=next]').click(function () {
            self.next();
        });
    }
};

Controller.prototype.next = function () {
    $(this.questions[this.currentQuestion]).slideUp();

    this.currentQuestion++;

    this.storeState();

    this._showQuestion(this.currentQuestion, true);
};

Controller.prototype.finish = function () {
    var self = this, percentage = Math.round(this.countCorrectAnswers() / this.totalQuestions * 100);

    this.ctlScore.html(this.options.scoreFormat(this.totalQuestions, this.countCorrectAnswers()));
    this.scrResults.find('[tag=conditional-score]').each(function () {
        if (String($(this).attr('rel')).match(/^(\d+)-(\d+)$/) && percentage >= parseInt(RegExp.$1, 10) && percentage <= parseInt(RegExp.$2, 10)) {
            $(this).show();
        } else {
            $(this).hide();
        }
    });
    this.renderLevel(this.scrResults);
    this.divAnswers.empty();
    this.questions.each(function (index) {
        var copy = (
            $($(this).find('ul:last > li').get(self.answers[index])).attr('tag') === 'correct' ?
            self.correctAnswerTemplate :
            self.wrongAnswerTemplate
        ).clone(true).appendTo(self.divAnswers);

        copy.find('[tag=question]')
            .html($(this).html())
            .find('ul:last, blockquote:last, [tag=instant]').remove();

        copy.find('[tag=yourAnswer]')
            .html($($(this).find('ul:last > li').get(self.answers[index])).html());

        copy.find('[tag=correctAnswer]')
            .html($(this).find('ul:last > li[tag=correct]').html());

        copy.find('[tag=comment]')
            .html($(this).find('blockquote:last').html());
    });
    this.scrQuestions.slideUp();
    this.scrResults.slideDown();

    this.clearStoredState();
};

jQuery.fn.quiz = function () {
    if ($(this).data(DATA_CONTROLLER)) {
        return $(this).data(DATA_CONTROLLER).dispatch.apply($(this).data(DATA_CONTROLLER), arguments);
    } else {
        return $(this).data(DATA_CONTROLLER, new Controller(this, arguments[0]));
    }
};

}());

