(function() {
    'use strict';
    describe('Video Context Menu', function() {
        var state, openMenu, keyPressEvent, openSubmenuMouse, openSubmenuKeyboard, closeSubmenuMouse,
            closeSubmenuKeyboard, menu, menuItems, menuSubmenuItem, submenu, submenuItems, overlay, $playButton;

        openMenu = function() {
            var $container = $('.video');
            $container.find('video').trigger('contextmenu');
            menu = $container.children('.contextmenu');
            menuItems = menu.children('.menu-item').not('.submenu-item');
            menuSubmenuItem = menu.children('.menu-item.submenu-item');
            submenu = menuSubmenuItem.children('.submenu');
            submenuItems = submenu.children('.menu-item');
            overlay = $container.children('.overlay');
            $playButton = $('.video_control.play');
        };

        keyPressEvent = function(key) {
            return $.Event('keydown', {keyCode: key});
        };

        openSubmenuMouse = function(menuSubmenuItem) {
            menuSubmenuItem.mouseover();
            jasmine.clock().tick(200);
            expect(menuSubmenuItem).toHaveClass('is-opened');
        };

        openSubmenuKeyboard = function(menuSubmenuItem, keyCode) {
            menuSubmenuItem.focus().trigger(keyPressEvent(keyCode || $.ui.keyCode.RIGHT));
            expect(menuSubmenuItem).toHaveClass('is-opened');
            expect(menuSubmenuItem.children().last().children().first()).toBeFocused();
        };

        closeSubmenuMouse = function(menuSubmenuItem) {
            menuSubmenuItem.mouseleave();
            jasmine.clock().tick(200);
            expect(menuSubmenuItem).not.toHaveClass('is-opened');
        };

        closeSubmenuKeyboard = function(menuSubmenuItem) {
            menuSubmenuItem.children().first().focus().trigger(keyPressEvent($.ui.keyCode.LEFT));
            expect(menuSubmenuItem).not.toHaveClass('is-opened');
            expect(menuSubmenuItem).toBeFocused();
        };

        beforeEach(function() {
            jasmine.clock().install();
            // $.cookie is mocked, make sure we have a state with an unmuted volume.
            $.cookie.and.returnValue('100');
            jasmine.addMatchers({
                toHaveCorrectLabels: function() {
                    return {
                        compare: function(actual, labelsList) {
                            return {
                                pass: _.difference(labelsList, _.map(actual, function(item) {
                                    return $(item).text();
                                })).length === 0
                            };
                        }
                    };
                }
            });
        });

        afterEach(function() {
            $('source').remove();
            _.result(state.storage, 'clear');
            _.result($('video').data('contextmenu'), 'destroy');
            _.result(state.videoPlayer, 'destroy');
            jasmine.clock().uninstall();
        });

        describe('constructor', function() {
            it('the structure should be created on first `contextmenu` call', function() {
                state = jasmine.initializePlayer();
                expect(menu).not.toExist();
                openMenu();
                /*
                  Make sure we have the expected HTML structure:
                   - Play (Pause)
                   - Mute (Unmute)
                   - Fill browser (Exit full browser)
                   - Speed >
                             - 0.75x
                             - 1.0x
                             - 1.25x
                             - 1.50x
                */

                // Only one context menu per video container
                expect(menu).toBeInDOM();
                expect(menu).toHaveClass('is-opened');
                expect(menuItems).toHaveCorrectLabels(['Play', 'Mute', 'Fill browser']);
                expect(menuSubmenuItem.children('span')).toHaveText('Speed');
                expect(submenuItems).toHaveCorrectLabels(['0.75x', '1.0x', '1.25x', '1.50x']);
                // Check that one of the speed submenu item is selected
                expect(_.size(submenuItems.filter('.is-selected'))).toBe(1);
            });

            it('add ARIA attributes to menu, menu items, submenu and submenu items', function() {
                state = jasmine.initializePlayer();
                openMenu();
                // Menu and its items.
                expect(menu).toHaveAttr('role', 'menu');
                menuItems.each(function() {
                    expect($(this)).toHaveAttrs({
                        'aria-selected': 'false',
                        role: 'menuitem'
                    });
                });

                expect(menuSubmenuItem).toHaveAttrs({
                    'aria-expanded': 'false',
                    'aria-haspopup': 'true',
                    role: 'menuitem'
                });

                // Submenu and its items.
                expect(submenu).toHaveAttr('role', 'menu');
                submenuItems.each(function() {
                    expect($(this)).toHaveAttr('role', 'menuitem');
                    expect($(this)).toHaveAttr('aria-selected');
                });
            });

            it('is not used by Youtube type of video player', function() {
                state = jasmine.initializePlayer('video.html');
                expect($('video, iframe')).not.toHaveData('contextmenu');
            });
        });

        describe('methods:', function() {
            beforeEach(function() {
                state = jasmine.initializePlayer();
                openMenu();
            });

            it('menu can be destroyed successfully', function() {
                var menuitemEvents = ['click', 'keydown', 'contextmenu', 'mouseover'],
                    menuEvents = ['keydown', 'contextmenu', 'mouseleave', 'mouseover'];

                menu.data('menu').destroy();
                expect(menu).not.toBeInDOM();
                expect(overlay).not.toBeInDOM();
                _.each(menuitemEvents, function(eventName) {
                    expect(menuItems.first()).not.toHandle(eventName);
                });
                _.each(menuEvents, function(eventName) {
                    expect(menuSubmenuItem).not.toHandle(eventName);
                });
                _.each(menuEvents, function(eventName) {
                    expect(menu).not.toHandle(eventName);
                });
                expect($('video')).not.toHandle('contextmenu');
                expect($('video')).not.toHaveData('contextmenu');
            });

            it('can change label for the submenu', function() {
                expect(menuSubmenuItem.children('span')).toHaveText('Speed');
                menuSubmenuItem.data('menu').setLabel('New Name');
                expect(menuSubmenuItem.children('span')).toHaveText('New Name');
            });

            it('can change label for the menuitem', function() {
                expect(menuItems.first()).toHaveText('Play');
                menuItems.first().data('menu').setLabel('Pause');
                expect(menuItems.first()).toHaveText('Pause');
            });
        });

        describe('when video is right-clicked', function() {
            beforeEach(function() {
                state = jasmine.initializePlayer();
                openMenu();
            });

            it('context menu opens', function() {
                expect(menu).toHaveClass('is-opened');
                expect(overlay).toBeInDOM();
            });

            it('mouseover and mouseleave behave as expected', function() {
                openSubmenuMouse(menuSubmenuItem);
                expect(menuSubmenuItem).toHaveClass('is-opened');
                closeSubmenuMouse(menuSubmenuItem);
                expect(menuSubmenuItem).not.toHaveClass('is-opened');
                submenuItems.eq(1).mouseover();
                expect(submenuItems.eq(1)).toBeFocused();
            });

            it('mouse left-clicking outside of the context menu will close it', function() {
                // Left-click outside of open menu, for example on Play button
                $playButton.click();
                expect(menu).not.toHaveClass('is-opened');
                expect(overlay).not.toBeInDOM();
            });

            it('mouse right-clicking outside of video will close it', function() {
                // Right-click outside of open menu for example on Play button
                $playButton.trigger('contextmenu');
                expect(menu).not.toHaveClass('is-opened');
                expect(overlay).not.toBeInDOM();
            });

            it('mouse right-clicking inside video but outside of context menu will not close it', function() {
                spyOn(menu.data('menu'), 'pointInContainerBox').and.returnValue(true);
                overlay.trigger('contextmenu');
                expect(menu).toHaveClass('is-opened');
                expect(overlay).toBeInDOM();
            });

            it('mouse right-clicking inside video but outside of context menu will close submenus', function() {
                spyOn(menu.data('menu'), 'pointInContainerBox').and.returnValue(true);
                openSubmenuMouse(menuSubmenuItem);
                expect(menuSubmenuItem).toHaveClass('is-opened');
                overlay.trigger('contextmenu');
                expect(menuSubmenuItem).not.toHaveClass('is-opened');
            });

            it('mouse left/right-clicking behaves as expected on play/pause menu item', function() {
                var menuItem = menuItems.first();
                spyOn(state.videoPlayer, 'isPlaying');
                spyOn(state.videoPlayer, 'play').and.callFake(function() {
                    state.videoPlayer.isPlaying.and.returnValue(true);
                    state.el.trigger('play');
                });
                spyOn(state.videoPlayer, 'pause').and.callFake(function() {
                    state.videoPlayer.isPlaying.and.returnValue(false);
                    state.el.trigger('pause');
                });
                // Left-click on play
                menuItem.click();
                expect(state.videoPlayer.play).toHaveBeenCalled();
                expect(menuItem).toHaveText('Pause');
                openMenu();
                // Left-click on pause
                menuItem.click();
                expect(state.videoPlayer.pause).toHaveBeenCalled();
                expect(menuItem).toHaveText('Play');
                state.videoPlayer.play.calls.reset();
                // Right-click on play
                menuItem.trigger('contextmenu');
                expect(state.videoPlayer.play).toHaveBeenCalled();
                expect(menuItem).toHaveText('Pause');
            });

            it('mouse left/right-clicking behaves as expected on mute/unmute menu item', function() {
                var menuItem = menuItems.eq(1);
                // Left-click on mute
                menuItem.click();
                expect(state.videoVolumeControl.getMuteStatus()).toBe(true);
                expect(menuItem).toHaveText('Unmute');
                openMenu();
                // Left-click on unmute
                menuItem.click();
                expect(state.videoVolumeControl.getMuteStatus()).toBe(false);
                expect(menuItem).toHaveText('Mute');
                // Right-click on mute
                menuItem.trigger('contextmenu');
                expect(state.videoVolumeControl.getMuteStatus()).toBe(true);
                expect(menuItem).toHaveText('Unmute');
                openMenu();
                // Right-click on unmute
                menuItem.trigger('contextmenu');
                expect(state.videoVolumeControl.getMuteStatus()).toBe(false);
                expect(menuItem).toHaveText('Mute');
            });

            it('mouse left/right-clicking behaves as expected on go to Exit full browser menu item', function() {
                var menuItem = menuItems.eq(2);
                // Left-click on Fill browser
                menuItem.click();
                expect(state.isFullScreen).toBe(true);
                expect(menuItem).toHaveText('Exit full browser');
                openMenu();
                // Left-click on Exit full browser
                menuItem.click();
                expect(state.isFullScreen).toBe(false);
                expect(menuItem).toHaveText('Fill browser');
                // Right-click on Fill browser
                menuItem.trigger('contextmenu');
                expect(state.isFullScreen).toBe(true);
                expect(menuItem).toHaveText('Exit full browser');
                openMenu();
                // Right-click on Exit full browser
                menuItem.trigger('contextmenu');
                expect(state.isFullScreen).toBe(false);
                expect(menuItem).toHaveText('Fill browser');
            });

            it('mouse left/right-clicking behaves as expected on speed submenu item', function() {
                // Set speed to 0.75x
                state.videoSpeedControl.setSpeed('0.75');
                // Left-click on second submenu speed (1.0x)
                openSubmenuMouse(menuSubmenuItem);
                submenuItems.eq(1).click();

                // Expect speed to be 1.0x
                expect(state.videoSpeedControl.currentSpeed).toBe('1.0');
                // Expect speed submenu item 0.75x not to be active
                expect(submenuItems.first()).not.toHaveClass('is-selected');
                // Expect speed submenu item 1.0x to be active
                expect(submenuItems.eq(1)).toHaveClass('is-selected');

                // Set speed to 0.75x
                state.videoSpeedControl.setSpeed('0.75');
                // Right-click on second submenu speed (1.0x)
                openSubmenuMouse(menuSubmenuItem);
                submenuItems.eq(1).trigger('contextmenu');

                // Expect speed to be 1.0x
                expect(state.videoSpeedControl.currentSpeed).toBe('1.0');
                // Expect speed submenu item 0.75x not to be active
                expect(submenuItems.first()).not.toHaveClass('is-selected');
                // Expect speed submenu item 1.0x to be active
                expect(submenuItems.eq(1)).toHaveClass('is-selected');
            });
        });

        describe('Keyboard interactions', function() {
            beforeEach(function() {
                state = jasmine.initializePlayer();
                openMenu();
            });

            it('focus the first item of the just opened menu on UP keydown', function() {
                menu.trigger(keyPressEvent($.ui.keyCode.UP));
                expect(menuSubmenuItem).toBeFocused();
            });

            it('focus the last item of the just opened menu on DOWN keydown', function() {
                menu.trigger(keyPressEvent($.ui.keyCode.DOWN));
                expect(menuItems.first()).toBeFocused();
            });

            it('open the submenu on ENTER keydown', function() {
                openSubmenuKeyboard(menuSubmenuItem, $.ui.keyCode.ENTER);
                expect(menuSubmenuItem).toHaveClass('is-opened');
                expect(submenuItems.first()).toBeFocused();
            });

            it('open the submenu on SPACE keydown', function() {
                openSubmenuKeyboard(menuSubmenuItem, $.ui.keyCode.SPACE);
                expect(menuSubmenuItem).toHaveClass('is-opened');
                expect(submenuItems.first()).toBeFocused();
            });

            it('open the submenu on RIGHT keydown', function() {
                openSubmenuKeyboard(menuSubmenuItem, $.ui.keyCode.RIGHT);
                expect(menuSubmenuItem).toHaveClass('is-opened');
                expect(submenuItems.first()).toBeFocused();
            });

            it('close the menu on ESCAPE keydown', function() {
                menu.trigger(keyPressEvent($.ui.keyCode.ESCAPE));
                expect(menu).not.toHaveClass('is-opened');
                expect(overlay).not.toBeInDOM();
            });

            it('close the submenu on ESCAPE keydown', function() {
                openSubmenuKeyboard(menuSubmenuItem);
                menuSubmenuItem.trigger(keyPressEvent($.ui.keyCode.ESCAPE));
                expect(menuSubmenuItem).not.toHaveClass('is-opened');
                expect(overlay).not.toBeInDOM();
            });

            it('close the submenu on LEFT keydown on submenu items', function() {
                closeSubmenuKeyboard(menuSubmenuItem);
            });

            it('do nothing on RIGHT keydown on submenu item', function() {
                submenuItems.eq(1).focus().trigger(keyPressEvent($.ui.keyCode.RIGHT)); // Mute
                // Is still focused.
                expect(submenuItems.eq(1)).toBeFocused();
            });

            it('do nothing on TAB keydown on menu item', function() {
                submenuItems.eq(1).focus().trigger(keyPressEvent($.ui.keyCode.TAB)); // Mute
                // Is still focused.
                expect(submenuItems.eq(1)).toBeFocused();
            });

            it('UP and DOWN keydown function as expected on menu/submenu items', function() {
                menuItems.eq(0).focus(); // Play
                expect(menuItems.eq(0)).toBeFocused();
                menuItems.eq(0).trigger(keyPressEvent($.ui.keyCode.DOWN));
                expect(menuItems.eq(1)).toBeFocused(); // Mute
                menuItems.eq(1).trigger(keyPressEvent($.ui.keyCode.DOWN));
                expect(menuItems.eq(2)).toBeFocused(); // Fullscreen
                menuItems.eq(2).trigger(keyPressEvent($.ui.keyCode.DOWN));
                expect(menuSubmenuItem).toBeFocused(); // Speed
                menuSubmenuItem.trigger(keyPressEvent($.ui.keyCode.DOWN));
                expect(menuItems.eq(0)).toBeFocused(); // Play

                menuItems.eq(0).trigger(keyPressEvent($.ui.keyCode.UP));
                expect(menuSubmenuItem).toBeFocused(); // Speed
                // Check if hidden item can be skipped correctly.
                menuItems.eq(2).hide(); // hide Fullscreen item
                menuSubmenuItem.trigger(keyPressEvent($.ui.keyCode.UP));
                expect(menuItems.eq(1)).toBeFocused(); // Mute
                menuItems.eq(1).trigger(keyPressEvent($.ui.keyCode.UP));
                expect(menuItems.eq(0)).toBeFocused(); // Play
            });

            it('current item is still focused if all siblings are hidden', function() {
                menuItems.eq(0).focus(); // Play
                expect(menuItems.eq(0)).toBeFocused(); // hide all siblings
                menuItems.eq(0).siblings().hide();
                menuSubmenuItem.trigger(keyPressEvent($.ui.keyCode.DOWN));
                expect(menuItems.eq(0)).toBeFocused();
                menuSubmenuItem.trigger(keyPressEvent($.ui.keyCode.UP));
                expect(menuItems.eq(0)).toBeFocused();
            });

            it('ENTER keydown on menu/submenu item selects its data and closes menu', function() {
                menuItems.eq(2).focus().trigger(keyPressEvent($.ui.keyCode.ENTER)); // Fullscreen
                expect(menuItems.eq(2)).toHaveClass('is-selected');
                expect(menuItems.eq(2).siblings()).not.toHaveClass('is-selected');
                expect(state.isFullScreen).toBeTruthy();
                expect(menuItems.eq(2)).toHaveText('Exit full browser');
            });

            it('SPACE keydown on menu/submenu item selects its data and closes menu', function() {
                submenuItems.eq(2).focus().trigger(keyPressEvent($.ui.keyCode.SPACE)); // 1.25x
                expect(submenuItems.eq(2)).toHaveClass('is-selected');
                expect(submenuItems.eq(2).siblings()).not.toHaveClass('is-selected');
                expect(state.videoSpeedControl.currentSpeed).toBe('1.25');
            });
        });
    });
}());
