/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

var packages = [];

if (getStorage("packages") != null) {
    packages = JSON.parse(getStorage("packages"));
}

/**
 * Count how many items are still undelivered for a location.
 * @param {type} location An item in the packages array.
 * @returns {Number}
 */
function getUndeliveredCount(location) {
    var undelivered = 0;
    for (var i = 0; i < location.items.length; i++) {
        if (!location.items[i].delivered) {
            undelivered++;
        }
    }
    return undelivered;
}

function getPackage(packageid) {
    for (var i = 0; i < packages.length; i++) {
        for (var j = 0; j < packages[i].items.length; j++) {
            if (packages[i].items[j].id == packageid) {
                return packages[i].items[j];
            }
        }
    }
}

function getIconForType(type) {
    return SETTINGS.itemtypes[type].icon;
}

function getMapIconForItems(items) {
    var types = {};
    var deliveredcount = 0;
    var type = "package";
    for (var i = 0; i < items.length; i++) {
        // Don't consider delivered packages when determining icon,
        // only count them to check if everything is delivered
        if (items[i].delivered) {
            deliveredcount++;
            continue;
        }
        if (isNaN(types[items[i].type])) {
            types[items[i].type] = 0;
        }
        types[items[i].type]++;
    }

    if (deliveredcount == items.length) {
        return "check";
    }

    item_types = 0;
    icon = "multiple-items";
    // Count how many types we have, and set/overwrite the icon assuming we
    // only have that type.  If we end up with multiple types, we return that
    // icon instead of a specific one.
    //console.log(types);
    for (var type in types) {
        //console.log(type);
        item_types++;
        if (types[type] == 1) {
            icon = SETTINGS.itemtypes[type].mapicon;
        } else {
            icon = SETTINGS.itemtypes[type].pluralmapicon;
        }
        //console.log(icon);
    }
    if (item_types > 1) {
        return "multiple-items";
    }
    return icon;
}

function addPackage(address, latitude, longitude, type, callback, deadline) {
    var added = false;
    if (typeof type == 'undefined') {
        type = SETTINGS.itemtypes[0].id;
    }
    if (typeof deadline == 'undefined') {
        deadline = false;
    }

    if (typeof address == "object") {
        var extendedaddress = address;
        address = extendedaddress.address;
    } else {
        var extendedaddress = false;
    }

    // Extra precision makes the map stupider,
    // and doesn't really increase accuracy since four decimal places ~= 11m
    latitude = +(parseFloat("" + latitude).toFixed(4));
    longitude = +(parseFloat("" + longitude).toFixed(4));

    var packageID = uuidv4();
    var coordsID = "";

    for (var i = 0; i < packages.length; i++) {
        if (packages[i].coords[0] == latitude && packages[i].coords[1] == longitude) {
            coordsID = packages[i].id;
            packages[i].items.push({
                extended: extendedaddress,
                address: address,
                delivered: false,
                type: type,
                deadline: deadline,
                id: packageID
            });
            added = true;
            break;
        }
    }
    if (!added) {
        coordsID = uuidv4();
        packages.push({
            coords: [
                latitude,
                longitude
            ],
            id: coordsID,
            address: address,
            items: [
                {
                    extended: extendedaddress,
                    address: address,
                    delivered: false,
                    type: type,
                    deadline: deadline,
                    id: packageID
                }
            ]
        });
    }
    setStorage("packages", JSON.stringify(packages));

    appendActivityLog("Added", SETTINGS.itemtypes[type].name, address, "fas fa-truck-loading");

    playSound("ok");

    app.toast.show({
        text: SETTINGS.itemtypes[type].name + ' Added!<br><span style="font-size: 80%;">' + address + "</span>",
        position: "bottom",
        destroyOnClose: true,
        closeTimeout: 1000 * 3
    });

    if (map != null) {
        reloadMap();
    }

    if (typeof callback == 'function') {
        callback({
            coordsID: coordsID,
            packageID: packageID
        });
    }

    addAutofillEntry(address);
}

/**
 * Import a second package list and merge it with the existing one.
 * @param {type} newlist
 * @return {number} The number of packages that were skipped because they already exist locally.
 */
function importPackageList(newlist) {
    skipped = 0;
    let count = 0;
    for (latlng in newlist) {
        var latitude = newlist[latlng].coords[0];
        var longitude = newlist[latlng].coords[1];

        latitude = +(parseFloat("" + latitude).toFixed(4));
        longitude = +(parseFloat("" + longitude).toFixed(4));

        for (pkg in newlist[latlng].items) {
            var added = false;
            for (var i = 0; i < packages.length; i++) {
                if (+(parseFloat("" + packages[i].coords[0]).toFixed(4)) == latitude && +(parseFloat("" + packages[i].coords[1]).toFixed(4)) == longitude) {
                    var newpackage = newlist[latlng].items[pkg];
                    for (var j in packages[i].items) {
                        if (packages[i].items[j].id == newpackage.id) {
                            // This package already exists in the local database.
                            added = true;
                            skipped++;
                        }
                    }
                    if (!added) {
                        packages[i].items.push(package);
                        count++;
                        added = true;
                    }
                    break;
                }
            }
            if (!added) {
                packages.push(newlist[latlng]);
            }
        }
    }
    setStorage("packages", JSON.stringify(packages));

    appendActivityLog("Imported List", count + " items added", "", "fas fa-file-download");
    if (map != null) {
        reloadMap();
    }
    return skipped;
}

function mapCalibrate(item, packagesentry) {
    // Determine if the delivery location isn't near the map pin
    if (userPosition.coords.accuracy < 20 && timeDiff(userPosition.updated) < 10) {
        // User location is accurate, check distance
        var distance = getDistance(packagesentry.coords[0], packagesentry.coords[1], userPosition.coords.latitude, userPosition.coords.longitude);
        var lat = userPosition.coords.latitude;
        var lon = userPosition.coords.longitude;
        if (distance > 100) { // Over 100 meters distance
            if (typeof item.extended == "object") {
                // we have all the info we need
                var fixmap = function (item, latitude, longitude, locationtype, pkgsentry) {
                    $.ajax({
                        type: "POST",
                        url: SETTINGS.mapfixapi,
                        data: {
                            number: item.extended.number,
                            unit: item.extended.unit,
                            street: item.extended.street,
                            citystate: item.extended.citystate,
                            zip: item.extended.zip,
                            latitude: latitude,
                            longitude: longitude,
                            locationtype: locationtype
                        },
                        success: function () {
                            appendActivityLog("Map Calibrated",
                                    item.address,
                                    "Thanks for improving the map accuracy!<br>"
                                    + "Old: <a class='geolink' href='geo:" + pkgsentry.coords[0] + "," + pkgsentry.coords[1] + "'>" + pkgsentry.coords[0] + ", " + pkgsentry.coords[1] + "</a><br>"
                                    + "New: <a class='geolink' href='geo:" + latitude + "," + longitude + "'>" + latitude + ", " + longitude + "</a>",
                                    "fas fa-map-marked-alt"
                                    );
                        },
                        error: function () {
                            // try again in five minutes
                            setTimeout(function () {
                                fixmap(item, latitude, longitude, locationtype, pkgsentry);
                            }, 1000 * 60 * 5);
                        },
                        dataType: "json"
                    });
                };

                app.dialog.create({
                    title: 'Map Calibration',
                    text: "Your actual location doesn't match the map location for the " + SETTINGS.itemtypes[item.type].name + " at " + item.address + ". Where are you?",
                    buttons: [
                        {
                            text: 'Address',
                            close: true
                        },
                        {
                            text: 'Mailbox/CBU',
                            close: true
                        },
                        {
                            text: 'Parcel Locker',
                            close: true
                        },
                        {
                            text: "Other/Cancel",
                            close: true
                        }
                    ],
                    verticalButtons: true,
                    onClick: function (dialog, index) {
                        switch (index) {
                            case 0:
                                fixmap(item, lat, lon, "address", packagesentry);
                                break;
                            case 1:
                                fixmap(item, lat, lon, "mailbox", packagesentry);
                                break;
                            case 2:
                                fixmap(item, lat, lon, "locker", packagesentry);
                                break;
                            default:
                                return;
                        }
                    }
                }).open();
            }
        }
    }
}

function markDelivered(id, delivered) {
    for (var i = 0; i < packages.length; i++) {
        for (var j = 0; j < packages[i].items.length; j++) {
            if (packages[i].items[j].id == id) {

                if (typeof delivered == 'undefined') {
                    if (packages[i].items[j].delivered == false) {
                        delivered = true;
                    } else {
                        delivered = false;
                    }
                }

                packages[i].items[j].delivered = delivered;
                let gpslink = "";
                if (userPosition.coords.accuracy < 40 && timeDiff(userPosition.updated) < 15) {
                    var lat = parseFloat(userPosition.coords.latitude).toFixed(5);
                    var lon = parseFloat(userPosition.coords.longitude).toFixed(5);
                    gpslink = "<br /><a class='geolink' href='geo:" + lat + "," + lon + "'>" + lat + ", " + lon + "</a> (&#177;" + getDisplayDistance(userPosition.coords.accuracy, false) + ")";
                }
                if (delivered) {
                    packages[i].items[j].deliverytimestamp = time();
                    appendActivityLog("Delivered", SETTINGS.itemtypes[packages[i].items[j].type].name, packages[i].items[j].address + gpslink, "far fa-check-circle");
                    mapCalibrate(packages[i].items[j], packages[i]);
                } else {
                    packages[i].items[j].deliverytimestamp = null;
                    appendActivityLog("Undelivered", SETTINGS.itemtypes[packages[i].items[j].type].name, packages[i].items[j].address, "fas fa-undo");
                }
                setStorage("packages", JSON.stringify(packages));
                return; // so we don't keep looping over the rest of the packages
            }
        }
    }
}

function confirmDeletePackage(package, callback) {
    app.dialog.confirm(
            "Delete " + SETTINGS.itemtypes[package.type].name.toLowerCase() + " at " + package.address + "?",
            "Confirm",
            function () {
                // delete
                deletePackage(package.id, callback);
            },
            function () {
                // cancel
            }
    );
}

function deletePackage(id, callback) {
    for (var i = 0; i < packages.length; i++) {
        for (var j = 0; j < packages[i].items.length; j++) {
            if (packages[i].items[j].id == id) {

                appendActivityLog("Deleted", SETTINGS.itemtypes[packages[i].items[j].type].name, packages[i].items[j].address, "fas fa-trash");

                packages[i].items.splice(j, 1);

                if (packages[i].items.length == 0) {
                    packages.splice(i, 1);
                }

                setStorage("packages", JSON.stringify(packages));
                loadPackageList();

                if (typeof callback == 'function') {
                    callback(id);
                }
                return;
            }
        }
    }
}

function countRemainingPackages() {
    var undelivered = 0;
    for (var i = 0; i < packages.length; i++) {
        for (var j = 0; j < packages[i].items.length; j++) {
            if (packages[i].items[j].delivered != true) {
                undelivered++;
            }
        }
    }
    return undelivered;
}

function countPackages() {
    var count = 0;
    for (var i = 0; i < packages.length; i++) {
        for (var j = 0; j < packages[i].items.length; j++) {
            count++;
        }
    }
    return count;
}

function addPackageByAddress(number, unit, street, citystate, zip, type, callback) {
    var requestfinished = false;
    var searchingdialogopen = false;
    var deadline = false;

    var ajaxlookup = function () {
        var geocodecache = getStorage("geocode_cache");
        if (geocodecache == null) {
            geocodecache = "{}";
            setStorage("geocode_cache", "{}");
        }

        geocodecache = JSON.parse(geocodecache);
        var cachekey = number + " || " + street + " || " + citystate + " || " + zip + " || " + SETTINGS.itemtypes[type].allowedlocationtypes;
        if (unit != '') {
            cachekey = number + " || " + unit + " || " + street + " || " + citystate + " || " + zip + " || " + SETTINGS.itemtypes[type].allowedlocationtypes;
        }
        var cacheitem = geocodecache[cachekey];
        var timestamp = Math.floor(Date.now() / 1000);
        if (typeof cacheitem != 'undefined') {
            if (cacheitem.added + SETTINGS.geocodecacheexpiry < timestamp) {
                console.log("Info", "Removing expired geocode cache item " + cachekey);
                delete geocodecache[cachekey];
                setStorage("geocode_cache", JSON.stringify(geocodecache));
            } else {
                console.log("Info", "Using cached geocode result", cacheitem);
                addPackage({
                    address: cacheitem.address,
                    number: number,
                    unit: unit,
                    street: street,
                    citystate: citystate,
                    zip: zip
                }, cacheitem.latitude, cacheitem.longitude, type, callback, deadline);
                return;
            }
        }

        $.ajax({
            url: SETTINGS.geocodeapi,
            dataType: 'json',
            data: {
                number: number,
                unit: unit,
                street: street,
                citystate: citystate,
                zip: zip,
                type: SETTINGS.itemtypes[type].allowedlocationtypes
            },
            timeout: 15 * 1000,
            success: function (resp) {
                if (searchingdialogopen) {
                    app.dialog.close();
                    searchingdialogopen = false;
                }
                requestfinished = true;
                if (resp.status == "OK") {
                    if (resp.accuracy.ok) {
                        addPackage({
                            address: resp.address.street,
                            number: number,
                            unit: unit,
                            street: street,
                            citystate: citystate,
                            zip: zip
                        }, resp.coords[0], resp.coords[1], type, callback, deadline);
                        geocodecache[cachekey] = {
                            number: resp.address.number,
                            unit: unit,
                            address: resp.address.street,
                            latitude: resp.coords[0],
                            longitude: resp.coords[1],
                            added: Math.floor(Date.now() / 1000)
                        };
                        setStorage("geocode_cache", JSON.stringify(geocodecache));
                    } else {
                        playSound("error");
                        app.dialog.confirm(
                                "The address \"" + address + "\" couldn't be reliably located. Add it anyways?",
                                "Accuracy Warning",
                                function (ok) {
                                    if (resp.address.street == "") {
                                        addPackage({
                                            address: address,
                                            number: number,
                                            unit: unit,
                                            street: street,
                                            citystate: citystate,
                                            zip: zip
                                        }, resp.coords[0], resp.coords[1], type, callback, deadline);
                                    } else {
                                        addPackage({
                                            address: resp.address.street,
                                            number: number,
                                            unit: unit,
                                            street: street,
                                            citystate: citystate,
                                            zip: zip
                                        }, resp.coords[0], resp.coords[1], type, callback, deadline);
                                    }
                                }
                        );
                    }
                } else {
                    playSound("error");
                    app.dialog.alert(resp.message, "Error");
                }
            },
            error: function (jqXHR, status, errorThrown) {
                if (searchingdialogopen) {
                    app.dialog.close();
                    searchingdialogopen = false;
                }
                requestfinished = true;
                playSound("error");
                app.dialog.alert("There was a network issue while finding the address.  Please try adding the item again.", "Error");
            }
        });

        // Open a loading message if there's a delay finding the address
        setTimeout(function () {
            if (!requestfinished) {
                app.dialog.preloader("Searching for address...");
                searchingdialogopen = true;
            }
        }, 750);
    }

    var prelookup = function () {
        if (type == "express") {
            if (getStorage("deadlinealarm_minutes") == null) {
                setStorage("deadlinealarm_minutes", 20);
            }
            var minutes = getStorage("deadlinealarm_minutes");
            app.dialog.create({
                title: 'Express Item',
                text: 'Set a reminder for ' + minutes + ' minutes before:',
                buttons: [
                    {
                        text: '10:30 AM',
                        close: true
                    },
                    {
                        text: '12:00 PM',
                        close: true
                    },
                    {
                        text: '3:00 PM',
                        close: true
                    },
                    {
                        text: "No reminder",
                        color: "red",
                        close: true
                    }
                ],
                verticalButtons: true,
                onClick: function (dialog, index) {
                    deadline = new Date();
                    switch (index) {
                        case 0:
                            deadline.setMinutes(30);
                            deadline.setHours(10);
                            break;
                        case 1:
                            deadline.setMinutes(00);
                            deadline.setHours(12);
                            break;
                        case 2:
                            deadline.setMinutes(00);
                            deadline.setHours(12 + 3);
                            break;
                        case 3:
                        default:
                            deadline = false;
                            break;
                    }

                    if (deadline != false) {
                        deadline = deadline.getTime() / 1000;
                    }
                    ajaxlookup();
                }
            }).open();
        } else {
            ajaxlookup();
        }
    }

    var deliverable = isDeliverable(number, street);

    if (deliverable.ok) {
        prelookup();
    } else {
        app.dialog.confirm(
                "A route note says this address " + deliverable.reason + ". Add item anyways?",
                "Confirm",
                function () {
                    prelookup();
                },
                function () {
                    // cancel
                }
        );
    }
}

function addPackageByBarcode(barcode, type, callback) {
    var requestfinished = false;
    var searchingdialogopen = false;
    var deadline = false;

    var ajaxlookup = function () {
        $.ajax({
            url: SETTINGS.geocodebarcodeapi,
            dataType: 'json',
            data: {
                code: barcode,
                type: SETTINGS.itemtypes[type].allowedlocationtypes
            },
            timeout: 15 * 1000,
            success: function (resp) {
                if (searchingdialogopen) {
                    app.dialog.close();
                    searchingdialogopen = false;
                }
                requestfinished = true;
                if (resp.status == "OK") {
                    if (resp.accuracy.ok) {
                        addPackage(resp.address.street, resp.coords[0], resp.coords[1], type, callback, deadline);
                    } else {
                        playSound("error");
                        app.dialog.alert("The scanned address couldn't be reliably located.", "Error");
                    }
                } else {
                    playSound("error");
                    app.dialog.alert(resp.message, "Error");
                }
            },
            error: function (jqXHR, status, errorThrown) {
                if (searchingdialogopen) {
                    app.dialog.close();
                    searchingdialogopen = false;
                }
                requestfinished = true;
                playSound("error");
                app.dialog.alert("There was a network issue while looking up the barcode.  Please try again.", "Error");
            }
        });

        // Open a loading message if there's a delay finding the address
        setTimeout(function () {
            if (!requestfinished) {
                app.dialog.preloader("Looking up barcode...");
                searchingdialogopen = true;
            }
        }, 750);
    }

    var prelookup = function () {
        if (type == "express") {
            if (getStorage("deadlinealarm_minutes") == null) {
                setStorage("deadlinealarm_minutes", 20);
            }
            var minutes = getStorage("deadlinealarm_minutes");
            app.dialog.create({
                title: 'Express Item',
                text: 'Set a reminder for ' + minutes + ' minutes before:',
                buttons: [
                    {
                        text: '10:30 AM',
                        close: true
                    },
                    {
                        text: '12:00 PM',
                        close: true
                    },
                    {
                        text: '3:00 PM',
                        close: true
                    },
                    {
                        text: "No reminder",
                        color: "red",
                        close: true
                    }
                ],
                verticalButtons: true,
                onClick: function (dialog, index) {
                    deadline = new Date();
                    switch (index) {
                        case 0:
                            deadline.setMinutes(30);
                            deadline.setHours(10);
                            break;
                        case 1:
                            deadline.setMinutes(00);
                            deadline.setHours(12);
                            break;
                        case 2:
                            deadline.setMinutes(00);
                            deadline.setHours(12 + 3);
                            break;
                        case 3:
                        default:
                            deadline = false;
                            break;
                    }

                    if (deadline != false) {
                        deadline = deadline.getTime() / 1000;
                    }
                    ajaxlookup();
                }
            }).open();
        } else {
            ajaxlookup();
        }
    }

    prelookup();
}

function checkDeadlines() {
    if (getStorage("deadlinealarm_minutes") == null) {
        setStorage("deadlinealarm_minutes", 20);
    }
    var minutes = getStorage("deadlinealarm_minutes");
    var currentTime = new Date().getTime() / 1000;
    var deadlineTime = currentTime + (minutes * 60);
    for (i in packages) {
        for (j in packages[i].items) {
            var item = packages[i].items[j];
            if (typeof item.deadline != 'undefined' && item.deadline != false && item.delivered != true) {
                if ((typeof item.deadlinealarmed == 'undefined' || item.deadlinealarmed != true) && item.deadline <= deadlineTime) {
                    playSound("alert");
                    app.dialog.alert(
                            "Item at " + item.address + " needs to be delivered by " + timestampToTimeString(item.deadline) + " (" + Math.floor((item.deadline - currentTime) / 60) + " minutes from now).",
                            "Delivery Alarm",
                            function () {

                            }
                    );
                    packages[i].items[j].deadlinealarmed = true;
                    setStorage("packages", JSON.stringify(packages));
                }
            }
        }
    }
}

setInterval(checkDeadlines, 15 * 1000);