/**
 * Keyboard shortcuts to navigate through a quiz
 *
 * @author Simon Willan <simon.willan@claromentis.com>
 */
(function () {
    'use strict';

    angular.module('cla.quiz')
        .directive('claQuizKeyboard', keyboard);

    //keyboard.$inject = ['$rootScope', '$scope'];

    /**
     *
     * @constructor
     */
    function keyboard() {
        return {
            controller: controller,
            restrict: 'E',
            scope: {
                bindTo: '@'
            }
        };

        function controller($rootScope, $scope) {
            var $ctl = this;

            var lookup = {
                _arrows: ['R', 37, 40],  // Arrows up, down, left, right
                _numbers: ['R', 49, 57], // Ranges 1 - 9
                _select: [13, 32],       // Enter and space
                _deselect: [27],         // Escape
                _review: 82,             // R
                _wasd: [65, 87, 68, 83]  // WASD alternative to arrows
            };

            var mappings = {};

            var bLookup = {
                _コナミ: [38, 38, 40, 40, 37, 39, 37, 39, 66, 65] // ???
            };
            var buffer = [];
            var timer = 0;

            this.$onInit = function() {
                init();
            };

            function init() {

                map();

                var $handle = $('#' + $scope.bindTo);
                $(document).off('keydown').on('keydown', $handle, function (event) {
                    var focusedElement = $(':focus');
                    if (focusedElement.length > 0) {
                        var elementType = focusedElement[0].nodeName;
                        if ((elementType === 'INPUT') ||
                            (elementType === 'TEXTAREA') ||
                            (elementType === 'SELECT')) {
                            // Don't track keys if we are in some kind of input element
                            return;
                        }
                    }
                    buffers(event.keyCode);
                    delegate(event.keyCode, event);
                    $scope.$apply();
                });
            }

            /**
             * map look up values to a mappings object for code delegation
             */
            function map() {
                for (var i in lookup) {
                    if (lookup[i].constructor === Array) {
                        if (lookup[i] && lookup[i][0] === 'R') {
                            // range
                            for (var j = lookup[i][1]; j <= lookup[i][2]; j++) {
                                mappings[j] = i;
                            }
                        } else {
                            for (var j in lookup[i]) {
                                mappings[lookup[i][j]] = i;
                            }
                        }
                    } else {
                        mappings[lookup[i]] = i;
                    }
                }
            }

            /**
             * Delegate key presses to a specific call.
             *
             * @param {int} code
             * @param {jQuery.Event} event
             */
            function delegate(code, event) {
                if (mappings[code]) {
                    var fn = $ctl.fns[mappings[code]];
                    if (typeof fn === 'function') {
                        fn.call($ctl.fns, code, event);
                    }
                }
            }

            /**
             * check buffered codes
             * @param code
             */
            function buffers(code) {
                buffer.push(code);

                if (!!timer && Date.now() - timer > 2000) {
                    bReset();
                    return;
                }
                timer = Date.now();

                for (var i in bLookup) {
                    if (JSON.stringify(buffer) === JSON.stringify(bLookup[i])) {
                        bReset();
                        var fn = $ctl.fns[i];
                        fn.call($ctl.fns, code);
                    }
                }
            }

            /**
             * reset buffer and timer
             */
            function bReset() {
                buffer.length = 0;
                timer = 0;
            }

            /**
             * Functions object.
             *
             * Handler functions for different key sets.
             */
            this.fns = {
                /**
                 * Navigate back, forth, up and down.
                 *
                 * @param {int} code
                 * @param {jQuery.Event} event
                 * @private
                 */
                _arrows: function (code, event) {
                    switch (code) {
                        case 37:
                            $rootScope.$broadcast('quiz.previous');
                            break;
                        case 39:
                            $rootScope.$broadcast('quiz.next');
                            break;
                        case 38:
                            $rootScope.$broadcast('quiz.highlight', {dir: -1});
                            break;
                        case 40:
                            $rootScope.$broadcast('quiz.highlight', {dir: +1});
                            break;
                    }
                    event.preventDefault();
                },

                /**
                 * Navigate directly to a question.
                 *
                 * @param {string} code
                 * @private
                 */
                _numbers: function (code) {
                    // assumes range given above
                    var index = (code - 4) % 9; // mod to retrieve 0 based index. -4 to bring code in line with mod divisible.
                    $rootScope.$broadcast('quiz.goto', {num: index});
                },

                /**
                 * Toggle selection of the highlighted question.
                 *
                 * @private
                 * @param {int} code
                 * @param {jQuery.Event} event
                 */
                _select: function (code, event) {
                    $rootScope.$broadcast('quiz.select');
                    event.preventDefault();
                },

                /**
                 * Remove the highlight from the currently highlighted question.
                 *
                 * @private
                 */
                _deselect: function () {
                    $rootScope.$broadcast('quiz.deselect');
                },

                /**
                 * Navigate to the review page.
                 *
                 * @private
                 */
                _review: function () {
                    $rootScope.$broadcast('quiz.review');
                },

                /**
                 * WASD alternative to arrow keys.
                 *
                 * @param {string} code
                 * @param {jQuery.Event} event
                 * @private
                 */
                _wasd: function (code, event) {
                    var map = {65: 37, 87: 38, 68: 39, 83: 40};
                    this._arrows(map[code], event);
                },

                /**
                 * ???
                 * @private
                 */
                _コナミ: function () {
                    window.confetti.start();
                }
            }

            // ToDo: Remove these lines when a Claro >= 8.10 compatible branch of this module is available
            if (angular.version.major === 1 && angular.version.minor < 5) {
                this.$onInit();
            }
        }
    }
}());
