"use strict";

describe("ContextModule.ConfigGrid", function() {

    var testEntityConfigs = null;
    var testViewConfigs = null;
    var testIrrelevantEntityConfigs = null;
    var testIrrelevantViewConfigs = null;
    
    beforeEach(function() {
        testEntityConfigs = [
             new App.ContextModule.Config({parameters: {x: 10, y: "test"}}),
             new App.ContextModule.Config({parameters: {x: 10, y: "test"}}),
             new App.ContextModule.Config({parameters: {x: 20}}),
            ];

        testViewConfigs = [
                new App.ContextModule.Config({parameters: {x: 10, y: "test"}}),
                new App.ContextModule.Config({parameters: {x: 10, y: "test"}}),
                new App.ContextModule.Config({parameters: {x: 20}}),
             ];

        testIrrelevantEntityConfigs = [
                new App.ContextModule.Config({parameters: {x: 10, y: "test"}}),
             ];
        testIrrelevantViewConfigs = [
                new App.ContextModule.Config({parameters: {x: 10, y: "test"}}),
             ];
    });

    it("is promptly created", function() {
        var testedConfigGrid = new App.ContextModule.ConfigGrid();
        
        expect(testedConfigGrid).not.toBe(null);
        
        expect(testedConfigGrid.entityConfigs).not.toBe(null);
        expect(testedConfigGrid.entityConfigs.size()).toEqual(0);
        
        expect(testedConfigGrid.viewConfigs).not.toBe(null);
        expect(testedConfigGrid.viewConfigs.size()).toEqual(0);
        
        expect(testedConfigGrid.getType()).toBe(undefined);
    });

    it("is promptly created with type", function() {
        var testedConfigGrid = new App.ContextModule.ConfigGrid("abc");
        
        expect(testedConfigGrid).not.toBe(null);
        
        expect(testedConfigGrid.getType()).toBe("abc");
    });

    it("can add parameter bags", function() {
        var testedConfigGrid = new App.ContextModule.ConfigGrid();
        
        testedConfigGrid.entityConfigs.add(testEntityConfigs[0]);
        testedConfigGrid.entityConfigs.add(testEntityConfigs[1]);
        testedConfigGrid.entityConfigs.add(testEntityConfigs[2]);

        testedConfigGrid.viewConfigs.add(testViewConfigs[0]);
        testedConfigGrid.viewConfigs.add(testViewConfigs[1]);
        testedConfigGrid.viewConfigs.add(testViewConfigs[2]);

        expect(testedConfigGrid.viewConfigs.size()).toEqual(3);
        expect(testedConfigGrid.entityConfigs.size()).toEqual(3);

        // Can't add what's already there
        testedConfigGrid.entityConfigs.add(testEntityConfigs[2]);
        testedConfigGrid.viewConfigs.add(testViewConfigs[2]);
        
        expect(testedConfigGrid.viewConfigs.size()).toEqual(3);
        expect(testedConfigGrid.entityConfigs.size()).toEqual(3);
    });

    it("can add parameter bags at a particular place", function() {
        var testedConfigGrid = new App.ContextModule.ConfigGrid();
        
        testedConfigGrid.entityConfigs.add(testEntityConfigs[0]);
        testedConfigGrid.entityConfigs.add(testEntityConfigs[1]);
        testedConfigGrid.entityConfigs.add(testEntityConfigs[2], {at: 0});

        testedConfigGrid.viewConfigs.add(testViewConfigs[0]);
        testedConfigGrid.viewConfigs.add(testViewConfigs[1]);
        testedConfigGrid.viewConfigs.add(testViewConfigs[2], {at: 1});

        expect(testedConfigGrid.viewConfigs.size()).toEqual(3);
        expect(testedConfigGrid.entityConfigs.size()).toEqual(3);
        
        expect(testedConfigGrid.entityConfigs.at(0)).toEqual(testEntityConfigs[2]);
        expect(testedConfigGrid.entityConfigs.at(1)).toEqual(testEntityConfigs[0]);
        expect(testedConfigGrid.entityConfigs.at(2)).toEqual(testEntityConfigs[1]);
        
        expect(testedConfigGrid.viewConfigs.at(0)).toEqual(testViewConfigs[0]);
        expect(testedConfigGrid.viewConfigs.at(1)).toEqual(testViewConfigs[2]);
        expect(testedConfigGrid.viewConfigs.at(2)).toEqual(testViewConfigs[1]);
    });

    it("can reset", function() {
        var testedConfigGrid = new App.ContextModule.ConfigGrid();
        
        testedConfigGrid.entityConfigs.add(testEntityConfigs[0]);
        testedConfigGrid.entityConfigs.add(testEntityConfigs[1]);
        testedConfigGrid.entityConfigs.add(testEntityConfigs[2]);

        testedConfigGrid.viewConfigs.add(testViewConfigs[0]);
        testedConfigGrid.viewConfigs.add(testViewConfigs[1]);
        testedConfigGrid.viewConfigs.add(testViewConfigs[2]);

        expect(testedConfigGrid.entityConfigs.size()).toEqual(3);
        expect(testedConfigGrid.viewConfigs.size()).toEqual(3);

        testedConfigGrid.entityConfigs.reset();
        testedConfigGrid.viewConfigs.reset();

        expect(testedConfigGrid.entityConfigs.size()).toEqual(0);
        expect(testedConfigGrid.viewConfigs.size()).toEqual(0);
        
        testedConfigGrid.entityConfigs.add(testEntityConfigs[0]);
        testedConfigGrid.entityConfigs.add(testEntityConfigs[1]);
        testedConfigGrid.entityConfigs.add(testEntityConfigs[2]);

        testedConfigGrid.viewConfigs.add(testViewConfigs[0]);
        testedConfigGrid.viewConfigs.add(testViewConfigs[1]);
        testedConfigGrid.viewConfigs.add(testViewConfigs[2]);
    });

    it("retreives neighbour entityConfigs and viewConfigs", function() {
        var testedConfigGrid = new App.ContextModule.ConfigGrid();

        testedConfigGrid.entityConfigs.add(testEntityConfigs[0]);
        testedConfigGrid.entityConfigs.add(testEntityConfigs[1]);
        testedConfigGrid.entityConfigs.add(testEntityConfigs[2]);

        testedConfigGrid.viewConfigs.add(testViewConfigs[0]);
        testedConfigGrid.viewConfigs.add(testViewConfigs[1]);
        testedConfigGrid.viewConfigs.add(testViewConfigs[2]);

        expect(testedConfigGrid.getPrevEntityNeighbour(testEntityConfigs[0])).toBe(undefined);
        expect(testedConfigGrid.getPrevEntityNeighbour(testEntityConfigs[1])).toEqual(testEntityConfigs[0]);
        expect(testedConfigGrid.getPrevEntityNeighbour(testEntityConfigs[2])).toEqual(testEntityConfigs[1]);
        expect(testedConfigGrid.getNextEntityNeighbour(testEntityConfigs[0])).toEqual(testEntityConfigs[1]);
        expect(testedConfigGrid.getNextEntityNeighbour(testEntityConfigs[1])).toEqual(testEntityConfigs[2]);
        expect(testedConfigGrid.getNextEntityNeighbour(testEntityConfigs[2])).toBe(undefined);

        expect(function(){testedConfigGrid.getPrevEntityNeighbour(testIrrelevantEntityConfigs[0]);}).toThrow();
        expect(function(){testedConfigGrid.getNextEntityNeighbour(testIrrelevantEntityConfigs[0]);}).toThrow();
        expect(function(){testedConfigGrid.getPrevEntityNeighbour(testViewConfigs[0]);}).toThrow();
        expect(function(){testedConfigGrid.getNextEntityNeighbour(testViewConfigs[0]);}).toThrow();

        expect(testedConfigGrid.getPrevViewNeighbour(testViewConfigs[0])).toEqual(undefined);
        expect(testedConfigGrid.getPrevViewNeighbour(testViewConfigs[1])).toEqual(testViewConfigs[0]);
        expect(testedConfigGrid.getPrevViewNeighbour(testViewConfigs[2])).toEqual(testViewConfigs[1]);
        expect(testedConfigGrid.getNextViewNeighbour(testViewConfigs[0])).toEqual(testViewConfigs[1]);
        expect(testedConfigGrid.getNextViewNeighbour(testViewConfigs[1])).toEqual(testViewConfigs[2]);
        expect(testedConfigGrid.getNextViewNeighbour(testViewConfigs[2])).toEqual(undefined);

        expect(function(){testedConfigGrid.getPrevViewNeighbour(testIrrelevantViewConfigs[0]);}).toThrow();
        expect(function(){testedConfigGrid.getNextViewNeighbour(testIrrelevantViewConfigs[0]);}).toThrow();
        expect(function(){testedConfigGrid.getPrevViewNeighbour(testEntityConfigs[0]);}).toThrow();
        expect(function(){testedConfigGrid.getNextViewNeighbour(testEntityConfigs[0]);}).toThrow();
    });

    it("relocates entityConfigs and viewConfigs", function() {
        var testedConfigGrid = new App.ContextModule.ConfigGrid();

        _.each([
                {
                    configCollection: testedConfigGrid.entityConfigs,
                    relocateFunctionName: "relocateEntityConfig",
                    relevantConfigs: testEntityConfigs,
                    irrelevantConfigs: testIrrelevantEntityConfigs,
                },
                {
                    configCollection: testedConfigGrid.viewConfigs,
                    relocateFunctionName: "relocateViewConfig",
                    relevantConfigs: testViewConfigs,
                    irrelevantConfigs: testIrrelevantViewConfigs
                }
            ], function(currentDimension) {

            currentDimension.configCollection.add(currentDimension.relevantConfigs[0]);
            currentDimension.configCollection.add(currentDimension.relevantConfigs[1]);
            currentDimension.configCollection.add(currentDimension.relevantConfigs[2]);
            expect(currentDimension.configCollection.size()).toEqual(3);

            // index
            testedConfigGrid[currentDimension.relocateFunctionName](currentDimension.relevantConfigs[1], 0);
            expect(currentDimension.configCollection.at(0)).toEqual(currentDimension.relevantConfigs[1]);
            expect(currentDimension.configCollection.at(1)).toEqual(currentDimension.relevantConfigs[0]);
            expect(currentDimension.configCollection.at(2)).toEqual(currentDimension.relevantConfigs[2]);
            
            // config
            testedConfigGrid[currentDimension.relocateFunctionName](currentDimension.relevantConfigs[1], currentDimension.relevantConfigs[2]);
            expect(currentDimension.configCollection.at(0)).toEqual(currentDimension.relevantConfigs[0]);
            expect(currentDimension.configCollection.at(1)).toEqual(currentDimension.relevantConfigs[1]);
            expect(currentDimension.configCollection.at(2)).toEqual(currentDimension.relevantConfigs[2]);

            // config cid
            testedConfigGrid[currentDimension.relocateFunctionName](currentDimension.relevantConfigs[2], currentDimension.relevantConfigs[0].getClientId());
            expect(currentDimension.configCollection.at(0)).toEqual(currentDimension.relevantConfigs[2]);
            expect(currentDimension.configCollection.at(1)).toEqual(currentDimension.relevantConfigs[0]);
            expect(currentDimension.configCollection.at(2)).toEqual(currentDimension.relevantConfigs[1]);

            // exceptional cases
            expect(function(){testedConfigGrid[currentDimension.relocateFunctionName](currentDimension.irrelevantConfigs[1], 0);}).toThrow();
            expect(function(){testedConfigGrid[currentDimension.relocateFunctionName](relevantConfigs[1], currentDimension.irrelevantConfigs[1]);}).toThrow();
            expect(function(){testedConfigGrid[currentDimension.relocateFunctionName](currentDimension.relevantConfigs[1], -1);}).toThrow();
            expect(function(){testedConfigGrid[currentDimension.relocateFunctionName](currentDimension.relevantConfigs[1], 4);}).toThrow();

            // index - last
            testedConfigGrid[currentDimension.relocateFunctionName](currentDimension.relevantConfigs[1], 3);
            expect(currentDimension.configCollection.at(0)).toEqual(currentDimension.relevantConfigs[2]);
            expect(currentDimension.configCollection.at(1)).toEqual(currentDimension.relevantConfigs[0]);
            expect(currentDimension.configCollection.at(2)).toEqual(currentDimension.relevantConfigs[1]);
            
            // clone of a config
            var clone = new App.ContextModule.Config(currentDimension.relevantConfigs[1].serialize());
            expect(function(){testedConfigGrid[currentDimension.relocateFunctionName](clone, 0);}).toThrow();
        });
    });

    xit("works with selectedEntityConfigClientId and selectedViewConfigClientId", function() {
    });
    
    xit("performs combined actions: addEntityAndSelectIt, addViewAndSelectIt", function() {
    });
    
    xit("performs combined actions: removeEntityAndSelectNeighbour, removeViewAndSelectNeighbour", function() {
    });

    it("serializes and unserializes itself", function() {
        var testedConfigGrid = new App.ContextModule.ConfigGrid();
        
        var originalSerializedObject = {
                entityConfigs: [
                        testEntityConfigs[1].serialize(),
                        testEntityConfigs[2].serialize(),
                        testEntityConfigs[0].serialize(),
                    ],
                viewConfigs: [
                        testViewConfigs[0].serialize(),
                        testViewConfigs[1].serialize()
                    ],
            };
        testedConfigGrid.unserialize(originalSerializedObject);
        
        expect(testedConfigGrid.entityConfigs.size()).toEqual(3);
        expect(testedConfigGrid.viewConfigs.size()).toEqual(2);
        
        expect(testedConfigGrid.entityConfigs.at(0).getClientId()).toEqual(testEntityConfigs[1].getClientId());
        expect(testedConfigGrid.entityConfigs.at(1).getClientId()).toEqual(testEntityConfigs[2].getClientId());
        expect(testedConfigGrid.entityConfigs.at(2).getClientId()).toEqual(testEntityConfigs[0].getClientId());

        expect(testedConfigGrid.viewConfigs.at(0).getClientId()).toEqual(testViewConfigs[0].getClientId());
        expect(testedConfigGrid.viewConfigs.at(1).getClientId()).toEqual(testViewConfigs[1].getClientId());

        expect(testedConfigGrid.serialize()).toEqual(originalSerializedObject);
        
        testedConfigGrid.relocateEntityConfig(testedConfigGrid.entityConfigs.at(2), 0);
        testedConfigGrid.entityConfigs.remove(testedConfigGrid.entityConfigs.at(2));
        testedConfigGrid.entityConfigs.add(testIrrelevantEntityConfigs[0]);

        testedConfigGrid.viewConfigs.add(testViewConfigs[2]);
        testedConfigGrid.relocateViewConfig(testedConfigGrid.viewConfigs.at(2), 0);
        testedConfigGrid.relocateViewConfig(testedConfigGrid.viewConfigs.at(1), 3);
        testedConfigGrid.viewConfigs.add(testIrrelevantViewConfigs[0]);
        
        var newSerializedObject = {
            entityConfigs: [
                      testEntityConfigs[0].serialize(),
                      testEntityConfigs[1].serialize(),
                      testIrrelevantEntityConfigs[0].serialize()
                    ],
            viewConfigs: [
                      testViewConfigs[2].serialize(),
                      testViewConfigs[1].serialize(),
                      testViewConfigs[0].serialize(),
                      testIrrelevantViewConfigs[0].serialize()
                    ],
            };
        expect(testedConfigGrid.serialize()).toEqual(newSerializedObject);
    });

    it("unserializes itself from faulty serialized objects", function() {
        var faultySerializedObjects = [
               null,
               undefined,
               42,
               "test",
               {entityConfigs: 42},
               {viewConfigs: [testViewConfigs[1].serialize()], "foo": "bar"},
           ];
        
        var testedConfigGrid = new App.ContextModule.ConfigGrid();
        
        _.each(faultySerializedObjects, function(faultySerializedObject) {
            expect(function() {
                testedConfigGrid.unserialize(faultySerializedObject);
            }).not.toThrow();
        });
    });
    
    it("triggers events when real changes occur", function() {
        var testedConfigGrid = new App.ContextModule.ConfigGrid();
        
        _.each([
                {
                    configCollection: testedConfigGrid.entityConfigs,
                    relocateFunctionName: "relocateEntityConfig",
                    relevantConfigs: testEntityConfigs,
                    irrelevantConfigs: testIrrelevantEntityConfigs,
                    
                    changeLayoutEventName: "change_layout",
                    changeConfigEventPrefix: "change_entity:",
                    changeConfigNeighboursPrefix: "change_entity_neighbours:",
                },
                {
                    configCollection: testedConfigGrid.viewConfigs,
                    relocateFunctionName: "relocateViewConfig",
                    relevantConfigs: testViewConfigs,
                    irrelevantConfigs: testIrrelevantViewConfigs,

                    changeLayoutEventName: "change_layout",
                    changeConfigEventPrefix: "change_view:",
                    changeConfigNeighboursPrefix: "change_view_neighbours:",
                }
            ], function(currentDimension) {

            var spyNames = [
                     "change",
                     "changeCollection",
                     "changeConfig0",
                     "changeConfig1",
                     "changeConfig2",
                     "changeConfigNeighbours0",
                     "changeConfigNeighbours1",
                     "changeConfigNeighbours2"
                 ];
            var spy = jasmine.createSpyObj("listener", spyNames);
    
            testedConfigGrid.on("change", spy.change, spy);
            testedConfigGrid.on(currentDimension.changeLayoutEventName, spy.changeCollection, spy);
            testedConfigGrid.on(currentDimension.changeConfigEventPrefix + currentDimension.relevantConfigs[0].getClientId(), spy.changeConfig0, spy);
            testedConfigGrid.on(currentDimension.changeConfigEventPrefix + currentDimension.relevantConfigs[1].getClientId(), spy.changeConfig1, spy);
            testedConfigGrid.on(currentDimension.changeConfigEventPrefix + currentDimension.relevantConfigs[2].getClientId(), spy.changeConfig2, spy);
    
            testedConfigGrid.on(currentDimension.changeConfigNeighboursPrefix + currentDimension.relevantConfigs[0].getClientId(), spy.changeConfigNeighbours0, spy);
            testedConfigGrid.on(currentDimension.changeConfigNeighboursPrefix + currentDimension.relevantConfigs[1].getClientId(), spy.changeConfigNeighbours1, spy);
            testedConfigGrid.on(currentDimension.changeConfigNeighboursPrefix + currentDimension.relevantConfigs[2].getClientId(), spy.changeConfigNeighbours2, spy);
            
            var expectSpyCallCount = function() {
                _.each(arguments, function(arg, i) {
                    //console.log("___", i, "--->", spy[spyNames[i]].calls.count(), arg);
                    expect(spy[spyNames[i]].calls.count()).toEqual(arg);
                });
                for (var i = arguments.length; i < spyNames.length; i++) {
                    expect(spy[spyNames[i]].calls.count()).toEqual(0);
                }
            };
            var resetSpyCallCount = function() {
                _.each(spyNames, function(spyName) {
                    spy[spyName].calls.reset();
                });
            };
            expectSpyCallCount(0);
            resetSpyCallCount();
            
            testedConfigGrid.unserialize(null);
            resetSpyCallCount();

            // Unserialize the whole grid
            var defaultSerializedState = {
                entityConfigs: [
                        testEntityConfigs[0].serialize(),
                        testEntityConfigs[1].serialize(),
                        testEntityConfigs[2].serialize(),
                    ],
                viewConfigs: [
                        testViewConfigs[0].serialize(),
                        testViewConfigs[1].serialize(),
                        testViewConfigs[2].serialize(),
                    ],
            };
            testedConfigGrid.unserialize(defaultSerializedState);
            expectSpyCallCount(1, 1);

            resetSpyCallCount();
            testedConfigGrid.unserialize(defaultSerializedState);
            expectSpyCallCount(0, 0);

            // Modify parameters
            resetSpyCallCount();
            expectSpyCallCount(0);
            
            currentDimension.configCollection.at(1).updateParameter("my", 1);
            expectSpyCallCount(1, 0,
                    0, 1, 0,
                    1, 0, 1);

            resetSpyCallCount();
            currentDimension.configCollection.at(2).planParameterUpdate("my", 1);
            expectSpyCallCount(1, 0,
                    0, 0, 1,
                    0, 1, 0);

            resetSpyCallCount();
            currentDimension.configCollection.remove(currentDimension.configCollection.at(2));
            expect(currentDimension.configCollection.size()).toEqual(2);
            expectSpyCallCount(1, 1,
                    0, 0, 0,
                    0, 1, 0);
            
            resetSpyCallCount();
            testedConfigGrid.unserialize(defaultSerializedState);
            expectSpyCallCount(1, 1,
                    0, 1, 0,
                    1, 1, 0);

            resetSpyCallCount();
            
            expect(function() {
                testedConfigGrid.relocateEntityConfig(testEntityConfigs[2], 0);
            }).toThrow();
            expect(function() {
                testedConfigGrid.relocateViewConfig(testViewConfigs[2], 0);
            }).toThrow();

            testedConfigGrid.relocateEntityConfig(testedConfigGrid.entityConfigs.at(1), 0);
            testedConfigGrid.relocateViewConfig(testedConfigGrid.viewConfigs.at(1), 0);
            expectSpyCallCount(2, 2,
                    0, 0, 0,
                    1, 1, 1);
            
            resetSpyCallCount();
            testedConfigGrid.unserialize(defaultSerializedState);
            expectSpyCallCount(1, 1,
                    0, 0, 0,
                    1, 1, 1);

            resetSpyCallCount();
            currentDimension.configCollection.add(currentDimension.irrelevantConfigs[0], {at: 0});
            expectSpyCallCount(1, 1,
                    0, 0, 0,
                    1, 0, 0);
        });
    });
});
