/*
 * index/viewmodel.js
 *
 * Copyright (c) CorvusGPS.com, 2015. All rights reserved.
 */

/**
 * Hides all the popovers from the current page.
 * 
 * @param {boolean} isClick Indicates whether a click is the source of this action.
 */
function removeAllPopovers(isClick) {
    "use strict";
    var popovers = $(".popover");
    $(popovers).removeClass("visible");
    if (isClick) {
        // this causes serious problems when used in different parts than clicking on a popover element
        $(popovers).css("display", "none");
    }

    $("div.backdrop").remove();
}

// viewmodel item for storing device data
function Device(data) {
    "use strict";
    this.id = ko.observable(data.id);
    this.name = ko.observable(data.name);
}

// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
function MapViewModel() {
    "use strict";
    var self = this;

    /* region: viewstate collections for the popovers */
    self.items = ko.observableArray([
        { id: -1, name: "Loading items..." }
    ]);
    self.modes = ko.observableArray([
        { id: 0, name: "Last position" },
        { id: 1, name: "Interval (last hour)" }
    ]);

    self.itemListTitle = ko.observable("Select a device...");
    self.resultModeListTitle = ko.observable("Last position");
    /* endregion */

    /* region: viewstate variables for creating a query object */
    self.isDevice = ko.observable(true);
    self.isSingleResult = ko.observable(true);
    self.did = "0";
    self.gid = "0";
    self.updateTimestamp = 0; // timestamp used to avoid full database search on location updates
    self.dateStart = (new Date().getTime() / 1000) - 60 * 60; // default value: last 1 hour
    self.dateEnd = (new Date().getTime() / 1000); // default value: current time
    self.isCurrent = true;
    self.report_type = "all";
    /* endregion */

    /* region: viewstate elements for the (auto) updating */
    self.queryObject = {
        mode: "device",
        did: "1",
        gid: "1",
        end: (new Date().getTime() / 1000),
        current: true,
        report_type: "all"
    };

    self.hasNoValidData = ko.observable(false);
    self.isAutoUpdateEnabled = ko.observable(true);
    self.autoUpdateInterval = setInterval(function () {
        self.updateMap(self.isDevice());
    }, 10000);
    self.autoAuthenticateInterval = setInterval(function () {
        corvusGps.refreshLogin();
    }, 2700000);
    /* endregion */

    /* view state variables */
    self.isManualUpdateAvailable = ko.observable(true);
    self.selectedItemId;
    /* endregion */

    /**
     * Updates the starting and ending dates to the current time in UNIX milliseconds.
     */
    self.updateTimeStamps = function () {
        self.dateEnd = (new Date().getTime() / 1000);
        self.dateStart = self.dateEnd - 60 * 60;
    }

    /**
     * Builds up the query object against the API from the current viewstate variables.
     */
    self.updateQueryObject = function () {
        var mode = "";
        if (self.isDevice()) {
            // device mode
            mode = "device";
            if (self.isSingleResult()) {
                // device mode + single result
                self.queryObject = {
                    mode: mode,
                    did: self.did,
                    single_or_multi: "single",
                    timestamp: self.updateTimestamp,
                    end: self.dateEnd,
                    current: self.isCurrent,
                    report_type: self.report_type
                };
            } else {
                // device mode + multiple results
                self.queryObject = {
                    mode: mode,
                    did: self.did,
                    single_or_multi: "multi",
                    start: self.dateStart,
                    end: self.dateEnd,
                    current: self.isCurrent,
                    report_type: self.report_type
                };
            }
        } else {
            // group mode
            mode = "group";
            self.queryObject = {
                mode: mode,
                gid: self.gid,
                group_time: self.updateTimestamp,
                report_type: self.report_type
            };
        }
    }

    /**
     * Parses the JSON containing the device or group collection.
     */
    self.parseItemListContent = function (data) {
        var itemList = $.map(data, function (v, i) {
            return new Device({ id: i, name: v });
        });

        // sorts the item list by the description
        itemList.sort(function (a, b) {
            return a.name().localeCompare(b.name());
        });

        // if no items were received
        if (itemList.length < 1) {
            if (self.isDevice()) {
                itemList.push(new Device({ id: -1, name: "(No devices have been added to your account)" }));
            } else {
                itemList.push(new Device({ id: -1, name: "(No groups have been added to your account)" }));
            }
        }

        self.items(itemList);
    }

    /**
     * Invokes the AJAX functions for getting updated device or group list.
     */
    self.updateItemList = function () {
        removeAllPopovers(false);
        self.items.removeAll();
        self.items([
            { id: -1, name: "Loading items..." }
        ]);

        if (self.isDevice()) {
            // if device list is going to be updated
            corvusGps.getDeviceCollection("", self.parseItemListContent, null);
        } else {
            // if group list is going to be updated
            corvusGps.getGroupCollection("", self.parseItemListContent, null);
        }
    }

    /**
     * Makes the selected device or group active and displays its location on the map.
     */
    self.selectItem = function (item) {
        removeAllPopovers(true);
        corvusGpsMapping.clearPopups();
        corvusGpsMapping.setIsNewItemSelected(true);

        if (item.id() >= 0) {
            self.hasNoValidData(false);
            self.itemListTitle(item.name().toString());

            self.selectedItemId = item.id();
            self.did = item.id();
            self.gid = item.id();

            self.updateTimestamp = 0; // performs full update when a new item has been selected
            self.updateTimeStamps();
            self.updateQueryObject();
            self.updateMap();
        }
    }

    /**
     * Toggles between devices and groups, updates their list with the respective items.
     */
    self.toggleGroups = function () {
        removeAllPopovers(false);
        corvusGpsMapping.clearPopups();
        corvusGpsMapping.setIsNewItemSelected(true);
        if (!self.isDevice()) {
            self.modes = ko.observableArray([
                { id: 0, name: "Last position" },
                { id: 1, name: "Interval (last hour)" }
            ]);

            // if previously groups were used, switches back to devices
            self.itemListTitle("Select a device...");
            self.isDevice(true);
            if (self.isSingleResult()) {
                self.toggleResultMode(self.modes()[0], false);
            } else {
                self.toggleResultMode(self.modes()[1], false);
            }
        } else {
            self.modes = ko.observableArray([
                { id: 0, name: "Last position" }
            ]);

            // if devices were used, switches to groups
            self.itemListTitle("Select a group...");
            self.isDevice(false);
            self.toggleResultMode(self.modes()[0], false);
        }

        corvusGpsMapping.setIsDevice(self.isDevice());
        self.selectedItemId = undefined;

        self.updateTimeStamps();
        self.updateQueryObject();
        self.updateItemList();

        // re-enables auto update, with the proper internal state (will prevent panning for groups)
        self.isAutoUpdateEnabled(self.isAutoUpdateEnabled() ^ true);
        self.toggleAutoUpdate();
    }

    /**
     * Toggles between single and multiple coordinates results, for devices mode only.
     */
    self.toggleResultMode = function (resultMode, isUpdateRequired) {
        if (typeof isUpdateRequired === "undefined") {
            isUpdateRequired = true;
        }

        corvusGpsMapping.clearPopups();
        corvusGpsMapping.setIsNewItemSelected(true);
        self.hasNoValidData(false);
        removeAllPopovers(false);
        try {
            // multiple results mode is valid only for a single selected device
            if (self.isDevice()) {
                self.resultModeListTitle(resultMode.name);
                switch (resultMode.id) {
                    case 0:
                        self.isSingleResult(true);
                        break;
                    case 1:
                        self.isSingleResult(false);
                        break;
                    default:
                        self.isSingleResult(true);
                }

                corvusGpsMapping.setIsSingleResult(self.isSingleResult());

                self.updateTimestamp = 0; // performs full update when a new mode has been selected
                self.updateTimeStamps();
                self.updateQueryObject();

                if (isUpdateRequired) {
                    self.updateMap();
                }
            } else {
                // to maintain proper state, for groups only the single result mode can be activated
                self.resultModeListTitle(self.modes()[0].name);
                self.isSingleResult(true);
                corvusGpsMapping.setIsSingleResult(self.isSingleResult());
                self.updateTimeStamps();
                self.updateQueryObject();
            }
        } catch (e) {
            console.log("Error occurred while switching result mode: " + e.toString());
        }
    };

    /**
     * Toggles between automatic and manual map data updates.
     */
    self.toggleAutoUpdate = function () {
        clearInterval(self.autoUpdateInterval);
        if (!self.isAutoUpdateEnabled()) {
            self.isAutoUpdateEnabled(true);
            self.autoUpdateInterval = setInterval(function () {
                // maintaining the argument of the updateMap, is essential for preventing panning for groups
                self.updateMap(self.isDevice());
            }, 10000);
        } else {
            self.isAutoUpdateEnabled(false);
        }
    }

    /**
     * Performs a manual map update.
     */
    self.manualUpdate = function () {
        if (self.isManualUpdateAvailable()) {
            self.isManualUpdateAvailable(false);

            // re-enables manual update after 5 secs
            setTimeout(function () {
                self.isManualUpdateAvailable(true);
            }, 5000);

            // restarts the auto update 10 secs interval
            clearInterval(self.autoUpdateInterval);
            if (self.isAutoUpdateEnabled()) {
                self.autoUpdateInterval = setInterval(function () {
                    self.updateMap(self.isDevice());
                }, 10000);
            }

            self.updateMap();
        }
    }

    /**
     * Updates the map with data specified by the current query object.
     * 
     * @param {boolean} isPanToRequired Indicates whether the map will be panned to center of the received geo data.
     */
    self.updateMap = function (isPanToRequired) {
        if (typeof (isPanToRequired) === "undefined") {
            isPanToRequired = true;
        }

        if (corvusGpsMapping.getIsMapReady() && self.selectedItemId !== undefined) {
            self.updateTimeStamps();
            self.updateQueryObject();
            corvusGps.getMapData(
                "",
                self.queryObject,
                function (data) {
                    if (data.length !== 0) {
                        self.hasNoValidData(false);
                        if (self.updateTimestamp < data["extra"]["max_timestamp"]) {
                            self.updateTimestamp = data["extra"]["max_timestamp"];
                        }

                        corvusGpsMapping.clearAdditionalMapLayers();
                        corvusGpsMapping.updateMapWithData(data, isPanToRequired);
                    } else {
                        if (self.updateTimestamp === 0) {
                            self.hasNoValidData(true);
                            console.log("No map data was received.");
                        }
                    }
                },
                function () {
                    console.log("Error while querying map data.");
                }
            );
        }
    }

    /**
     * Performs the logout.
     */
    self.logout = function () {
    	if (navigator.notification && navigator.notification.confirm)
    	{
			navigator.notification.confirm(
						"This will log you out.",
						function (buttonIndex) {
							if (buttonIndex === 1) {
								if (self.isAutoUpdateEnabled()) {
									self.toggleAutoUpdate();
								}

								corvusGps.removeAuthenticationData();
								window.localStorage.clear();
								window.plugins.insomnia.allowSleepAgain();
								window.location.replace("login.html");
							}
						},
						"Log out");
    	}
        
    else
		{
			corvusGps.removeAuthenticationData();
			window.localStorage.clear();
			if(window.plugins)
				window.plugins.insomnia.allowSleepAgain();
			window.location.replace("login.html");
		}
    }

    /**
     * Prepares the view model for running more efficiently in the background, therefore suspends all the current timers.
     */
    self.suspendCurrentTimers = function () {
        clearInterval(self.autoUpdateInterval);
        clearInterval(self.autoAuthenticateInterval);
    }

    /**
     * Restores the previously suspended timers.
     */
    self.restoreCurrentTimers = function () {
        clearInterval(self.autoUpdateInterval);
        if (self.isAutoUpdateEnabled()) {
            self.autoUpdateInterval = setInterval(function () {
                self.updateMap(self.isDevice());
            }, 10000);
        }

        // resets auto authenticate timer, just in case
        clearInterval(self.autoAuthenticateInterval);
        self.autoAuthenticateInterval = setInterval(function () {
            corvusGps.refreshLogin();
        }, 2700000);
    }

    /**
     * Restores the previously saved state of the viewmodel.
     * 
     * @param {string} viewStateJson Contains the JSON serialized view state object.
     */
    self.restoreViewState = function (viewStateJson) {
        console.log("indexviewmodel.js/restoreViewState(): Restoring viewstate...");
        if (typeof (viewStateJson) !== "undefined" && viewStateJson != null && viewStateJson.length > 0) {
            var viewState = JSON.parse(viewStateJson);
            if (typeof (viewState) !== "undefined" && viewState != null) {
                self.items(viewState.items);
                self.isDevice(viewState.isDevice);
                corvusGpsMapping.setIsDevice(self.isDevice());
                self.isSingleResult(viewState.isSingleResult);
                if (self.isSingleResult() || !self.isDevice()) {
                    self.toggleResultMode(self.modes[0]);
                } else {
                    self.toggleResultMode(self.modes[1]);
                }

                corvusGpsMapping.setIsSingleResult(self.isSingleResult());
                self.updateItemList();

                self.did = viewState.did;
                self.gid = viewState.gid;
                self.updateTimestamp = viewState.updateTimestamp;
                self.dateStart = viewState.dateStart;
                self.dateEnd = viewState.dateEnd;
                self.isCurrent = viewState.isCurrent;
                self.report_type = viewState.report_type;
                self.queryObject = viewState.queryObject;
                self.isAutoUpdateEnabled(viewState.isAutoUpdateEnabled);
                self.restoreCurrentTimers();

                self.isManualUpdateAvailable(true);
                self.itemListTitle(viewState.itemListTitle);
                self.resultModeListTitle(viewState.resultModeListTitle);
                self.selectedItemId = viewState.selectedItemId;

                // after restoring the view state, updates the map
                self.updateTimeStamps();
                self.updateQueryObject();
                self.updateMap(false);
            }
        }
    }

    /* region: initialization code */
    corvusGpsMapping.initializeLeaflet("map");

    var viewStateStorageJson = window.localStorage.getItem("indexViewState");
    if (typeof (viewStateStorageJson) !== "undefined" && viewStateStorageJson != null && viewStateStorageJson.length > 0) {
        self.restoreViewState(viewStateStorageJson);
    } else {
        corvusGps.getDeviceCollection("", self.parseItemListContent, null);
    }

    // clears the stored view state from the local storage in every case
    // as we do not want to restore that saved state anymore
    window.localStorage.removeItem("indexViewState");
    /* endregion */
}

// places the view model into global variable
index = { viewModel: new MapViewModel() };

// Activates knockout.js
var options = {
    attribute: "data-bind",        // default "data-sbind"
    globals: window,               // default {}
    bindings: ko.bindingHandlers,  // default ko.bindingHandlers
    noVirtualElements: false       // default true
};
ko.bindingProvider.instance = new ko.secureBindingsProvider(options);
ko.applyBindings(index.viewModel);