/**
 * Controls the taking of a quiz
 *
 * @author Simon Willan <simon.willan@claromentis.com>
 * @author Chris Andrew <chris.andrew@claromentis.com>
 */
(function (window) {
    'use strict';

    angular.module('cla.quiz')
        .controller('startController', startController);

    startController.$inject = ['$scope', '$window', 'quizTracker', 'quizActions', 'quizRedirect'];

    /**
     *
     * @param $scope
     * @param $window
     * @param quizTracker
     * @param quizActions
     * @constructor
     */
    function startController($scope, $window, quizTracker, quizActions) {
        var questionContainer;

        $scope.question = null; // current question object
        $scope.optionIndexes = null; // map of option indexes to option IDs

        $scope.toggleOption = toggleOption;
        $scope.isOptionHighlighted = isOptionHighlighted;
        $scope.isOptionSelected = isOptionSelected;
        $scope.getOptionLetter = getOptionLetter;
        $scope.localise = localise;
        $scope.skip = skip;
        $scope.next = next;
        $scope.isSkipHidden = isSkipHidden;
        $scope.isNextDisabled = isNextDisabled;

        $scope.tracker = quizTracker;

        $scope.quiz_id = $window.quiz_preload.quiz.id;
        $scope.preview = $window.quiz_preload.preview;

        init();

        /**
         * initialise controller, with routing
         */
        function init() {
            angular.element("#quiz-wrap").hide();
            questionContainer = angular.element('#questions');

            $scope.$on('quiz.highlight', onHighlight);
            $scope.$on('quiz.select', onSelect);
            $scope.$on('quiz.deselect', onDeselect);

            start();
        }

        /**
         * load quiz start
         */
        function start() {
            quizActions.setPreview($scope.preview);
            quizActions.start($scope.quiz_id, function () {
                $scope.question = quizTracker.currentQuestion;
                $scope.optionIndexes = quizTracker.optionIndexes;
                angular.element("#quiz-wrap").show();
            });
        }

        /**
         * Set a selected option.
         *
         * Time taken is not tracked until we move to the next question, so this is set to null.
         *
         * @param {int} option_id
         */
        function toggleOption(option_id) {
            quizTracker.toggleOption(option_id, null);
        }

        /**
         * Check whether the given option highlighted by the user.
         *
         * @param {int} option_id
         * @return {boolean}
         */
        function isOptionHighlighted(option_id) {
            return getOptionIndex(option_id) === quizTracker.highlightedIndex;
        }

        /**
         * Check whether the given option selected by the user.
         *
         * @param {int} option_id
         * @returns {boolean}
         */
        function isOptionSelected(option_id) {
            return quizTracker.isOptionChosen(quizTracker.question, option_id);
        }

        /**
         * Get capital letter denoting option
         * todo: what happens at the end of the alphabet?
         * @param index used as increment for char code
         * @returns {string}
         */
        function getOptionLetter(index) {
            return String.fromCharCode(65 + index);
        }

        /**
         * localisation
         * @returns {string}
         */
        function localise() {
            return String.localise.apply(this, arguments);
        }

        /**
         * broadcast to nav, so skip the question
         */
        function skip() {
            $scope.$broadcast('quiz.skip');
        }

        /**
         * broadcast to nav. next question and save
         */
        function next() {
            $scope.$broadcast('quiz.next');
        }

        /**
         * Highlight option event listener.
         *
         * This event is currently fired by the keyboard directive only.
         *
         * @listens quiz.select
         * @param {object} e
         * @param {object} args
         */
        function onHighlight(e, args) {
            var dir = args.dir;
            var index = null;

            // Handle input when nothing is currently highlighted
            if (quizTracker.highlightedIndex === null) {
                if (dir === -1) {
                    // Highlight the bottom answer
                    index = getOptionsCount() - 1;
                } else {
                    // Highlight the top answer
                    index = 0;
                }
            } else {
                index = quizTracker.highlightedIndex + sign(dir);
            }

            // Clamp and update and the highlighted index
            index = positiveModulo(index, getOptionsCount());
            quizTracker.highlightedIndex = index;
        }

        /**
         * Select option event listener.
         *
         * This event is currently fired by the keyboard directive only.
         *
         * @listens quiz.select
         * @param {object} e
         * @param {object} args
         */
        function onSelect(e, args) {
            var optionIndexes = quizTracker.optionIndexes[quizTracker.currentQuestion.id];
            var optionId = optionIndexes[quizTracker.highlightedIndex];

            toggleOption(optionId);
        }

        /**
         * Deselect option event listener.
         *
         * Unhighlights the highlighted option.
         *
         * @param {object} e
         * @param {object} args
         */
        function onDeselect(e, args) {
            quizTracker.highlightedIndex = null;
        }

        /**
         * Checks if the skip button should be hidden or not.
         *
         * @returns {boolean}
         */
        function isSkipHidden() {
            return (quizTracker.trackpos == quizTracker.count - 1);
        }

        /**
         * Checks if the next button should be disabled or not.
         *
         * @returns {boolean}
         */
        function isNextDisabled() {
            return !quizTracker.chosen[quizTracker.question];
        }

        /**
         * Get the index of a given option belonging to the current question.
         *
         * @param {int} option_id
         * @return {int} The option index
         */
        function getOptionIndex(option_id) {
            var optionIds = quizTracker.optionIndexes[quizTracker.currentQuestion.id];

            return optionIds.indexOf(option_id);
        }

        /**
         * Get the options count for the current question.
         *
         * @private
         * @returns {number} The number of options for the current question.
         */
        function getOptionsCount() {
            return quizTracker.getOptionsCount(quizTracker.question);
        }

        /**
         * Calculate the positive modulo of `i % n`.
         *
         * @param {int} i - Operand
         * @param {int} n - Divisor
         * @returns {number} - Positive number
         */
        function positiveModulo(i, n) {
            return (i % n + n) % n;
        }

        /**
         * Calculate the sign of a number (-1, 0, or 1).
         *
         * Polyfill for `Math.sign()`.
         *
         * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign#Polyfill
         * @param {int} n - Number
         * @returns {number} - Sign
         */
        function sign(n) {
            return (n > 0) - (n < 0) || +n;
        }
    }
}(window));
