import { Component, NO_ERRORS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NavigationComponent } from './navigation.component';
import { AppState } from '../../../app.service';
import { IntervalService } from '../../../services/interval';
import { FromEventObservable } from 'rxjs/observable/FromEventObservable';
import { TemplateDirective } from '../../template.directive';
import { WindowRef } from '../../../services/window.service';

@Component({
    selector: 'app',
    template: `
        <div #menuWrapper>
            <div ui-navigation>
                <li *templateDirective style="width:100px"><a>Page1</a></li>
                <li *templateDirective style="width:100px"><a>Page2</a></li>
                <li *templateDirective style="width:100px"><a>Page3</a></li>
                <li *templateDirective style="width:100px"><a>Page4</a></li>
                <li *templateDirective style="width:200px"><a>Page5</a></li>
                <li *templateDirective style="width:200px"><a>Page6</a></li>
                <li *templateDirective style="width:200px"><a>Page7</a></li>
            </div>
        </div>
    `
})
class App {}


describe('Navigation component', () => {

    let fixture: ComponentFixture<NavigationComponent>;
    let compNavigation: NavigationComponent;

    let fixtureApp: ComponentFixture<App>;
    let compApp: App;

    beforeEach(async () => {
        TestBed.configureTestingModule({
            declarations: [App, TemplateDirective, NavigationComponent],
            providers: [AppState, IntervalService, WindowRef],
            schemas: [ NO_ERRORS_SCHEMA ]
        }).compileComponents();
    });

    beforeEach(() => {
        fixtureApp = TestBed.createComponent(App);
        compApp = fixtureApp.componentInstance;

        jasmine.clock().uninstall();
        jasmine.clock().install();

        spyOn(FromEventObservable, 'create').and.callThrough();
        spyOn(FromEventObservable.prototype, 'subscribe').and.callThrough();

        fixtureApp.detectChanges();
        compNavigation = fixtureApp.debugElement.children[0].childNodes[1].componentInstance;

        spyOn(compNavigation, 'normalize').and.callThrough();
        spyOn(compNavigation.mainContainer, 'createEmbeddedView').and.callThrough();
        spyOn(compNavigation, 'fillMenu').and.callThrough();

    });

    it('should create the Navigation components', () => {
        expect(compApp).toBeTruthy();
        expect(compNavigation).toBeTruthy();
        expect(compApp instanceof App).toBeTruthy();
        expect(compNavigation instanceof NavigationComponent).toBeTruthy();
    });

    it('should create listener for window resize', () => {
        expect(FromEventObservable.create).toHaveBeenCalledWith(window, 'resize');
        expect(FromEventObservable.prototype.subscribe).toHaveBeenCalled();
    });

    it('should fill main container with elements of menu after view init', () => {
        expect(compNavigation.mainContainer.createEmbeddedView).not.toHaveBeenCalled();
        jasmine.clock().tick(1000);
        expect(compNavigation.mainContainer.createEmbeddedView).toHaveBeenCalled();
        expect(compNavigation.fillMenu).toHaveBeenCalled();
    });

    it('should normalize menu after view init', () => {
        expect(compNavigation.normalize).not.toHaveBeenCalled();
        jasmine.clock().tick(1000);
        expect(compNavigation.normalize).toHaveBeenCalled();
        expect(compNavigation.normalize).toHaveBeenCalledTimes(1);
    });

    it('should normalize menu after each 10 seconds', () => {
        jasmine.clock().tick(1000);
        (compNavigation.normalize as any).calls.reset();
        jasmine.clock().tick(50000);
        expect(compNavigation.normalize).toHaveBeenCalledTimes(5);
    });

    describe('Normalization of menu', () => {

        beforeEach(() => {
            spyOn(compNavigation, 'addToMain').and.callThrough();
            spyOn(compNavigation, 'addToDrop').and.callThrough();
            compNavigation.fillMenu();
        });

        it('should add elements of menu in main menu ' +
            'if window\'s width less then 768px (mobile version)', () => {
            compNavigation.winRef.nativeWindow.innerWidth = 700;

            compNavigation.normalize();
            expect(compNavigation.addToMain).toHaveBeenCalledTimes(1);
            expect(compNavigation.mainContainer.length).toBe(7);
            expect(compNavigation.dropContainer.length).toBe(0);
            expect(compNavigation.showDropButton).toBeFalsy();
        });

        describe('Distribution elements of menu between main and dropdown menu ' +
            'when window\'s width at least 768px (desktop version)', () => {

            beforeEach(() => {
                compNavigation.winRef.nativeWindow.innerWidth = 768;
            });

            it('should hide extra elements in dropdown menu', () => {
                spyOn(compNavigation, 'getMainNavWidth').and.returnValue(901);
                spyOn(compNavigation, 'getMainItemsWidth').and.returnValues(1000, 900);

                compNavigation.normalize();
                expect(compNavigation.addToDrop).toHaveBeenCalledTimes(1);
                expect(compNavigation.mainContainer.length).toBe(6);
                expect(compNavigation.dropContainer.length).toBe(1);
                expect(compNavigation.showDropButton).toBeTruthy();
            });

            it('should hide all elements in dropdown menu if any element is not fit', () => {
                spyOn(compNavigation, 'getMainNavWidth').and.returnValue(150);
                spyOn(compNavigation, 'getMainItemsWidth')
                    .and.returnValues(1000, 800, 600, 500, 400, 300, 200, 100);

                compNavigation.normalize();
                expect(compNavigation.addToDrop).toHaveBeenCalledTimes(7);
                expect(compNavigation.mainContainer.length).toBe(0);
                expect(compNavigation.dropContainer.length).toBe(7);
                expect(compNavigation.showDropButton).toBeTruthy();
            });

        });

        it('should show all elements in main menu if all element is fit', () => {
            // Emulate minimization of window.
            compNavigation.winRef.nativeWindow.innerWidth = 768;
            let getMainNavWidthSpy = spyOn(compNavigation, 'getMainNavWidth')
                .and.returnValue(99);
            let getMainItemsWidthSpy = spyOn(compNavigation, 'getMainItemsWidth')
                .and.returnValue(100);
            compNavigation.normalize();
            expect(compNavigation.addToDrop).toHaveBeenCalled();
            expect(compNavigation.mainContainer.length).toBe(0);
            expect(compNavigation.dropContainer.length).toBe(7);
            expect(compNavigation.showDropButton).toBeTruthy();

            // Emulate maximization of window.
            getMainNavWidthSpy.and.returnValue(1200);
            getMainItemsWidthSpy.and.returnValue(1000);
            // Define width of the first element in dropdown menu
            spyOn(compNavigation, 'viewWidth').and.returnValue(100);
            compNavigation.normalize();
            expect(compNavigation.addToMain).toHaveBeenCalled();
            expect(compNavigation.mainContainer.length).toBe(7);
            expect(compNavigation.dropContainer.length).toBe(0);
            expect(compNavigation.showDropButton).toBeFalsy();
            expect(compNavigation.addToMain).toHaveBeenCalled();
            expect(compNavigation.mainContainer.length).toBe(7);
            expect(compNavigation.dropContainer.length).toBe(0);
            expect(compNavigation.showDropButton).toBeFalsy();
        });

    });

    afterEach(() => {
        jasmine.clock().uninstall();
    });

});
