/**
 * This services tracks the status of the poll, the chosen options and sets the attempt
 * answers if the page is reloaded if the user visits the poll without having submitted it.
 */
(function () {
    'use strict';

    angular.module('cla.poll')
        .service('pollTracker', pollTracker);

    /**
     *
     * @constructor
     */
    function pollTracker() {
        var service = {
            options: {                // tracker options
                showHelpText: false,  // show single or multiple answer help text
                shuffleOptions: false // shuffle question options
            },

            questions: [],          // all questions in this poll
            optionIndexes: {},      // dictionary of question option indexes
            question: 0,		    // current question index
            currentQuestion: {},    // current question object
            count: 0,

            timer: 0,			    // question timer
            chosen: {},			    // what the user has chosen
            loaded: false,		    // tracker initialised?
            highlightedIndex: null, // highlighted option index (for keyboard navigation and selection)

            tracker: [],            // track array for question tracking (question indices)
            trackpos: 0,            // current position of tracker index

            results: null,          // results objects
            can_retake: null,       // whether the poll can be attempted again
            attempt_id: null,       // the current attempt id
            is_retaking: true,      // user is retaking the poll

                               init: init,
                             update: update,
                          setOption: setOption,
                         setTracker: setTracker,
                       toggleOption: toggleOption,
                       resetTracker: resetTracker,
                     isOptionChosen: isOptionChosen,
                 buildOptionIndexes: buildOptionIndexes,
            firstUnansweredQuestion: firstUnansweredQuestion
        };
        return service;

        /**
         * initialise tracker
         * @param {object[]} questions
         * @param {object[]} answers

         */
        function init(questions, answers) {
            // skip initialisation if we have already loaded the data
            if (service.loaded)
                return;

            reset();

            service.questions = angular.extend(service.questions, questions);
            service.options = angular.extend(service.options,{});

            // set tracker
            resetTracker();

            // set prefilled choices if choices are given
            setAnswers(answers);

            // poll started, timer set time
            service.timer = Date.now();
            service.loaded = true;

            // set up question option indexes
            service.buildOptionIndexes();
        }

        /**
         * Set any preloaded user answers.
         *
         * Usually the result of continuing a suspended poll.
         *
         * @param {object} answers
         */
        function setAnswers(answers) {
            // have we been given a choice? #deep
            if (answers && answers.constructor !== Array) {
                var i;

                // create a dictionary with question ids as key, and their index as a value
                var questionIndexMap = {};
                for (i in service.questions) {
                    questionIndexMap[service.questions[i].id] = i
                }

                // for every choice...
                for (i in answers) {
                    // temporarily set active question
                    service.question = parseInt(questionIndexMap[i]);

                    // check properties
                    var properties = 'properties' in answers[i] ? answers[i].properties : null;

                    var response = [];
                    if (properties && 'response' in answers[i].properties)
                        response = answers[i].properties.response;

                    var time_taken = null;
                    if (properties && 'time_taken' in answers[i].properties)
                        time_taken = answers[i].properties.time_taken;

                    // get the users responses
                    var responseKeys = [];
                    for (var j in response) {
                        responseKeys.push(parseInt(j));
                    }

                    // set the option
                    service.setOption(responseKeys, parseInt(time_taken));
                    service.chosen[service.question].submitted = true;
                }

                service.question = 0;
            }
        }



        /**
         * Get the first unanswered question for the current Quiz attempt.
         *
         * @returns {number}
         */
        function firstUnansweredQuestion() {
            for (var i = 0; i < service.questions.length; i++) {
                if (!service.chosen[i]) {
                    return i;
                }
            }
            return (service.questions.length - 1);
        }

        /**
         * Update the current question view.
         */
        function update() {
            // angular extend used to update the existing object rather than overwrite entirely
            service.currentQuestion = angular.extend(service.currentQuestion, service.questions[service.question]);

            // reset timer whenever there is an object update (change in question)
            service.timer = Date.now();
            service.highlightedIndex = null;
        }

        /**
         * Build the option index arrays for each question.
         *
         * This facilitates shuffling answer order, if enabled, and is used in
         * the template to iterate over the questions in that order.
         */
        function buildOptionIndexes() {
            var i, j, question, questions = service.questions;

            for (i = 0; i < questions.length; i++) {
                question = questions[i];

                // Build the dictionary of options per question
                if (!service.optionIndexes[question.id]) {
                    service.optionIndexes[question.id] = [];
                }

                for (j in question.options) {
                    if (question.options.hasOwnProperty(j)) {
                        service.optionIndexes[question.id].push(parseInt(j));
                    }
                }

                // Shuffle the options if configured
                if (service.options.shuffleOptions) {
                    service.optionIndexes[question.id] = _.shuffle(service.optionIndexes[question.id]);
                }
            }
        }

        /**
         * Set the selected option for the current question.
         *
         * @param {int[]} option_ids
         * @param {int} selected_time
         */
        function setOption(option_ids, selected_time) {
            service.chosen[service.question] = {
                options: option_ids,
                time_taken: selected_time,
                submitted: false
            };
        }

        /**
         * Toggle the selection of an option for the current question.
         *
         * @param {int} option_id
         * @param {int} selected_time
         */
        function toggleOption(option_id, selected_time) {

            // If an answer hasn't been created yet, create it and return
            if (!service.chosen[service.question]) {
                service.setOption([option_id], selected_time);
                return;
            }

            // Update the answer
            var answer = service.chosen[service.question];
            var answerOptionIndex = answer.options.indexOf(option_id);

            if (answerOptionIndex >= 0) {
                // If the option has already been chosen, we will remove it
                // from the selection
                answer.options.splice(answerOptionIndex, 1);
            } else {
                    // Otherwise we only allow one option to be chosen
                    answer.options = [option_id];
            }

            answer.time_taken = selected_time;
        }

        /**
         * Check whether the given option ID is chosen for a question index.
         *
         * @param {int} question_index - Index of the question to check
         * @param {int} option_id - Option ID to check
         * @returns {boolean}
         */
        function isOptionChosen(question_index, option_id) {
            var i, answer = service.chosen[question_index];

            if (answer) {
                for (i in answer.options) {
                    if (answer.options[i] == option_id) {
                        return true;
                    }
                }
            }

            return false;
        }



        /**
         * reset any initial vars back to their default states without breaking references.
         */
        function reset() {
            // service.optionIndexes = [];
            service.questions.length = 0;

            service.highlightedIndex = null;
            service.currentQuestion = {};
            service.chosen = {};

            // reset current question to 0
            service.question = 0;
        }



        /**
         * fill a track with indexes
         */
        function setTracker(array) {
            service.tracker = [];
            service.trackpos = 0;
            for (var i = 0; i < array.length; i++) {
                service.tracker.push(parseInt(array[i]));
            }
            service.count = service.tracker.length;
        }

        /**
         * reset tracker to question list
         */
        function resetTracker() {

            // set tracking array
            var tracking = [];
            for (var i = 0; i < service.questions.length; i++) {
                tracking.push(i);
            }
            service.setTracker(tracking);
            service.question = 0;
            service.update();
        }
    }
}());
