/*global QUnit*/
sap.ui.define([
	"sap/ui/thirdparty/jquery",
	"sap/m/ResponsivePopover",
	"sap/m/SinglePlanningCalendarGrid",
	"sap/m/SinglePlanningCalendarGridRenderer",
	"sap/m/library",
	"sap/ui/unified/CalendarAppointment",
	"sap/ui/events/KeyCodes",
	'sap/ui/unified/calendar/CalendarDate',
	"sap/ui/core/Core"
], function(
	jQuery,
	ResponsivePopover,
	SinglePlanningCalendarGrid,
	SinglePlanningCalendarGridRenderer,
	mobileLibrary,
	CalendarAppointment,
	KeyCodes,
	CalendarDate,
	oCore
) {
	"use strict";

	var PlacementType = mobileLibrary.PlacementType;

	QUnit.module("Other");

	QUnit.test("updateNowMarkerPosition and text is called on after rendering", function (assert) {
		// Arrange
		var oSPCGrid = new SinglePlanningCalendarGrid(),
			oUpdateRowHeaderAndNowMarkerSpy = this.spy(oSPCGrid, "_updateRowHeaderAndNowMarker");

		// Act
		oSPCGrid.onAfterRendering();

		// Assert
		assert.equal(oUpdateRowHeaderAndNowMarkerSpy.callCount, 1, "_updateRowHeaderAndNowMarker is called once onAfterRendering");

		// Cleanup
		oUpdateRowHeaderAndNowMarkerSpy.restore();
		oSPCGrid.destroy();
	});

	QUnit.test("_calculateVisibleBlockers", function(assert) {
		// prepare
		var aFullDayApps = [
				new CalendarAppointment({
					startDate: new Date(2018, 6, 7),
					endDate: new Date(2018, 6, 8)
				}),
				new CalendarAppointment({
					startDate: new Date(2018, 6, 8),
					endDate: new Date(2018, 6, 9)
				}),
				new CalendarAppointment({
					startDate: new Date(2018, 6, 6),
					endDate: new Date(2018, 6, 9)
				}),
				new CalendarAppointment({
					startDate: new Date(2018, 6, 7),
					endDate: new Date(2018, 6, 7)
				}),
				new CalendarAppointment({
					startDate: new Date(2018, 6, 8),
					endDate: new Date(2018, 6, 8)
				}),
				new CalendarAppointment({
					startDate: new Date(2018, 6, 9),
					endDate: new Date(2018, 6, 9)
				})
			],
			oStartDate = new Date(2018, 6, 8),
			oGrid = new SinglePlanningCalendarGrid({
				startDate: oStartDate,
				appointments: aFullDayApps
			}),
			oAppointmentsMap = oGrid._createAppointmentsMap(oGrid.getAppointments()),
			aVisibleBlockers;

		// act
		oGrid._setColumns(1);
		aVisibleBlockers = oGrid._calculateVisibleBlockers(oAppointmentsMap.blockers, CalendarDate.fromLocalJSDate(oStartDate), oGrid._getColumns());

		// assert
		assert.equal(aVisibleBlockers.length, 4, "Visible full day appointments are correct count");

		// cleanup
		oGrid.destroy();
	});

	QUnit.test("_getLineClamp", function (assert) {
		// Arrange
		var SPCRenderer = SinglePlanningCalendarGridRenderer,
			oAppStartDate = new Date("2018", "6", "9", "9", "0"),
			oAppEndDate = new Date("2018", "6", "9", "9", "20"),
			sLineClamp;

		// Act
		sLineClamp = SPCRenderer._getLineClamp(oAppStartDate, oAppEndDate);

		// Assert
		assert.equal(sLineClamp, "1", "One line of appointment text will be shown");

		//Arrange
		// Set appointment duration to 1 hour
		oAppStartDate = new Date("2018", "6", "9", "9", "0");
		oAppEndDate = new Date("2018", "6", "9", "10", "0");

		// Act
		sLineClamp = SPCRenderer._getLineClamp(oAppStartDate, oAppEndDate);

		// Assert
		assert.equal(sLineClamp, "2", "Two lines of appointment text will be shown");

		//Arrange
		// Set appointment duration to 1 hour and 20 minutes
		oAppStartDate = new Date("2018", "6", "9", "9", "0");
		oAppEndDate = new Date("2018", "6", "9", "10", "20");

		// Act
		sLineClamp = SPCRenderer._getLineClamp(oAppStartDate, oAppEndDate);

		// Assert
		assert.equal(sLineClamp, "3", "Three lines of appointment text will be shown");


		//Arrange
		// Set appointment duration to 1 hour 40 minutes
		oAppStartDate = new Date("2018", "6", "9", "9", "0");
		oAppEndDate = new Date("2018", "6", "9", "10", "40");

		// Act
		sLineClamp = SPCRenderer._getLineClamp(oAppStartDate, oAppEndDate);

		// Assert
		assert.equal(sLineClamp, "4", "Four lines of appointment text will be shown");

		//Arrange
		// Set appointment duration to 2 hours
		oAppStartDate = new Date("2018", "6", "9", "9", "0");
		oAppEndDate = new Date("2018", "6", "9", "11", "00");

		// Act
		sLineClamp = SPCRenderer._getLineClamp(oAppStartDate, oAppEndDate);

		// Assert
		assert.equal(sLineClamp, "5", "Five lines of appointment text will be shown");

		//Arrange
		// Set appointment duration to 2.15 hours
		oAppStartDate = new Date("2018", "6", "9", "9", "0");
		oAppEndDate = new Date("2018", "6", "9", "11", "15");

		// Act
		sLineClamp = SPCRenderer._getLineClamp(oAppStartDate, oAppEndDate);

		// Assert
		assert.equal(sLineClamp, "6", "Six lines of appointment text will be shown");

		//Arrange
		// Set appointment duration to 2.30 hours
		oAppStartDate = new Date("2018", "6", "9", "9", "0");
		oAppEndDate = new Date("2018", "6", "9", "11", "30");

		// Act
		sLineClamp = SPCRenderer._getLineClamp(oAppStartDate, oAppEndDate);

		// Assert
		assert.equal(sLineClamp, "7", "Seven lines of appointment text will be shown");

		//Arrange
		// Set appointment duration to 3 hours
		oAppStartDate = new Date("2018", "6", "9", "9", "0");
		oAppEndDate = new Date("2018", "6", "9", "12", "00");

		// Act
		sLineClamp = SPCRenderer._getLineClamp(oAppStartDate, oAppEndDate);

		// Assert
		assert.equal(sLineClamp, "8", "Eight lines of appointment text will be shown");
	});

	QUnit.test("_getCellStartEndInfo start/end format for 12-hour clocks", function (assert) {
		// Prepare
		oCore.getConfiguration().setFormatLocale("en-US");

		var oGrid = new SinglePlanningCalendarGrid(),
			oMockStardDate = new Date(2019, 7, 5, 10),
			oMockEndDate = new Date(2019, 7, 5, 15),
			sExpectedInfo = oGrid._oUnifiedRB.getText("CALENDAR_START_TIME") + ": Monday 05/08/2019 at 10:00:00 AM; " +
				oGrid._oUnifiedRB.getText("CALENDAR_END_TIME") + ": Monday 05/08/2019 at 3:00:00 PM";

		oGrid.placeAt("qunit-fixture");
		oCore.applyChanges();

		// Assert
		assert.strictEqual(oGrid._getCellStartEndInfo(oMockStardDate, oMockEndDate), sExpectedInfo, "Cell's start/end info is properly formatted");

		// Destroy
		oGrid.destroy();
	});

	QUnit.test("_getCellStartEndInfo start/end format for 24-hour clocks", function (assert) {
		// Prepare
		oCore.getConfiguration().setFormatLocale("en-GB");

		var oGrid = new SinglePlanningCalendarGrid(),
			oMockStardDate = new Date(2019, 7, 5, 10),
			oMockEndDate = new Date(2019, 7, 5, 15),
			sExpectedInfo = oGrid._oUnifiedRB.getText("CALENDAR_START_TIME") + ": Monday 05/08/2019 at 10:00:00; " +
				oGrid._oUnifiedRB.getText("CALENDAR_END_TIME") + ": Monday 05/08/2019 at 15:00:00";

		oGrid.placeAt("qunit-fixture");
		oCore.applyChanges();

		// Assert
		assert.strictEqual(oGrid._getCellStartEndInfo(oMockStardDate, oMockEndDate), sExpectedInfo, "Cell's start/end info is properly formatted");

		// Destroy
		oGrid.destroy();
	});

	QUnit.test("applyFocusInfo", function(assert) {
		// prepare
		var oAppointment = new CalendarAppointment({
				startDate: new Date(2018, 6, 14, 5),
				endDate: new Date(2018, 6, 14, 6),
				selected: false
			}),
			oGrid = new SinglePlanningCalendarGrid({
				startDate: new Date(2018, 6, 8),
				appointments: [oAppointment]
			}),
			oPopover = new ResponsivePopover({
				placement: PlacementType.Auto
			}),
			fnApplyFocusInfoSpy = this.spy(oGrid, "applyFocusInfo");

		oGrid.placeAt("qunit-fixture");
		oCore.applyChanges();

		// act
		oAppointment.getDomRef().focus();
		oGrid._toggleAppointmentSelection(oAppointment, true);
		oPopover.openBy(oAppointment);
		this.clock.tick(500);

		// assert
		assert.ok(oPopover.isOpen(), "The popover is opened");
		assert.strictEqual(oPopover.getDomRef().id, document.activeElement.id, "The popover is the active DOM element");

		// act
		oPopover.close();
		this.clock.tick(500);

		// assert
		assert.ok(fnApplyFocusInfoSpy.calledOnce, "applyFocusInfo was called");
		assert.strictEqual(oAppointment.getDomRef().id, document.activeElement.id, "Focus is back on the appointment");

		// cleanup
		oGrid.destroy();
	});

	QUnit.test("_getVisibleStartHour returns the proper start hour", function (assert) {
		// Prepare
		var oGrid = new SinglePlanningCalendarGrid();

		// Assert
		assert.strictEqual(oGrid._getVisibleStartHour(), 0, "Correct when there is no value set to the startHour property and fullDay is set to true");

		// Act
		oGrid.setFullDay(false);

		// Assert
		assert.strictEqual(oGrid._getVisibleStartHour(), 0, "Correct when there is no value set to the startHour property and fullDay is set to false");

		// Act
		oGrid.setStartHour(8);

		// Assert
		assert.strictEqual(oGrid._getVisibleStartHour(), 8, "Correct when there is a value set to the startHour property and fullDay is set to false");

		// Act
		oGrid.setFullDay(true);

		// Assert
		assert.strictEqual(oGrid._getVisibleStartHour(), 0, "Correct when there is a value set to the startHour property and fullDay is set to true");

		// Destroy
		oGrid.destroy();
	});

	QUnit.test("_getVisibleEndHour returns the proper end hour", function (assert) {
		// Prepare
		var oGrid = new SinglePlanningCalendarGrid();

		// Assert
		assert.strictEqual(oGrid._getVisibleEndHour(), 23, "Correct when there is no value set to the endHour property and fullDay is set to true");

		// Act
		oGrid.setFullDay(false);

		// Assert
		assert.strictEqual(oGrid._getVisibleEndHour(), 23, "Correct when there is no value set to the endHour property and fullDay is set to false");

		// Act
		oGrid.setEndHour(20);

		// Assert
		assert.strictEqual(oGrid._getVisibleEndHour(), 19, "Correct when there is a value set to the endHour property and fullDay is set to false");

		// Act
		oGrid.setFullDay(true);

		// Assert
		assert.strictEqual(oGrid._getVisibleEndHour(), 23, "Correct when there is a value set to the endHour property and fullDay is set to true");

		// Destroy
		oGrid.destroy();
	});

	QUnit.test("_isVisibleHour works correctly", function (assert) {
		// Prepare
		var oGrid = new SinglePlanningCalendarGrid();

		// Assert
		assert.strictEqual(oGrid._isVisibleHour(5), true, "valid hour and neither start nor end hour set");
		assert.strictEqual(oGrid._isVisibleHour(25), false, "invalid hour passed");

		// Prepare
		oGrid.setStartHour(8);

		// Assert
		assert.strictEqual(oGrid._isVisibleHour(5), false, "start hour set, the passed value is outside visible range");
		assert.strictEqual(oGrid._isVisibleHour(8), true, "start hour set, the passed value is the same as the start hour");
		assert.strictEqual(oGrid._isVisibleHour(20), true, "start hour set, the passed value is inside visible range");

		// Prepare
		oGrid.setEndHour(20);

		// Assert
		assert.strictEqual(oGrid._isVisibleHour(5), false, "start and end hour set, the passed value is outside visible range");
		assert.strictEqual(oGrid._isVisibleHour(8), true, "start and end hour set, the passed value is the same as the start hour");
		assert.strictEqual(oGrid._isVisibleHour(12), true, "start and end hour set, the passed value is inside visible range");
		assert.strictEqual(oGrid._isVisibleHour(20), false, "start and end hour set, the passed value is the same as the end hour");
		assert.strictEqual(oGrid._isVisibleHour(21), false, "start and end hour set, the passed value is outside visible range");

		// Prepare
		oGrid.setStartHour(21);
		oGrid.setEndHour(11);

		// Assert
		assert.strictEqual(oGrid._isVisibleHour(0), true, "start hours and end hour in next day set, the passed value is inside visible range");
		assert.strictEqual(oGrid._isVisibleHour(21), true, "start hours and end hour in next day set, the passed value is the same as the start hour");
		assert.strictEqual(oGrid._isVisibleHour(11), false, "start hours and end hour in next day set, the passed value is the same as the end hour");
		assert.strictEqual(oGrid._isVisibleHour(16), false, "start hours and end hour in next day set, the passed value is outside visible range");

		// Destroy
		oGrid.destroy();
	});

	QUnit.test("_findSrcControl", function(assert) {
		// Prepare
		var oAppointment = new CalendarAppointment({
				startDate: new Date(2022,0,20),
				endDate: new Date(2022,11,31)
			}),
			oGrid = new SinglePlanningCalendarGrid({
				startDate: new Date(2022,0,25),
				appointments: [oAppointment]
			}),
			oFireAppointmentSelectSpy = this.spy(oGrid, "fireAppointmentSelect");

		oGrid.placeAt("qunit-fixture");
		sap.ui.getCore().applyChanges();

		// Act
		oGrid.ontap({
			target: {
				parentElement: oAppointment.getDomRef(),
				classList: {
					contains: function() {
						return false;
					}
				}
			}
		});

		// Assert
		assert.ok(oFireAppointmentSelectSpy.calledOnce, "AppointmentSelect event is fired");

		// Destroy
		oGrid.destroy();

	});

	QUnit.test("Formatters pattern is correct", function (assert) {
		// Prepare
		var oGrid = new SinglePlanningCalendarGrid();

		assert.strictEqual(oGrid._oFormatStartEndInfoAria.oFormatOptions.pattern.indexOf("EEEE dd/MM/yyyy"), 0, "Start End Aria info pattern is correct (contains 'yyyy' instead of 'YYYY'");
		assert.strictEqual(oGrid._oFormatAriaFullDayCell.oFormatOptions.pattern.indexOf("EEEE dd/MM/yyyy"), 0, "Full day cell Aria info pattern is correct (contains 'yyyy' instead of 'YYYY'");
		assert.strictEqual(oGrid._getDateFormatter().oFormatOptions.pattern, "yyyyMMdd-HHmm", "Grid cell pattern is correct (contains 'yyyy' instead of 'YYYY'");

	});

	QUnit.module("Events");

	QUnit.test("appointmentSelect: select a single appointment", function (assert) {
		var oAppointment = new CalendarAppointment(),
			oGrid = new SinglePlanningCalendarGrid({
				appointments: [
					oAppointment
				]
			}),
			oFakeEvent = {
				target: {
					classList: {
						contains: function() {
							return false;
						}
					}
				},
				srcControl: oAppointment
			},
			fnFireAppointmentSelectSpy = this.spy(oGrid, "fireAppointmentSelect");

		//act
		oGrid.ontap(oFakeEvent);

		//assert
		assert.ok(fnFireAppointmentSelectSpy.calledOnce, "Event was fired");
		assert.ok(fnFireAppointmentSelectSpy.calledWithExactly({
			appointment: oAppointment,
			appointments: [oAppointment],
			id: oGrid.getId()
		}), "Event was fired with the correct parameters");

		//clean up
		oGrid.destroy();
	});

	QUnit.test("appointmentSelect: deselect all appointments", function (assert) {
		var oGrid = new SinglePlanningCalendarGrid({
				appointments: [
					new CalendarAppointment({
						startDate: new Date(2018, 6, 8, 5),
						endDate: new Date(2018, 6, 8, 6),
						selected: true
					}),
					new CalendarAppointment({
						startDate: new Date(2018, 6, 9, 4),
						endDate: new Date(2018, 6, 10, 4),
						selected: true
					})
				]
			}),
			oFakeEvent = {
				target: jQuery("<div></div>").attr({
					"data-sap-start-date": "20180708-0300",
					"data-sap-end-date": "20180708-0400",
					"class": "sapMSinglePCRow"
				}).get(0)
			},
			fnFireAppointmentSelectSpy = this.spy(oGrid, "fireAppointmentSelect");

		//act
		oGrid.ontap(oFakeEvent);

		//assert
		assert.ok(fnFireAppointmentSelectSpy.calledOnce, "Event was fired");
		assert.ok(fnFireAppointmentSelectSpy.calledWith({
			appointment: undefined,
			appointments: oGrid.getAggregation("appointments"),
			id: oGrid.getId()
		}), "Event was fired with the correct parameters");

		//clean up
		oGrid.destroy();
	});

	QUnit.test("cellPress", function(assert) {
		// prepare
		var oGrid = new SinglePlanningCalendarGrid({
				startDate: new Date(2018, 6, 8)
			}),
			oFakeEvent = {
				target: jQuery("<div></div>").attr({
					"data-sap-start-date": "20180708-0300",
					"data-sap-end-date": "20180708-0400",
					"class": "sapMSinglePCRow"
				}).get(0),
				which: KeyCodes.ENTER,
				preventDefault: function() {}
			},
			fnFireGridCellFocusSpy = this.spy(oGrid, "fireEvent");

		// act
		oGrid._fireSelectionEvent(oFakeEvent);

		// assert
		assert.ok(fnFireGridCellFocusSpy.withArgs("cellPress").calledOnce, "Event was fired");
		assert.ok(fnFireGridCellFocusSpy.calledWithExactly("cellPress", {
			startDate: new Date(2018, 6 , 8, 3),
			endDate: new Date(2018, 6, 8, 4),
			id: oGrid.getId()
		}), "Event was fired with the correct parameters");

		// cleanup
		oGrid.destroy();
	});

	QUnit.test("borderReached: when focus is on appointment and we are navigating in backward direction on week view", function(assert) {
		// prepare
		var oAppointment = new CalendarAppointment({
				startDate: new Date(2018, 6, 8, 5),
				endDate: new Date(2018, 6, 8, 6),
				selected: true
			}),
			oGrid = new SinglePlanningCalendarGrid({
				startDate: new Date(2018, 6, 8),
				appointments: [oAppointment]
			}),
			oFakeEvent = {
				target: {
					id: oAppointment.getId(),
					classList: {
						contains: function() {
							return false;
						}
					},
					which: KeyCodes.ARROW_LEFT
				},
				preventDefault: function() {}
			},
			fnFireBorderReachedSpy = this.spy(oGrid, "fireEvent"),
			fnFireAppointmentSelectSpy = this.spy(oGrid, "fireAppointmentSelect");

		// act
		oGrid.onsapleft(oFakeEvent);

		// assert
		assert.ok(fnFireBorderReachedSpy.withArgs("borderReached").calledOnce, "Event was fired");
		assert.ok(fnFireBorderReachedSpy.calledWithExactly("borderReached", {
			startDate: new Date(2018, 6, 8, 5),
			next: false,
			fullDay: false,
			id: oGrid.getId()
		}), "Event was fired with the correct parameters");

		assert.ok(fnFireAppointmentSelectSpy.calledOnce, "FireAppointmentSelect is called once");
		assert.notOk(oAppointment.getSelected(), "Appointment is deselected");

		// cleanup
		oGrid.destroy();
	});

	QUnit.test("borderReached: when focus is on appointment and we are navigating in forward direction on week view", function(assert) {
		// prepare
		var oAppointment = new CalendarAppointment({
				startDate: new Date(2018, 6, 14, 5),
				endDate: new Date(2018, 6, 14, 6),
				selected: true
			}),
			oGrid = new SinglePlanningCalendarGrid({
				startDate: new Date(2018, 6, 8),
				appointments: [oAppointment]
			}),
			oFakeEvent = {
				target: {
					id: oAppointment.getId(),
					classList: {
						contains: function() {
							return false;
						}
					},
					which: KeyCodes.ARROW_RIGHT
				},
				preventDefault: function() {}
			},
			fnFireBorderReachedSpy = this.spy(oGrid, "fireEvent"),
			fnFireAppointmentSelectSpy = this.spy(oGrid, "fireAppointmentSelect");

		// act
		oGrid.onsapright(oFakeEvent);

		// assert
		assert.ok(fnFireBorderReachedSpy.withArgs("borderReached").calledOnce, "Event was fired");
		assert.ok(fnFireBorderReachedSpy.calledWithExactly("borderReached", {
			startDate: new Date(2018, 6, 14, 5),
			next: true,
			fullDay: false,
			id: oGrid.getId()
		}), "Event was fired with the correct parameters");

		assert.ok(fnFireAppointmentSelectSpy.calledOnce, "fireAppointmentSelect is called");
		assert.notOk(oAppointment.getSelected(), "Appointment is deselected");

		// cleanup
		oGrid.destroy();
	});

	QUnit.test("borderReached: when focus is on grid cell and we are navigation in backward direction on week view", function(assert) {
		// prepare
		var oGrid = new SinglePlanningCalendarGrid({
				startDate: new Date(2018, 6, 8)
			}),
			oFakeEvent = {
				target: jQuery("<div></div>").attr({
					"data-sap-start-date": "20180708-0300",
					"data-sap-end-date": "20180708-0400",
					"class": "sapMSinglePCRow"
				}).get(0),
				which: KeyCodes.ARROW_LEFT,
				preventDefault: function() {}
			},
			fnFireBorderReachedSpy = this.spy(oGrid, "fireEvent");

		// act
		oGrid.onsapleft(oFakeEvent);

		// assert
		assert.ok(fnFireBorderReachedSpy.withArgs("borderReached").calledOnce, "Event was fired");
		assert.ok(fnFireBorderReachedSpy.calledWithExactly("borderReached", {
			startDate: new Date(2018, 6, 8, 3),
			next: false,
			fullDay: false,
			id: oGrid.getId()
		}), "Event was fired with the correct parameters");

		// cleanup
		oGrid.destroy();
	});

	QUnit.test("borderReached: when focus is on grid cell and we are navigation in forward direction on week view", function(assert) {
		// prepare
		var oGrid = new SinglePlanningCalendarGrid({
				startDate: new Date(2018, 6, 8)
			}),
			oFakeEvent = {
				target: jQuery("<div></div>").attr({
					"data-sap-start-date": "20180714-0300",
					"data-sap-end-date": "20180714-0400",
					"class": "sapMSinglePCRow"
				}).get(0),
				which: KeyCodes.ARROW_RIGHT,
				preventDefault: function() {}
			},
			fnFireBorderReachedSpy = this.spy(oGrid, "fireEvent");

		// act
		oGrid.onsapright(oFakeEvent);

		// assert
		assert.ok(fnFireBorderReachedSpy.withArgs("borderReached").calledOnce, "Event was fired");
		assert.ok(fnFireBorderReachedSpy.calledWithExactly("borderReached", {
			startDate: new Date(2018, 6, 14, 3),
			next: true,
			fullDay: false,
			id: oGrid.getId()
		}), "Event was fired with the correct parameters");

		// cleanup
		oGrid.destroy();
	});
});
