(function() {

    $("#resultModal").on("hidden.bs.modal",
        function() {
            //close
            window.location.href = "/";
        });

    function stringToAsciiByteArray(str) {
        const bytes = [];
        for (let i = 0; i < str.length; ++i) {
            bytes.push(str.charCodeAt(i));
        }
        return bytes;
    }

    //https://stackoverflow.com/questions/14234560/javascript-how-to-get-parent-element-by-selector
    function parent_by_selector(node, selector, stop_selector = "body") {
        var parent = node.parentNode;
        while (true) {
            if (parent.matches(stop_selector)) break;
            if (parent.matches(selector)) break;
            parent = parent.parentNode; // get upper parent and check again
        }
        if (parent.matches(stop_selector)) parent = null; // when parent is a tag 'body' -> parent not found
        return parent;
    }

    const app = angular
        .module("myApp",
            [
                "cp.ngConfirm",
                "ui.sortable",
                "ui.toggle",
                "ui.bootstrap-slider",
                "nucleusConstants",
                "corradeVersion",
                "translation"
            ])
        .filter("arrayOrder",
            function() {
                return function(object) {
                    var array = [];
                    angular.forEach(object,
                        function(item) {
                            array.push(item);
                        });
                    array.sort(function(a, b) {
                        return a.toLowerCase().localeCompare(b.toLowerCase());
                    });
                    return array;
                };
            })
        .run(function($rootScope) {
            $rootScope.typeOf = function(value) {
                return typeof value;
            };
        })
        .directive("stringToNumber",
            function() {
                return {
                    require: "ngModel",
                    link: function(scope, element, attrs, ngModel) {
                        ngModel.$parsers.push(function(value) {
                            return `${value}`;
                        });
                        ngModel.$formatters.push(function(value) {
                            return parseFloat(value);
                        });
                    }
                };
            });

    app.directive("myEnter",
        function() {
            return function(scope, element, attrs) {
                element.bind("keydown keypress",
                    function(event) {
                        if (event.which === 13) {
                            scope.$apply(function() {
                                scope.$eval(attrs.myEnter);
                            });

                            event.preventDefault();
                        }
                    });
            };
        });

    app.controller("myCtrl",
        [
            "$scope",
            "$http",
            "$window",
            "$ngConfirm",
            "$translate",
            "$compile",
            "$templateCache",
            "$sce",
            "cfg.CONSTANTS",
            function(
                $scope,
                $http,
                $window,
                $ngConfirm,
                $translate,
                $compile,
                $templateCache,
                $sce,
                CONSTANTS
            ) {
                $scope.x2js = new X2JS({
                    arrayAccessFormPaths: [
                        "CorradeConfiguration.Groups.Group",
                        "CorradeConfiguration.Groups.Group.Permissions.Permission",
                        "CorradeConfiguration.Groups.Group.Notifications.Notification",
                        "CorradeConfiguration.StartLocations.StartLocation"
                    ],
                    stripWhitespaces: false
                });

                $scope.cfg = {};
                $scope.cfgInitial = {};

                $scope.availablePermissions = [];
                $scope.availableNotifications = [];

                $scope.selectedStartLocation = {};
                $scope.selectedStartLocation.Position = {};

                // Used to reveal various password fields.
                $scope.togglePassword = function(element_id, value) {
                    switch (value) {
                    case true:
                        $(`#${element_id}`).attr("type", "text");
                        break;
                    default:
                        $(`#${element_id}`).attr("type", "password");
                        break;
                    }
                };

                // Fetch available RLV behaviours.
                function getBehaviours() {
                    return new Promise(function(resolve, reject) {
                        $http({
                            method: "GET",
                            url: urljoin(
                                CONSTANTS.BaseURL,
                                "/corrade/",
                                "/behaviours"
                            ),
                            transformResponse: function(data) {
                                return $scope.x2js.xml2js(data);
                            }
                        }).then(
                            function successCallback(response, status) {
                                $scope.availableBehaviours =
                                    response.data.Behaviours.Behaviours;
                                resolve();
                            },
                            function errorCallback(data, status) {
                                reject();
                            }
                        );
                    });
                }

                // Fetch available permissions.
                function getPermissions() {
                    return new Promise(function(resolve, reject) {
                        $http({
                            method: "GET",
                            url: urljoin(
                                CONSTANTS.BaseURL,
                                "/cfg/",
                                "/groupPermissions"
                            ),
                            transformResponse: function(data) {
                                return $scope.x2js.xml2js(data);
                            }
                        }).then(
                            function successCallback(response, status) {
                                $scope.availablePermissions =
                                    response.data.GroupPermissions.Permissions;
                                resolve();
                            },
                            function errorCallback(data, status) {
                                reject();
                            }
                        );
                    });
                }

                // Fetch available notifications.
                function getNotifications() {
                    return new Promise(function(resolve, reject) {
                        $http({
                            method: "GET",
                            url: urljoin(
                                CONSTANTS.BaseURL,
                                "/cfg/",
                                "/groupNotifications"
                            ),
                            transformResponse: function(data) {
                                return $scope.x2js.xml2js(data);
                            }
                        }).then(
                            function successCallback(response, status) {
                                $scope.availableNotifications =
                                    response.data.GroupNotifications.Notifications;
                                resolve();
                            },
                            function errorCallback(data, status) {
                                reject();
                            }
                        );
                    });
                }

                // Fetch the actual configuration.
                function getDefaultConfiguration() {
                    return new Promise(function(resolve, reject) {
                        $http({
                            method: "GET",
                            url: urljoin(
                                CONSTANTS.BaseURL,
                                "/cfg/",
                                "CorradeConfiguration.xml.default"
                            ),
                            transformResponse: function(data) {
                                return $scope.x2js.xml2js(data);
                            }
                        }).then(
                            function successCallback(response, status) {
                                angular.copy(response.data, $scope.cfg);
                                angular.copy(response.data, $scope.cfgInitial);
                                resolve();
                            },
                            function errorCallback(data, status) {
                                // All hope is lost.
                                reject();
                            }
                        );
                    });
                }

                function getConfiguration() {
                    return new Promise(function(resolve, reject) {
                        $http({
                            method: "GET",
                            url: urljoin(
                                CONSTANTS.BaseURL,
                                "/cfg/",
                                "CorradeConfiguration.xml"
                            ),
                            transformResponse: function(data) {
                                // Decrypt the configuration file.
                                data = stringToAsciiByteArray(
                                    forge.util.decode64(data)
                                );

                                const salt = String.fromCodePoint(
                                    ...data.slice(0, 16)
                                );
                                const ciphertext = String.fromCodePoint(
                                    ...data.slice(16)
                                );

                                const sha1 = forge.md.sha1.create();
                                sha1.update(Cookies.get("Session"));

                                var derivedKey = forge.pkcs5.pbkdf2(
                                    sha1.digest().toHex(),
                                    salt,
                                    1000,
                                    48
                                );

                                derivedKey = stringToAsciiByteArray(derivedKey);

                                var key = derivedKey.slice(0, 32);

                                key = String.fromCodePoint(...key);

                                var iv = derivedKey.slice(32);

                                iv = String.fromCodePoint(...iv);

                                const input = forge.util.createBuffer(ciphertext);

                                const decipher = forge.cipher.createDecipher(
                                    "AES-CBC",
                                    key
                                );
                                decipher.start({ iv: iv });
                                decipher.update(input);
                                if (!decipher.finish()) {
                                    $("#resultModalSubject").text("Failure");
                                    $("#resultModalBody").text(
                                        "Unable to decrypt configuration file."
                                    );
                                    $("#resultModal").modal("show");
                                    return;
                                }

                                const result = decipher.output.toString();

                                return $scope.x2js.xml2js(result);
                            }
                        }).then(
                            function successCallback(response, status) {
                                // If fetching the configuration failed, then try the default configuration.
                                if (response.status === 404) {
                                    reject();
                                    return;
                                }

                                angular.copy(response.data, $scope.cfg);
                                angular.copy(response.data, $scope.cfgInitial);

                                resolve();
                            },
                            function errorCallback(data, status) {
                                // If fetching the configuration failed, then try the default configuration.
                                reject();
                            }
                        );
                    });
                }

                function getConfigurationAuthorized() {
                    getConfiguration().then(
                        function() {
                            // The configuration has loaded.

                            // XML woes.
                            switch (
                                $scope.cfg.CorradeConfiguration.AutoConnect.toString().toUpperCase()
                            ) {
                            case "TRUE":
                            case "1":
                                $scope.cfg.CorradeConfiguration.AutoConnect = true;
                                break;
                            case "FALSE":
                            case "0":
                                $scope.cfg.CorradeConfiguration.AutoConnect = false;
                                break;
                            }

                            switch (
                                $scope.cfg.CorradeConfiguration.RLV.Enable.toString().toUpperCase()
                            ) {
                            case "TRUE":
                            case "1":
                                $scope.cfg.CorradeConfiguration.RLV.Enable = true;
                                break;
                            case "FALSE":
                            case "0":
                                $scope.cfg.CorradeConfiguration.RLV.Enable = false;
                                break;
                            }

                            switch (
                                $scope.cfg.CorradeConfiguration.Servers.HTTPServer.Enable.toString().toUpperCase()
                            ) {
                            case "TRUE":
                            case "1":
                                $scope.cfg.CorradeConfiguration.Servers.HTTPServer.Enable = true;
                                break;
                            case "FALSE":
                            case "0":
                                $scope.cfg.CorradeConfiguration.Servers.HTTPServer.Enable = false;
                                break;
                            }

                            switch (
                                $scope.cfg.CorradeConfiguration.Servers.MQTTServer.Enable.toString().toUpperCase()
                            ) {
                            case "TRUE":
                            case "1":
                                $scope.cfg.CorradeConfiguration.Servers.MQTTServer.Enable = true;
                                break;
                            case "FALSE":
                            case "0":
                                $scope.cfg.CorradeConfiguration.Servers.MQTTServer.Enable = false;
                                break;
                            }

                            switch (
                                $scope.cfg.CorradeConfiguration.Servers.MQTTServer.Compression.toString().toUpperCase()
                            ) {
                            case "TRUE":
                            case "1":
                                $scope.cfg.CorradeConfiguration.Servers.MQTTServer.Compression = true;
                                break;
                            case "FALSE":
                            case "0":
                                $scope.cfg.CorradeConfiguration.Servers.MQTTServer.Compression = false;
                                break;
                            }

                            switch (
                                $scope.cfg.CorradeConfiguration.Servers.TCPServer.Enable.toString().toUpperCase()
                            ) {
                            case "TRUE":
                            case "1":
                                $scope.cfg.CorradeConfiguration.Servers.TCPServer.Enable = true;
                                break;
                            case "FALSE":
                            case "0":
                                $scope.cfg.CorradeConfiguration.Servers.TCPServer.Enable = false;
                                break;
                            }

                            switch (
                                $scope.cfg.CorradeConfiguration.Servers.TCPServer.Compression.toString().toUpperCase()
                            ) {
                            case "TRUE":
                            case "1":
                                $scope.cfg.CorradeConfiguration.Servers.TCPServer.Compression = true;
                                break;
                            case "FALSE":
                            case "0":
                                $scope.cfg.CorradeConfiguration.Servers.TCPServer.Compression = false;
                                break;
                            }

                            switch (
                                $scope.cfg.CorradeConfiguration.Servers.UDPServer.Enable.toString().toUpperCase()
                            ) {
                            case "TRUE":
                            case "1":
                                    $scope.cfg.CorradeConfiguration.Servers.UDPServer.Enable = true;
                                break;
                            case "FALSE":
                            case "0":
                                    $scope.cfg.CorradeConfiguration.Servers.UDPServer.Enable = false;
                                break;
                            }

                            switch (
                            $scope.cfg.CorradeConfiguration.Servers.UDPServer.Compression.toString().toUpperCase()
                            ) {
                            case "TRUE":
                            case "1":
                                    $scope.cfg.CorradeConfiguration.Servers.UDPServer.Compression = true;
                                break;
                            case "FALSE":
                            case "0":
                                    $scope.cfg.CorradeConfiguration.Servers.UDPServer.Compression = false;
                                break;
                            }

                            switch (
                                $scope.cfg.CorradeConfiguration.Servers.WebSocketsServer.Enable.toString().toUpperCase()
                            ) {
                            case "TRUE":
                            case "1":
                                $scope.cfg.CorradeConfiguration.Servers.WebSocketsServer.Enable = true;
                                break;
                            case "FALSE":
                            case "0":
                                $scope.cfg.CorradeConfiguration.Servers.WebSocketsServer.Enable = false;
                                break;
                            }

                            switch (
                                $scope.cfg.CorradeConfiguration.Servers.WebSocketsServer.Compression.toString()
                                    .toUpperCase()
                            ) {
                            case "TRUE":
                            case "1":
                                $scope.cfg.CorradeConfiguration.Servers.WebSocketsServer.Compression = true;
                                break;
                            case "FALSE":
                            case "0":
                                $scope.cfg.CorradeConfiguration.Servers.WebSocketsServer.Compression = false;
                                break;
                            }

                            switch (
                                $scope.cfg.CorradeConfiguration.MessageLogging.toString().toUpperCase()
                            ) {
                            case "TRUE":
                            case "1":
                                $scope.cfg.CorradeConfiguration.MessageLogging = true;
                                break;
                            case "FALSE":
                            case "0":
                                $scope.cfg.CorradeConfiguration.MessageLogging = false;
                                break;
                            }

                            $.get("/fragments/configurationForm.html",
                                function(
                                    data
                                ) {
                                    $("#configurationFormFragment").html(data);
                                    $sce.trustAsHtml(data);
                                    $compile($("#configurationFormFragment"))($scope);
                                    $scope.$digest();
                                });
                        },
                        function() {
                            // Loading the configuration has failed, so attempt to load the default configuration.
                            getDefaultConfiguration().then(
                                function() {
                                    // Set all communication servers to off.
                                    $scope.cfg.CorradeConfiguration.Servers.HTTPServer.Enable = false;
                                    $scope.cfg.CorradeConfiguration.Servers.MQTTServer.Enable = false;
                                    $scope.cfg.CorradeConfiguration.Servers.TCPServer.Enable = false;
                                    $scope.cfg.CorradeConfiguration.Servers.UDPServer.Enable = false;
                                    $scope.cfg.CorradeConfiguration.Servers.WebSocketsServer.Enable = false;

                                    $scope.cfg.CorradeConfiguration.Servers.MQTTServer.Compression = false;
                                    $scope.cfg.CorradeConfiguration.Servers.TCPServer.Compression = false;
                                    $scope.cfg.CorradeConfiguration.Servers.UDPServer.Compression = false;
                                    $scope.cfg.CorradeConfiguration.Servers.WebSocketsServer.Compression = false;

                                    $.get(
                                        "/fragments/configurationForm.html",
                                        function(data) {
                                            $("#configurationFormFragment").html(
                                                data
                                            );
                                            $sce.trustAsHtml(data);
                                            $compile(
                                                $("#configurationFormFragment")
                                            )($scope);
                                            $scope.$digest();
                                        }
                                    );

                                    // The default configuration has been loaded.
                                    $("#welcomeModalSubject").text(
                                        "Welcome to Corrade!"
                                    );
                                    $("#welcomeModalBody").text(
                                        "Welcome to Corrade! Please use the configurator to configure Corrade for the first time."
                                    );
                                    $("#welcomeModal").modal("show");
                                },
                                function() {
                                    // Loading the default configuration has failed - something is wrong with the Corrade distribution.
                                    $("#welcomeModalSubject").text(
                                        "Failed loading any configuration!"
                                    );
                                    $("#welcomeModalBody").text(
                                        "Neither the configuration nor the default configuration could be loaded, please check your Corrade install and reload Nucleus."
                                    );
                                    $("#welcomeModal").modal("show");
                                }
                            );
                        }
                    );
                }

                // Retrieve behaviours.
                getBehaviours().then(
                    function() {
                        // Permissions retrieved successfully.
                    },
                    function() {
                        // Failed retrieving permissions.
                        $("#resultModalSubject").text(
                            "Unable to retrieve behaviours!"
                        );
                        $("#resultModalBody").text(
                            "Corrade behaviours could not be retrieved, please check your Corrade install and reload Nucleus."
                        );
                        $("#resultModal").modal("show");
                    }
                );

                // Retrieve permissions.
                getPermissions().then(
                    function() {
                        // Permissions retrieved successfully.
                    },
                    function() {
                        // Failed retrieving permissions.
                        $("#resultModalSubject").text(
                            "Unable to retrieve permissions!"
                        );
                        $("#resultModalBody").text(
                            "Group permissions could not be retrieved, please check your Corrade install and reload Nucleus."
                        );
                        $("#resultModal").modal("show");
                    }
                );

                // Retrieve notifications.
                getNotifications().then(
                    function() {
                        // Notifications retrieved successfully.
                    },
                    function() {
                        // Failed retrieving notifications.
                        $("#resultModalSubject").text(
                            "Unable to retrieve notifications!"
                        );
                        $("#resultModalBody").text(
                            "Group notifications could not be retrieved, please check your Corrade install and reload Nucleus."
                        );
                        $("#resultModal").modal("show");
                    }
                );

                // Start Locations
                $scope.onAddStartLocation = function() {
                    $scope.cfg.CorradeConfiguration.StartLocations.StartLocation.push({
                        Region: "Region Name",
                        Position: { Y: "", X: "", Z: "" }
                    });
                };

                $scope.onMakePosition = function($index) {
                    if (
                        $scope.cfg.CorradeConfiguration.StartLocations.StartLocation[
                                $index
                            ].Position !==
                            undefined
                    )
                        return;

                    $scope.cfg.CorradeConfiguration.StartLocations.StartLocation[
                        $index
                    ].Position = {};
                    $scope.cfg.CorradeConfiguration.StartLocations.StartLocation[
                        $index
                    ].Position.X = "";
                    $scope.cfg.CorradeConfiguration.StartLocations.StartLocation[
                        $index
                    ].Position.Y = "";
                    $scope.cfg.CorradeConfiguration.StartLocations.StartLocation[
                        $index
                    ].Position.Z = "";
                };

                $scope.onMakeRegion = function($index) {
                    if (
                        $scope.cfg.CorradeConfiguration.StartLocations.StartLocation[
                                $index
                            ].Region !==
                            undefined
                    )
                        return;

                    $scope.cfg.CorradeConfiguration.StartLocations.StartLocation[
                        $index
                    ].Region = "";
                };

                $scope.lastSelectedStartLocation = -1;
                $scope.onClickLocation = function($index, $event) {
                    // Remove selected on the other item.
                    if ($scope.lastSelectedStartLocation !== -1) {
                        $(
                            `#startLocation_${$scope.lastSelectedStartLocation}`
                        ).removeClass("selected");
                    }

                    // Add selected to the closest predecessor that is a table row of the start locations table.
                    const parentTableRow = parent_by_selector(
                        $($event.target)[0],
                        "#tableStartLocations tr"
                    );
                    $(parentTableRow).addClass("selected");

                    // Set the last selected item.
                    $scope.lastSelectedStartLocation = $index;
                };

                $scope.onStartDragLocation = function() {
                    $(
                        `#startLocation_${$scope.lastSelectedStartLocation}`
                    ).removeClass("selected");
                    $scope.lastSelectedStartLocation = -1;
                };

                $scope.onStopDragLocation = function() {};

                $scope.onDeleteStartLocation = function() {
                    if ($scope.lastSelectedStartLocation === -1) {
                        return;
                    }

                    $scope.cfg.CorradeConfiguration.StartLocations.StartLocation.splice(
                        $scope.lastSelectedStartLocation,
                        1
                    );
                    $(
                        `#startLocation_${$scope.lastSelectedStartLocation}`
                    ).removeClass("selected");
                    $scope.lastSelectedStartLocation = -1;
                };

                // Groups
                $scope.selectedGroup = {};
                $scope.onGroupSelected = function() {
                    let group = $.grep(
                        $scope.cfg.CorradeConfiguration.Groups.Group,
                        function(group) {
                            return group.Name === $scope.selectedGroup.Name;
                        }
                    ).shift();
                    switch (
                        group.CacheMembers.toString().toUpperCase()
                    ) {
                    case "TRUE":
                    case "1":
                        $scope.selectedGroup.CacheMembers  = true;
                        break;
                    case "FALSE":
                    case "0":
                        $scope.selectedGroup.CacheMembers  = false;
                        break;
                    }
                };

                $scope.onToggleBehaviour = function($behaviour, $event) {
                    if ($($event.target).is(":checked")) {
                        if (
                            $scope.cfg.CorradeConfiguration.RLV.Blacklist.Behaviour.indexOf(
                                    $behaviour
                                ) >
                                -1
                        )
                            return;

                        $scope.cfg.CorradeConfiguration.RLV.Blacklist.Behaviour.push(
                            $behaviour
                        );

                        return;
                    }

                    if (
                        $scope.cfg.CorradeConfiguration.RLV.Blacklist.Behaviour.indexOf(
                                $behaviour
                            ) ===
                            -1
                    )
                        return;

                    $scope.cfg.CorradeConfiguration.RLV.Blacklist.Behaviour.splice(
                        $.inArray(
                            $behaviour,
                            $scope.cfg.CorradeConfiguration.RLV.Blacklist
                        ),
                        1
                    );
                };

                $scope.onTogglePermission = function($permission, $event) {
                    if ($($event.target).is(":checked")) {
                        if (
                            $scope.selectedGroup.Permissions.Permission.indexOf(
                                    $permission
                                ) >
                                -1
                        )
                            return;

                        $scope.selectedGroup.Permissions.Permission.push(
                            $permission
                        );

                        return;
                    }

                    if (
                        $scope.selectedGroup.Permissions.Permission.indexOf(
                                $permission
                            ) ===
                            -1
                    )
                        return;

                    $scope.selectedGroup.Permissions.Permission.splice(
                        $.inArray(
                            $permission,
                            $scope.selectedGroup.Permissions.Permission
                        ),
                        1
                    );
                };

                $scope.onToggleNotification = function($notification, $event) {
                    if ($($event.target).is(":checked")) {
                        if (
                            $scope.selectedGroup.Notifications.Notification.indexOf(
                                    $notification
                                ) >
                                -1
                        )
                            return;

                        $scope.selectedGroup.Notifications.Notification.push(
                            $notification
                        );

                        return;
                    }

                    if (
                        $scope.selectedGroup.Notifications.Notification.indexOf(
                                $notification
                            ) ===
                            -1
                    )
                        return;

                    $scope.selectedGroup.Notifications.Notification.splice(
                        $.inArray(
                            $notification,
                            $scope.selectedGroup.Notifications.Notification
                        ),
                        1
                    );
                };

                $scope.onAddGroup = function() {
                    const group = {
                        Name: "New Group",
                        Password: "",
                        // Some default notifications and permissions.
                        Notifications: {
                            Notification: [
                                "group",
                                "message",
                                "notice",
                                "local",
                                "dialog",
                                "permission",
                                "invite",
                                "control",
                                "sit",
                                "teleport",
                                "inventory"
                            ]
                        },
                        Permissions: {
                            Permission: [
                                "inventory",
                                "movement",
                                "grooming",
                                "interact",
                                "notifications",
                                "talk",
                                "group",
                                "land"
                            ]
                        }
                    };

                    $scope.cfg.CorradeConfiguration.Groups.Group.push(group);

                    $scope.selectedGroup = group;
                };

                $scope.onDeleteGroup = function() {
                    $scope.cfg.CorradeConfiguration.Groups.Group = $.grep(
                        $scope.cfg.CorradeConfiguration.Groups.Group,
                        function(group) {
                            return group.Name !== $scope.selectedGroup.Name;
                        }
                    );
                };

                $scope.Commit = function() {
                    // Compute hashes for grid passwords.
                    if (
                        $scope.cfgInitial.CorradeConfiguration.Password !==
                            $scope.cfg.CorradeConfiguration.Password
                    ) {
                        // SL passwords only account for the first 16 characters.
                        const loginPassword = new Hashes.MD5().hex(
                            $scope.cfg.CorradeConfiguration.Password.substring(0, 16)
                        );
                        $scope.cfg.CorradeConfiguration.Password = `$1$${loginPassword}`;
                        $scope.cfgInitial.CorradeConfiguration.Password =
                            `$1$${loginPassword}`;
                    }

                    // Compute hashes for groups.
                    var SHA1 = new Hashes.SHA1();
                    $.each($scope.cfg.CorradeConfiguration.Groups.Group,
                        function(
                            index,
                            group
                        ) {
                            var initialGroup;
                            const passwordHash = SHA1.hex(group.Password);

                            initialGroup = $.grep(
                                $scope.cfgInitial.CorradeConfiguration.Groups.Group,
                                function(initialGroup) {
                                    return group.Name === initialGroup.Name;
                                }
                            );

                            if (Array.isArray(initialGroup) && initialGroup.length) {
                                initialGroup = initialGroup.pop();

                                if (initialGroup.Password === group.Password) return;

                                initialGroup.Password = passwordHash;
                            }

                            group.Password = passwordHash;
                        });

                    // Remove position points for regions "last" and "home"
                    for (
                        let i = 0;
                        i <
                            $scope.cfg.CorradeConfiguration.StartLocations.StartLocation
                            .length;
                        ++i
                    ) {
                        if (
                            $scope.cfg.CorradeConfiguration.StartLocations.StartLocation[i]
                                .Region ===
                                "last" ||
                                $scope.cfg.CorradeConfiguration.StartLocations.StartLocation[i]
                                .Region ===
                                "home"
                        ) {
                            delete $scope.cfg.CorradeConfiguration.StartLocations
                                .StartLocation[i].Position;
                        }
                    }

                    // Clean up angular JSON.
                    const cleanCfg = JSON.parse(angular.toJson($scope.cfg));
                    const xml = $scope.x2js.js2xml(cleanCfg);
                    setConfigurationAuthorized(xml);

                    /*
                    $ngConfirm({
                        title: '',
                        contentUrl: '/fragments/codeword.html',
                        theme: 'bootstrap',
                        scope: $scope,
                        onScopeReady: function (scope) {
                            // call button action when enter is pressed
                            var self = this
                            scope.submit = function () {
                                self.buttons.formSubmit.action(scope)
                                self.close()
                            }
                        },
                        buttons: {
                            formSubmit: {
                                text: 'Submit',
                                btnClass: 'btn-green',
                                keys: ['enter'],
                                action: function (scope) {
                                    var codeword = scope.codeword
                                    if (!codeword) {
                                        return false
                                    }
    
                                    setConfigurationAuthorized(xml, codeword)
                                }
                            },
                            cancel: {
                                text: 'Cancel',
                                btnClass: 'btn-red',
                                keys: ['esc'],
                                action: function () {
                                    //close
                                    window.location.href = '/'
                                }
                            }
                        },
                        containerFluid: true,
                        columnClass: 'col-md-6 col-md-offset-3'
                    })
                    */
                };

                function setConfigurationAuthorized(xml) {
                    // Encrypt XML file.
                    const salt = forge.random.getBytesSync(16);

                    const sha1 = forge.md.sha1.create();
                    sha1.update(Cookies.get("Session"));

                    var derivedKey = forge.pkcs5.pbkdf2(
                        sha1.digest().toHex(),
                        salt,
                        1000,
                        48
                    );

                    derivedKey = stringToAsciiByteArray(derivedKey);

                    var key = derivedKey.slice(0, 32);

                    key = String.fromCodePoint(...key);

                    var iv = derivedKey.slice(32);

                    iv = String.fromCodePoint(...iv);

                    const cipher = forge.cipher.createCipher("AES-CBC", key);
                    cipher.start({ iv: iv });
                    cipher.update(forge.util.createBuffer(xml));
                    if (!cipher.finish()) {
                        $("#resultModalSubject").text("Failure");
                        $("#resultModalBody").text(
                            "Could not encrypt configuration file."
                        );
                        $("#resultModal").modal("show");
                        return;
                    }

                    const result = forge.util.encode64(
                        salt + cipher.output.getBytes()
                    );

                    $http({
                        method: "POST",
                        url: urljoin(
                            CONSTANTS.BaseURL,
                            "/cfg/",
                            "/CorradeConfiguration.xml"
                        ),
                        data: result,
                        headers: { 'Content-Type': "text/plain" }
                    }).then(
                        function(response) {
                            $("#resultModalSubject").text("Success");
                            $("#resultModalBody").text(
                                "Changes have been applied, Corrade should reload the configuration in a moment."
                            );
                            $("#resultModal").modal("show");
                        },
                        function(error) {
                            $("#resultModalSubject").text("Failed");
                            $("#resultModalBody").text(
                                "Could not apply configuration. Please check your Corrade installation."
                            );
                            $("#resultModal").modal("show");
                        }
                    );
                }

                getConfigurationAuthorized();

                /*
                $ngConfirm({
                    title: '',
                    contentUrl: '/fragments/codeword.html',
                    theme: 'bootstrap',
                    scope: $scope,
                    onScopeReady: function (scope) {
                        // call button action when enter is pressed
                        var self = this
                        scope.submit = function () {
                            self.buttons.formSubmit.action(scope)
                            self.close()
                        }
                    },
                    buttons: {
                        formSubmit: {
                            text: 'Submit',
                            btnClass: 'btn-green',
                            keys: ['enter'],
                            action: function (scope) {
                                var codeword = scope.codeword
                                if (!codeword) {
                                    return false
                                }
    
                                getConfigurationAuthorized(codeword)
                            }
                        },
                        cancel: {
                            text: 'Cancel',
                            btnClass: 'btn-red',
                            keys: ['esc'],
                            action: function () {
                                //close
                                window.location.href = '/'
                            }
                        }
                    },
                    containerFluid: true,
                    columnClass: 'col-md-6 col-md-offset-3'
                })
                */
            }
        ]);
})();