var $ = require("jquery"),
    ko = require("knockout"),
    DataSource = require("data/data_source/data_source").DataSource,
    logger = require("core/utils/console").logger,
    fx = require("animation/fx"),
    dataGridMocks = require("../../helpers/dataGridMocks.js");

require("ui/data_grid/ui.data_grid");
require("integration/knockout");

QUnit.testStart(function() {
    var markup =
        '<style>\
            .fixed-height {\
                height: 400px;\
            }\
            .qunit-fixture-auto-height {\
                position: static !important;\
                height: auto !important;\
            }\
        </style>\
        <div id="dataGrid">\
            <div data-options="dxTemplate: { name: \'test\' }">Template Content</div>\
        </div>\
        <div id="landOfKO">\
            <div id="dataGridKO" data-bind="dxDataGrid: gridOptions">\
                <div data-options="dxTemplate: { name: \'testCellTemplate\'}">\
                    <span data-bind="text: $root.getCellText($data)"></span>\
                </div>\
                <table data-options="dxTemplate: { name: \'testRowTemplate\' }">\
                    <tr class="test-row" data-bind="click: $root.rowClick">\
                        <td>Cell Content</td>\
                    </tr>\
                </table>\
            </div>\
        </div>';

    $("#qunit-fixture").html(markup);
});

var processColumnsForCompare = function(columns, parameterNames) {
    var processedColumns = $.extend(true, [], columns);
    $.each(processedColumns, function() {
        var propertyName;
        for(propertyName in this) {
            if(parameterNames) {
                if($.inArray(propertyName, parameterNames) === -1) {
                    delete this[propertyName];
                }
            } else {
                if($.isFunction(this[propertyName])) {
                    delete this[propertyName];
                }
                if(propertyName === 'filterOperations' ||
                    propertyName === 'showInColumnChooser' ||
                    propertyName === 'userDataType' ||
                    propertyName === 'defaultFilterOperation' ||
                    propertyName === 'defaultFilterOperations' ||
                    propertyName === 'visibleIndex' ||
                    propertyName === 'resizedCallbacks' ||
                    propertyName === 'headerId') {
                    delete this[propertyName];
                }
            }
        }
    });
    return processedColumns;
};

fx.off = true;

QUnit.module("Assign options", {
    beforeEach: function() {
        this.clock = sinon.useFakeTimers();
    },
    afterEach: function() {
        this.clock.restore();
    }
});

QUnit.test('Lookup dataSource is observable value', function(assert) {
    // arrange, act
    var errorMessage;

    logger.error = function(message) {
        errorMessage = message;
    };

    // act
    $("#dataGrid").dxDataGrid({
        columns: [{ dataField: 'field1', lookup: { dataSource: ko.observableArray([]) } }],
        loadingTimeout: undefined,
        dataSource: [{ field1: 1 }]
    });

    // assert
    assert.ok(errorMessage.indexOf("Unexpected type of data source is provided for a lookup column") > -1, "Error message");
});

var setupModule = function() {
    dataGridMocks.setupDataGridModules(this, ['columns', 'data', 'selection', 'editing', 'filterRow', 'masterDetail']);

    this.applyOptions = function(options) {
        $.extend(this.options, options);
        this.columnsController.init();
        this.selectionController.init();
        this.editingController.init();
    };

    this.getColumns = function(parameterNames) {
        return processColumnsForCompare(this.columnsController.getColumns(), parameterNames);
    };
    this.getVisibleColumns = function(parameterNames) {
        return processColumnsForCompare(this.columnsController.getVisibleColumns(), parameterNames);
    };
};

var teardownModule = function() {
    this.dispose();
};

QUnit.module("initialization from dataSource", { beforeEach: setupModule, afterEach: teardownModule });

// T111157
QUnit.test("Initialize from array store with observable fields", function(assert) {
    var dataSource = new DataSource([
        { name: ko.observable('Alex'), age: ko.observable(15), birthDate: ko.observable(new Date(1995, 5, 23)) },
        { name: ko.observable('Dan'), age: ko.observable(19), birthDate: ko.observable(new Date(1991, 6, 15)) }
    ]);
    dataSource.load();

    this.columnsController.applyDataSource(dataSource);

    var visibleColumns = this.columnsController.getVisibleColumns();

    assert.deepEqual(processColumnsForCompare(visibleColumns), [
        { index: 0, visible: true, showEditorAlways: false, allowFiltering: true, dataField: 'name', caption: 'Name', alignment: 'left', dataType: 'string' },
        { index: 1, visible: true, showEditorAlways: false, allowFiltering: true, dataField: 'age', caption: 'Age', alignment: 'right', dataType: 'number', serializationFormat: null },
        { index: 2, visible: true, showEditorAlways: false, allowFiltering: true, dataField: 'birthDate', caption: 'Birth Date', alignment: 'left', dataType: 'date', format: 'shortDate', serializationFormat: null }
    ]);

    assert.strictEqual(visibleColumns[0].index, 0);
    assert.strictEqual(visibleColumns[1].index, 1);
    assert.strictEqual(visibleColumns[2].index, 2);
});

// T387248
QUnit.test("Set selectedRows where there is a nested knockout observable value inside dataSource", function(assert) {
    // arrange
    var items;

    this.array = [
        { name: ko.observable('Alex'), age: ko.observable(15), birthDate: ko.observable(new Date(1995, 5, 23)) },
        { name: ko.observable('Dan'), age: ko.observable(16), birthDate: ko.observable(new Date(1991, 6, 15)) },
        { name: ko.observable('Tom'), age: ko.observable(18), birthDate: ko.observable(new Date(1992, 8, 14)) }
    ];
    var dataSource = new DataSource(this.array);
    this.dataController.setDataSource(dataSource);
    dataSource.load();

    this.applyOptions({
        selection: { mode: 'single' }
    });

    this.selectionController.selectRows(this.array[1]);

    // assert
    items = this.dataController.items();
    assert.deepEqual(this.selectionController.getSelectedRowKeys(), [this.array[1]], "keys of the selected rows");
    assert.equal(items.length, 3, "count item");
    assert.ok(!items[0].isSelected, "first item isn't selected");
    assert.ok(items[1].isSelected, "second item is selected");
    assert.ok(!items[2].isSelected, "third item isn't selected");
});

QUnit.module("Work with knockout", {
    beforeEach: function() {
        this.clock = sinon.useFakeTimers();

        this.viewModel = {
            gridOptions: {
                dataSource: new DataSource({
                    store: {
                        type: 'array',
                        data: [
                            { field1: "test1", field2: "test2" },
                            { field1: "test3", field2: "test4" },
                            { field1: "test5", field2: "test6" },
                            { field1: "test7", field2: "test8" }
                        ]
                    },
                    paginate: false
                }),
                headerFilter: {
                    visible: true
                },
                columns: [
                    "field1", "field2"
                ]
            }
        };
        this.createDataGrid = function() {
            ko.applyBindings(this.viewModel, $("#landOfKO").get(0));

            var dataGrid = $("#dataGridKO").dxDataGrid("instance");
            this.clock.tick(500);
            return dataGrid;
        };
    },
    afterEach: function() {
        this.clock.restore();
    }
});

QUnit.test("Check that header filter shows without errors when using KO", function(assert) {
    this.createDataGrid();

    // act
    $("#landOfKO")
        .find(".dx-header-filter")
        .first()
        .trigger("dxclick"); // Without the fix for T269782 we will get error after that


    // assert
    assert.ok(true);
});

// T266949
QUnit.test("Root view model in cellTemplate", function(assert) {
    var getCellTextCallCount = 0;

    this.viewModel.getCellText = function(options) {
        getCellTextCallCount++;
        return options.rowIndex + " - " + options.text;
    };
    this.viewModel.gridOptions.columns[0] = {
        dataField: "field1",
        cssClass: "test-cell",
        cellTemplate: "testCellTemplate"
    };

    // act
    this.createDataGrid();

    // assert
    assert.equal(getCellTextCallCount, 4, "cell template call count");
    assert.ok($(".dx-data-row .test-cell").eq(0).text().indexOf("0 - test1") >= 0, "test cell 0 text");
    assert.ok($(".dx-data-row .test-cell").eq(1).text().indexOf("1 - test3") >= 0, "test cell 1 text");
});

QUnit.test('Two-way binding', function(assert) {
    // arrange, act

    var data = [{ field1: ko.observable(1), field2: ko.observable(2) }, { field1: ko.observable(3), field2: ko.observable(4) }];

    this.viewModel.gridOptions = {
        dataSource: data
    };

    // act
    var dataGrid = this.createDataGrid();

    // assert
    var $rows = $(dataGrid.$element().find(".dx-data-row"));
    assert.equal($rows.length, 2, "row count");
    assert.equal($rows.eq(0).children().eq(0).text(), "1");
    assert.equal($rows.eq(1).children().eq(0).text(), "3");

    // act
    data[0].field1(666);

    // assert
    $rows = $(dataGrid.$element().find(".dx-data-row"));
    assert.equal($rows.length, 2, "row count");
    assert.equal($rows.eq(0).children().eq(0).text(), "666");
    assert.equal($rows.eq(1).children().eq(0).text(), "3");
});

QUnit.test('Two-way binding disabled', function(assert) {
    // arrange, act

    var data = [{ field1: ko.observable(1), field2: ko.observable(2) }, { field1: ko.observable(3), field2: ko.observable(4) }];

    this.viewModel.gridOptions = {
        dataSource: data,
        twoWayBindingEnabled: false
    };

    // act
    var dataGrid = this.createDataGrid();

    // assert
    var $rows = $(dataGrid.$element().find(".dx-data-row"));
    assert.equal($rows.length, 2, "row count");
    assert.equal($rows.eq(0).children().eq(0).text(), "1");
    assert.equal($rows.eq(1).children().eq(0).text(), "3");

    // act
    data[0].field1(666);

    // assert
    $rows = $(dataGrid.$element().find(".dx-data-row"));
    assert.equal($rows.length, 2, "row count");
    assert.equal($rows.eq(0).children().eq(0).text(), "1");
    assert.equal($rows.eq(1).children().eq(0).text(), "3");
});

QUnit.test('$root model in rowTemplate', function(assert) {
    // arrange, act

    this.viewModel.rowClick = sinon.spy();
    this.viewModel.gridOptions = {
        rowTemplate: "testRowTemplate",
        dataSource: [{ id: 1 }, { id: 2 }]
    };

    var dataGrid = this.createDataGrid();
    var $rows = $(dataGrid.$element().find(".test-row"));

    // act
    $rows.eq(1).click();

    // assert
    assert.equal($rows.length, 2, "row count");
    assert.ok(this.viewModel.rowClick.calledOnce, "rowClick called once");
    assert.equal(this.viewModel.rowClick.getCall(0).args[0].data.id, 2, "rowClick args");
});

QUnit.module("Editing", {
    beforeEach: function() {
        this.clock = sinon.useFakeTimers();

        this.array = ko.observableArray([]);
        this.array.push({
            FirstName: ko.observable("Jon"),
            LastName: ko.observable("Smith")
        });
        this.editing = {
            mode: "row",
            allowUpdating: true,
            allowAdding: true,
            allowDeleting: true
        };

        this.viewModel = {
            gridOptions: {
                dataSource: this.array,
                editing: this.editing
            }
        };
        this.createDataGrid = function() {
            ko.applyBindings(this.viewModel, $("#landOfKO").get(0));

            var dataGrid = $("#dataGridKO").dxDataGrid("instance");
            this.clock.tick(500);
            return dataGrid;
        };
    },
    afterEach: function() {
        this.clock.restore();
    }
}, function() {
    // T566012
    QUnit.test("Row mode: DataSource fields should not be changed when editing row", function(assert) {
        // arrange
        var $input,
            dataGrid = this.createDataGrid();

        dataGrid.editRow(0);
        $input = $(dataGrid.element()).find(".dx-datagrid-rowsview .dx-editor-cell input").first();

        // assert
        assert.strictEqual($input.length, 1, "has input");

        // act
        $input.val("Test");
        $input.trigger("change");

        // assert
        assert.strictEqual(this.array()[0].FirstName(), "Jon", "first name has not changed");
    });

    // T566012
    QUnit.test("Batch mode: DataSource fields should not be changed when editing cell", function(assert) {
        // arrange
        this.editing.mode = "batch";
        var dataGrid = this.createDataGrid();

        // act
        dataGrid.cellValue(0, 0, "Test");

        // assert
        assert.strictEqual(this.array()[0].FirstName(), "Jon", "first name has not changed");
    });
});
