(function () {

'use strict';

angular.module('OpenSlidesApp.users.site', [
    'OpenSlidesApp.users',
    'OpenSlidesApp.core.pdf',
    'OpenSlidesApp.users.pdf',
    'OpenSlidesApp.users.csv',
])

.config([
    'mainMenuProvider',
    'gettext',
    function (mainMenuProvider, gettext) {
        mainMenuProvider.register({
            'ui_sref': 'users.user.list',
            'img_class': 'user',
            'title': gettext('Participants'),
            'weight': 500,
            'perm': 'users.can_see_name',
        });
    }
])
.config([
    'SearchProvider',
    'gettext',
    function (SearchProvider, gettext) {
        SearchProvider.register({
            'verboseName': gettext('Participants'),
            'collectionName': 'users/user',
            'urlDetailState': 'users.user.detail',
            'weight': 500,
        });
    }
])

.config([
    '$stateProvider',
    'gettext',
    function($stateProvider, gettext) {
        $stateProvider
        .state('users', {
            url: '/users',
            abstract: true,
            template: "<ui-view/>",
            data: {
                title: gettext('Participants'),
                basePerm: 'users.can_see_name',
            },
        })
        .state('users.user', {
            abstract: true,
            template: "<ui-view/>",
        })
        .state('users.user.list', {})
        .state('users.user.create', {})
        .state('users.user.detail', {
            resolve: {
                userId: ['$stateParams', function($stateParams) {
                    return $stateParams.id;
                }]
            }
        })
        .state('users.user.change-password', {
            url: '/change-password/{id}',
            controller: 'UserChangePasswordCtrl',
            templateUrl: 'static/templates/users/user-change-password.html',
            resolve: {
                userId: ['$stateParams', function($stateParams) {
                    return $stateParams.id;
                }]
            }
        })
        .state('users.user.import', {
            url: '/import',
            controller: 'UserImportCtrl',
        })
        // groups
        .state('users.group', {
            url: '/groups',
            abstract: true,
            template: "<ui-view/>",
            data: {
                title: gettext('Groups'),
            },
        })
        .state('users.group.list', {
            resolve: {
                permissions: function(Group) {
                    return Group.getPermissions();
                }
            }
        })
        .state('login', {
            template: null,
            url: '/login',
            params: {
                guest_enabled: false,
                msg: null,
            },
            onEnter: ['$state', '$stateParams', 'ngDialog', function($state, $stateParams, ngDialog) {
                ngDialog.open({
                    template: 'static/templates/core/login-form.html',
                    controller: 'LoginFormCtrl',
                    showClose: $stateParams.guest_enabled,
                    closeByEscape: $stateParams.guest_enabled,
                    closeByDocument: $stateParams.guest_enabled,
                });
            }],
            data: {
                title: 'Login',
            },
        });
    }
])

/*
 * Directive to check for permissions
 *
 * This is the Code from angular.js ngIf.
 *
 * TODO: find a way not to copy the code.
*/
.directive('osPerms', [
    '$animate',
    function($animate) {
        return {
            multiElement: true,
            transclude: 'element',
            priority: 600,
            terminal: true,
            restrict: 'A',
            $$tlb: true,
            link: function($scope, $element, $attr, ctrl, $transclude) {
                var block, childScope, previousElements, perms;
                if ($attr.osPerms[0] === '!') {
                    perms = _.trimStart($attr.osPerms, '!');
                } else {
                    perms = $attr.osPerms;
                }
                $scope.$watch(
                    function (scope) {
                        return scope.operator && scope.operator.hasPerms(perms);
                    },
                    function (value) {
                        if ($attr.osPerms[0] === '!') {
                            value = !value;
                        }
                        if (value) {
                            if (!childScope) {
                                $transclude(function(clone, newScope) {
                                    childScope = newScope;
                                    clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
                                    // Note: We only need the first/last node of the cloned nodes.
                                    // However, we need to keep the reference to the jqlite wrapper as it might be changed later
                                    // by a directive with templateUrl when its template arrives.
                                    block = {
                                        clone: clone
                                    };
                                    $animate.enter(clone, $element.parent(), $element);
                                });
                            }
                        } else {
                            if (previousElements) {
                                previousElements.remove();
                                previousElements = null;
                            }
                            if (childScope) {
                                childScope.$destroy();
                                childScope = null;
                            }
                            if (block) {
                                previousElements = getBlockNodes(block.clone);
                                $animate.leave(previousElements).then(function() {
                                    previousElements = null;
                                });
                                block = null;
                            }
                        }
                    }
                );
            }
        };
    }
])

.factory('PasswordGenerator', [
    function () {
        return {
            generate: function (length) {
                if (!length) {
                    length = 8;
                }
                var chars = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789',
                pw = '';
                for (var i = 0; i < length; ++i) {
                    pw += chars.charAt(Math.floor(Math.random() * chars.length));
                }
                return pw;
            }
        };
    }
])

// Service for generic assignment form (create and update)
.factory('UserForm', [
    '$http',
    'gettextCatalog',
    'Editor',
    'Group',
    'Mediafile',
    'PasswordGenerator',
    function ($http, gettextCatalog, Editor, Group, Mediafile, PasswordGenerator) {
        return {
            // ngDialog for user form
            getDialog: function (user) {
                return {
                    template: 'static/templates/users/user-form.html',
                    controller: (user) ? 'UserUpdateCtrl' : 'UserCreateCtrl',
                    className: 'ngdialog-theme-default wide-form',
                    closeByEscape: false,
                    closeByDocument: false,
                    resolve: {
                        userId: function () {return user ? user.id : void 0;},
                    }
                };
            },
            // angular-formly fields for user form
            getFormFields: function (hideOnCreateForm) {
                var images = Mediafile.getAllImages();
                return [
                {
                    className: "row",
                    fieldGroup: [
                        {
                            key: 'title',
                            type: 'input',
                            className: "col-xs-2 no-padding-left",
                            templateOptions: {
                                label: gettextCatalog.getString('Title')
                            }
                        },
                        {
                            key: 'first_name',
                            type: 'input',
                            className: "col-xs-5 no-padding",
                            templateOptions: {
                                label: gettextCatalog.getString('Given name')
                            }
                        },
                        {
                            key: 'last_name',
                            type: 'input',
                            className: "col-xs-5 no-padding-right",
                            templateOptions: {
                                label: gettextCatalog.getString('Surname')
                            }
                        }
                    ]
                },
                {
                    className: "row",
                    fieldGroup: [
                        {
                            key: 'structure_level',
                            type: 'input',
                            className: "col-xs-9 no-padding-left",
                            templateOptions: {
                                label: gettextCatalog.getString('Structure level'),
                            }
                        },
                        {   key: 'number',
                            type: 'input',
                            className: "col-xs-3 no-padding-left no-padding-right",
                            templateOptions: {
                                label:gettextCatalog.getString('Participant number')
                            }
                        }
                    ]
                },
                {
                    key: 'username',
                    type: 'input',
                    templateOptions: {
                        label: gettextCatalog.getString('Username')
                    },
                    hide: hideOnCreateForm
                },
                {
                    key: 'groups_id',
                    type: 'select-multiple',
                    templateOptions: {
                        label: gettextCatalog.getString('Groups'),
                        options: Group.getAll(),
                        ngOptions: 'option.id as option.name | translate for option in to.options | ' +
                                   'filter: {id: "!1"}',
                        placeholder: gettextCatalog.getString('Select or search a group ...')
                    }
                },
                {
                    key: 'default_password',
                    type: 'input',
                    templateOptions: {
                        label: gettextCatalog.getString('Initial password'),
                        description: gettextCatalog.getString('Initial password can not be changed.'),
                        addonRight: {
                            text: gettextCatalog.getString('Generate'),
                            class: 'fa fa-magic',
                            onClick:function (options, scope) {
                                scope.$parent.model.default_password = PasswordGenerator.generate();
                            }
                        }
                    },
                    hide: !hideOnCreateForm
                },
                {
                    key: 'comment',
                    type: 'input',
                    templateOptions: {
                        label: gettextCatalog.getString('Comment'),
                        description: gettextCatalog.getString('Only for internal notes.')
                    }
                },
                {
                    key: 'more',
                    type: 'checkbox',
                    templateOptions: {
                        label: gettextCatalog.getString('Show extended fields')
                    }
                },
                {
                    template: '<hr class="smallhr">',
                    hideExpression: '!model.more'
                },
                {
                    key: 'is_present',
                    type: 'checkbox',
                    templateOptions: {
                        label: gettextCatalog.getString('Is present'),
                        description: gettextCatalog.getString('Designates whether this user is in the room or not.')
                    },
                    defaultValue: true,
                    hideExpression: '!model.more'
                },
                {
                    key: 'is_active',
                    type: 'checkbox',
                    templateOptions: {
                        label: gettextCatalog.getString('Is active'),
                        description: gettextCatalog.getString(
                            'Designates whether this user should be treated as ' +
                            'active. Unselect this instead of deleting the account.')
                    },
                    defaultValue: true,
                    hideExpression: '!model.more'
                },
                {
                    key: 'is_committee',
                    type: 'checkbox',
                    templateOptions: {
                        label: gettextCatalog.getString('Is a committee'),
                        description: gettextCatalog.getString(
                            'Designates whether this user should be treated as a committee.')
                    },
                    defaultValue: false,
                    hideExpression: '!model.more'
                },
                {
                    key: 'about_me',
                    type: 'editor',
                    templateOptions: {
                        label: gettextCatalog.getString('About me'),
                    },
                    data: {
                        ckeditorOptions: Editor.getOptions(images)
                    },
                    hideExpression: '!model.more'
                }
                ];
            }
        };
    }
])

.factory('UserProfileForm', [
    'gettextCatalog',
    'Editor',
    'Mediafile',
    function (gettextCatalog, Editor, Mediafile) {
        return {
            // ngDialog for user form
            getDialog: function (user) {
                return {
                    template: 'static/templates/users/profile-password-form.html',
                    controller: 'UserProfileCtrl',
                    className: 'ngdialog-theme-default wide-form',
                    closeByEscape: false,
                    closeByDocument: false,
                };
            },
            // angular-formly fields for user form
            getFormFields: function (hideOnCreateForm) {
                var images = Mediafile.getAllImages();
                return [
                {
                    key: 'username',
                    type: 'input',
                    templateOptions: {
                        label: gettextCatalog.getString('Username'),
                        required: true
                    },
                },
                {
                    key: 'about_me',
                    type: 'editor',
                    templateOptions: {
                        label: gettextCatalog.getString('About me'),
                    },
                    data: {
                        ckeditorOptions: Editor.getOptions(images)
                    },
                }
                ];
            }
        };
    }
])

.factory('UserPasswordForm', [
    'gettextCatalog',
    function (gettextCatalog) {
        return {
            // ngDialog for user form
            getDialog: function (user) {
                return {
                    template: 'static/templates/users/profile-password-form.html',
                    controller: 'UserPasswordCtrl',
                    className: 'ngdialog-theme-default',
                    closeByEscape: false,
                    closeByDocument: false,
                };
            },
            // angular-formly fields for user form
            getFormFields: function (hideOnCreateForm) {
                return [
                {
                    key: 'oldPassword',
                    type: 'password',
                    templateOptions: {
                        label: gettextCatalog.getString('Old password'),
                        required: true
                    },
                },
                {
                    key: 'newPassword',
                    type: 'password',
                    templateOptions: {
                        label: gettextCatalog.getString('New password'),
                        required: true
                    },
                },
                {
                    key: 'newPassword2',
                    type: 'password',
                    templateOptions: {
                        label: gettextCatalog.getString('Confirm new password'),
                        required: true
                    },
                },
                ];
            }
        };
    }
])

.controller('UserListCtrl', [
    '$scope',
    '$state',
    '$http',
    'ngDialog',
    'UserForm',
    'User',
    'Group',
    'PasswordGenerator',
    'Projector',
    'ProjectionDefault',
    'UserListContentProvider',
    'Config',
    'UserAccessDataListContentProvider',
    'PdfMakeDocumentProvider',
    'gettextCatalog',
    'UserCsvExport',
    'osTableFilter',
    'osTableSort',
    'gettext',
    'PdfCreate',
    function($scope, $state, $http, ngDialog, UserForm, User, Group, PasswordGenerator, Projector, ProjectionDefault,
        UserListContentProvider, Config, UserAccessDataListContentProvider, PdfMakeDocumentProvider, gettextCatalog,
        UserCsvExport, osTableFilter, osTableSort, gettext, PdfCreate) {
        User.bindAll({}, $scope, 'users');
        Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups');
        $scope.$watch(function () {
            return Projector.lastModified();
        }, function () {
            var projectiondefault = ProjectionDefault.filter({name: 'users'})[0];
            if (projectiondefault) {
                $scope.defaultProjectorId = projectiondefault.projector_id;
            }
        });
        $scope.alert = {};

        // Filtering
        $scope.filter = osTableFilter.createInstance('UserTableFilter');

        if (!$scope.filter.existsStorageEntry()) {
            $scope.filter.multiselectFilters = {
                group: [],
            };
            $scope.filter.booleanFilters = {
                isPresent: {
                    value: undefined,
                    displayName: gettext('Present'),
                    choiceYes: gettext('Is present'),
                    choiceNo: gettext('Is not present'),
                    needExtraPermission: true,
                },
                isActive: {
                    value: undefined,
                    displayName: gettext('Active'),
                    choiceYes: gettext('Is active'),
                    choiceNo: gettext('Is not active'),
                    needExtraPermission: true,
                },
                isCommittee: {
                    value: undefined,
                    displayName: gettext('Committee'),
                    choiceYes: gettext('Is a committee'),
                    choiceNo: gettext('Is not a committee'),
                },

            };
        }
        $scope.filter.propertyList = ['first_name', 'last_name', 'title', 'number', 'comment', 'structure_level'];
        $scope.filter.propertyDict = {
            'groups_id' : function (group_id) {
                return Group.get(group_id).name;
            },
        };
        $scope.getItemId = {
            group: function (user) {return user.groups_id;},
        };
        // Sorting
        $scope.sort = osTableSort.createInstance();
        $scope.sort.column = $scope.config('users_sort_by');
        $scope.sortOptions = [
            {name: 'first_name',
             display_name: gettext('Given name')},
            {name: 'last_name',
             display_name: gettext('Surname')},
            {name: 'is_present',
             display_name: gettext('Present')},
            {name: 'is_active',
             display_name: gettext('Active')},
            {name: 'is_committee',
             display_name: gettext('Committee')},
            {name: 'number',
             display_name: gettext('Number')},
            {name: 'structure_level',
             display_name: gettext('Structure level')},
            {name: 'comment',
             display_name: gettext('Comment')},
        ];

        // pagination
        $scope.currentPage = 1;
        $scope.itemsPerPage = 25;
        $scope.limitBegin = 0;
        $scope.pageChanged = function() {
            $scope.limitBegin = ($scope.currentPage - 1) * $scope.itemsPerPage;
        };

        // Toggle group from user
        $scope.toggleGroup = function (user, group) {
            if (_.indexOf(user.groups_id, group.id) > -1) {
                user.groups_id = _.filter(user.groups_id, function (group_id) {
                    return group_id != group.id;
                });
            } else {
                user.groups_id.push(group.id);
            }
            $scope.save(user);
        };
        // open new/edit dialog
        $scope.openDialog = function (user) {
            ngDialog.open(UserForm.getDialog(user));
        };
        // save changed user
        $scope.save = function (user) {
            User.save(user).then(
                function(success) {
                    //user.quickEdit = false;
                    $scope.alert.show = false;
                },
                function(error){
                    $scope.alert = ErrorMessage.forAlert(error);
                });
        };
        // delete single user
        $scope.delete = function (user) {
            User.destroy(user.id);
        };
        // *** select mode functions ***
        $scope.isSelectMode = false;
        // check all checkboxes
        $scope.checkAll = function () {
            $scope.selectedAll = !$scope.selectedAll;
            _.forEach($scope.usersFiltered, function (user) {
                user.selected = $scope.selectedAll;
            });
        };
        // uncheck all checkboxes if isSelectMode is closed
        $scope.uncheckAll = function () {
            if (!$scope.isSelectMode) {
                $scope.selectedAll = false;
                angular.forEach($scope.users, function (user) {
                    user.selected = false;
                });
            }
        };
        var selectModeAction = function (predicate) {
            angular.forEach($scope.usersFiltered, function (user) {
                if (user.selected) {
                    predicate(user);
                }
            });
            $scope.isSelectMode = false;
            $scope.uncheckAll();
        };
        // delete all selected users
        $scope.deleteMultiple = function () {
            selectModeAction(function (user) {
                $scope.delete(user);
            });
        };
        // add group for selected users
        $scope.addGroupMultiple = function (group) {
            if (group) {
                selectModeAction(function (user) {
                    user.groups_id.push(group);
                    User.save(user);
                });
            }
        };
        // remove group for selected users
        $scope.removeGroupMultiple = function (group) {
            if (group) {
                selectModeAction(function (user) {
                    var groupIndex = _.indexOf(user.groups_id, parseInt(group));
                    if (groupIndex > -1) {
                        user.groups_id.splice(groupIndex, 1);
                        User.save(user);
                    }
                });
            }
        };
        // generate new passwords
        $scope.generateNewPasswordsMultiple = function () {
            selectModeAction(function (user) {
                var newPassword = PasswordGenerator.generate();
                user.default_password = newPassword;
                User.save(user);
                $http.post(
                    '/rest/users/user/' + user.id + '/reset_password/',
                    {'password': newPassword}
                );
            });
        };
        // set boolean properties (is_active, is_present, is_committee)
        $scope.setBoolPropertyMultiple = function (property, value) {
            selectModeAction(function (user) {
                user[property] = value;
                User.save(user);
            });
        };

        // Export as PDF
        $scope.pdfExportUserList = function () {
            var filename = gettextCatalog.getString("List of participants")+".pdf";
            var userListContentProvider = UserListContentProvider.createInstance($scope.usersFiltered, $scope.groups);
            var documentProvider = PdfMakeDocumentProvider.createInstance(userListContentProvider);
            PdfCreate.download(documentProvider.getDocument(), filename);
        };
        $scope.pdfExportUserAccessDataList = function () {
            var filename = gettextCatalog.getString("List of access data")+".pdf";
            var userAccessDataListContentProvider = UserAccessDataListContentProvider.createInstance(
                $scope.usersFiltered, $scope.groups, Config);
            var documentProvider = PdfMakeDocumentProvider.createInstance(userAccessDataListContentProvider);
            var noFooter = true;
            PdfCreate.download(documentProvider.getDocument(noFooter), filename);
        };
        // Export as a csv file
        $scope.csvExport = function () {
            UserCsvExport.export($scope.usersFiltered);
        };
    }
])

.controller('UserDetailCtrl', [
    '$scope',
    'ngDialog',
    'UserForm',
    'User',
    'userId',
    'Group',
    'Projector',
    'ProjectionDefault',
    function($scope, ngDialog, UserForm, User, userId, Group, Projector, ProjectionDefault) {
        User.bindOne(userId, $scope, 'user');
        Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups');
        $scope.$watch(function () {
            return Projector.lastModified();
        }, function () {
            var projectiondefault = ProjectionDefault.filter({name: 'users'})[0];
            if (projectiondefault) {
                $scope.defaultProjectorId = projectiondefault.projector_id;
            }
        });

        // open edit dialog
        $scope.openDialog = function (user) {
            ngDialog.open(UserForm.getDialog(user));
        };
    }
])

.controller('UserCreateCtrl', [
    '$scope',
    '$state',
    'User',
    'UserForm',
    'Group',
    'ErrorMessage',
    function($scope, $state, User, UserForm, Group, ErrorMessage) {
        Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups');
        $scope.alert = {};
        // get all form fields
        $scope.formFields = UserForm.getFormFields(true);

        // save user
        $scope.save = function (user) {
            if (!user.groups_id) {
                user.groups_id = [];
            }
            User.create(user).then(
                function (success) {
                    $scope.closeThisDialog();
                },
                function (error) {
                    $scope.alert = ErrorMessage.forAlert(error);
                }
            );
        };
    }
])

.controller('UserUpdateCtrl', [
    '$scope',
    '$state',
    '$http',
    'User',
    'UserForm',
    'Group',
    'userId',
    'ErrorMessage',
    function($scope, $state, $http, User, UserForm, Group, userId, ErrorMessage) {
        Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups');
        $scope.alert = {};
        // set initial values for form model by create deep copy of user object
        // so list/detail view is not updated while editing
        $scope.model = angular.copy(User.get(userId));

        // get all form fields
        $scope.formFields = UserForm.getFormFields();

        // save user
        $scope.save = function (user) {
            if (!user.groups_id) {
                user.groups_id = [];
            }
            // inject the changed user (copy) object back into DS store
            User.inject(user);
            // save change user object on server
            User.save(user).then(
                function (success) {
                    $scope.closeThisDialog();
                },
                function (error) {
                    // save error: revert all changes by restore
                    // (refresh) original user object from server
                    User.refresh(user);
                    $scope.alert = ErrorMessage.forAlert(error);
                }
            );
        };
    }
])

.controller('UserProfileCtrl', [
    '$scope',
    'Editor',
    'User',
    'operator',
    'UserProfileForm',
    'gettext',
    'ErrorMessage',
    function($scope, Editor, User, operator, UserProfileForm, gettext, ErrorMessage) {
        $scope.model = angular.copy(operator.user);
        $scope.title = gettext('Edit profile');
        $scope.formFields = UserProfileForm.getFormFields();
        $scope.save = function (user) {
            User.inject(user);
            User.save(user).then(
                function(success) {
                    $scope.closeThisDialog();
                },
                function(error) {
                    // save error: revert all changes by restore
                    // (refresh) original user object from server
                    User.refresh(user);
                    $scope.alert = ErrorMessage.forAlert(error);
                }
            );
        };
    }
])

.controller('UserChangePasswordCtrl', [
    '$scope',
    '$state',
    '$http',
    'User',
    'userId',
    'gettextCatalog',
    'PasswordGenerator',
    'ErrorMessage',
    function($scope, $state, $http, User, userId, gettextCatalog, PasswordGenerator, ErrorMessage) {
        User.bindOne(userId, $scope, 'user');
        $scope.alert = {};
        $scope.generatePassword = function () {
            $scope.new_password = PasswordGenerator.generate();
        };
        $scope.save = function (user) {
            if ($scope.new_password !== '') {
                $http.post(
                    '/rest/users/user/' + user.id + '/reset_password/',
                    {'password': $scope.new_password}
                ).then(
                    function (success) {
                        $scope.alert = {type: 'success', msg: success.data.detail, show: true};
                        $scope.new_password = '';
                    },
                    function (error) {
                        $scope.alert = ErrorMessage.forAlert(error);
                    }
                );
            }
        };
    }
])

.controller('UserPasswordCtrl', [
    '$scope',
    '$state',
    '$http',
    'gettext',
    'UserPasswordForm',
    'ErrorMessage',
    function($scope, $state, $http, gettext, UserPasswordForm, ErrorMessage) {
        $scope.title = 'Change password';
        $scope.alert = {};
        $scope.model = {};
        $scope.formFields = UserPasswordForm.getFormFields();
        $scope.save = function (data) {
            if (data.newPassword != data.newPassword2) {
                data.newPassword = data.newPassword2 = '';
                $scope.alert = {
                    type: 'danger',
                    msg: gettext('Password confirmation does not match.'),
                    show: true,
                };
            } else {
                $http.post(
                    '/users/setpassword/',
                    {'old_password': data.oldPassword, 'new_password': data.newPassword}
                ).then(
                    function (success) {
                        $scope.closeThisDialog();
                    },
                    function (error) {
                        // Error, e. g. wrong old password.
                        $scope.model = {};
                        $scope.alert = ErrorMessage.forAlert(error);
                    }
                );
            }
        };
    }
])

.controller('UserImportCtrl', [
    '$scope',
    '$q',
    'gettext',
    'gettextCatalog',
    'User',
    'Group',
    'UserCsvExport',
    function($scope, $q, gettext, gettextCatalog, User, Group, UserCsvExport) {
        // import from textarea
        $scope.importByLine = function () {
            $scope.usernames = $scope.userlist[0].split("\n");
            $scope.importcounter = 0;
            $scope.usernames.forEach(function(name) {
                // Split each full name in first and last name.
                // The last word is set as last name, rest is the first name(s).
                // (e.g.: "Max Martin Mustermann" -> last_name = "Mustermann")
                var names = name.split(" ");
                var last_name = names.slice(-1)[0];
                var first_name = names.slice(0, -1).join(" ");
                var user = {
                    first_name: first_name,
                    last_name: last_name,
                    groups_id: []
                };
                User.create(user).then(
                    function(success) {
                        $scope.importcounter++;
                    }
                );
            });
        };

        // pagination
        $scope.currentPage = 1;
        $scope.itemsPerPage = 100;
        $scope.limitBegin = 0;
        $scope.pageChanged = function() {
            $scope.limitBegin = ($scope.currentPage - 1) * $scope.itemsPerPage;
        };
        $scope.duplicateActions = [
            gettext('keep original'),
            gettext('override new'),
            gettext('create duplicate')
        ];

        // *** csv import ***
        $scope.csvConfig = {
            accept: '.csv, .txt',
            encodingOptions: ['UTF-8', 'ISO-8859-1'],
            parseConfig: {
                skipEmptyLines: true,
            },
        };

        var FIELDS = ['title', 'first_name', 'last_name', 'structure_level', 'number',
        'groups', 'comment', 'is_active', 'is_present', 'is_committee', 'default_password'];
        $scope.users = [];
        $scope.onCsvChange = function (csv) {
            // All user objects are already loaded via the resolve statement from ui-router.
            var users = User.getAll();
            $scope.users = [];

            var csvUsers = [];
            _.forEach(csv.data, function (row) {
                if (row.length >= 2) {
                    var filledRow = _.zipObject(FIELDS, row);
                    csvUsers.push(filledRow);
                }
            });
            $scope.duplicates = 0;
            _.forEach(csvUsers, function (user) {
                user.selected = true;
                if (!user.first_name && !user.last_name) {
                    user.importerror = true;
                    user.name_error = gettext('Error: Given name or surname is required.');
                }
                // number
                if (!user.number) {
                    user.number = "";
                }
                // groups
                user.groups_id = []; // will be overwritten if there are groups
                if (user.groups) {
                    user.groups = user.groups.split(',');
                    user.groups = _.map(user.groups, function (group) {
                        return _.trim(group); // remove whitespaces on start or end
                    });

                    // All group objects are already loaded via the resolve statement from ui-router.
                    var allGroups = Group.getAll();
                    // in allGroupsNames ar all original group names and translated names if a
                    // translation exists (e.g. for default group Delegates)
                    var allGroupsNames = [];
                    _.forEach(allGroups, function (group) {
                        var groupTranslation = gettextCatalog.getString(group.name);
                        if (group.name !== groupTranslation) {
                            allGroupsNames.push(groupTranslation);
                        }
                        allGroupsNames.push(group.name);
                    });
                    user.groupsToCreate = _.difference(user.groups, allGroupsNames);

                    // for template:
                    user.groupsNotToCreate = _.difference(user.groups, user.groupsToCreate);
                }
                user.is_active = (user.is_active !== undefined && user.is_active === '1');
                user.is_present = (user.is_present !== undefined && user.is_present === '1');
                user.is_committee = (user.is_committee !== undefined && user.is_committee === '1');

                // Check for duplicates
                user.duplicate = false;
                users.forEach(function(user_) {
                    if (user_.first_name == user.first_name &&
                        user_.last_name == user.last_name &&
                        user_.structure_level == user.structure_level) {
                        if (user.duplicate) {
                            // there are multiple duplicates!
                            user.duplicate_info += '\n' + gettextCatalog.getString('There are more than one duplicates of this user!');
                        } else {
                            user.duplicate = true;
                            user.duplicateAction = $scope.duplicateActions[1];
                            user.duplicate_info = '';
                            if (user_.title)
                                user.duplicate_info += user_.title + ' ';
                            if (user_.first_name)
                                user.duplicate_info += user_.first_name;
                            if (user_.first_name && user_.last_name)
                                user.duplicate_info += ' ';
                            if (user_.last_name)
                                user.duplicate_info += user_.last_name;
                            user.duplicate_info += ' (';
                            if (user_.number)
                                user.duplicate_info += gettextCatalog.getString('Number') + ': ' + user_.number + ', ';
                            if (user_.structure_level)
                                user.duplicate_info += gettextCatalog.getString('Structure level') + ': ' + user_.structure_level + ', ';
                            user.duplicate_info += gettextCatalog.getString('Username') + ': ' + user_.username + ') '+
                                gettextCatalog.getString('already exists.');

                            $scope.duplicates++;
                        }
                    }
                });
                $scope.users.push(user);
            });
            $scope.calcStats();
        };

        // Stats
        $scope.calcStats = function() {
            // not imported: if importerror or duplicate->keep original
            $scope.usersWillNotBeImported = 0;
            // imported: all others
            $scope.usersWillBeImported = 0;

            $scope.users.forEach(function(user) {
                if (!user.selected || user.importerror || (user.duplicate && user.duplicateAction == $scope.duplicateActions[0])) {
                    $scope.usersWillNotBeImported++;
                } else {
                    $scope.usersWillBeImported++;
                }
            });
        };

        $scope.setGlobalAction = function (action) {
            $scope.users.forEach(function (user) {
                if (user.duplicate)
                    user.duplicateAction = action;
            });
            $scope.calcStats();
        };

        // import from csv file
        $scope.import = function () {
            $scope.csvImporting = true;

            // collect all needed groups and create non existing groups
            var groupsToCreate = [];
            _.forEach($scope.users, function (user) {
                if (user.selected && !user.importerror && user.groups.length) {
                    _.forEach(user.groupsToCreate, function (group) { // Just append groups, that are not listed yet.
                        if (_.indexOf(groupsToCreate, group) == -1) {
                            groupsToCreate.push(group);
                        }
                    });
                }
            });
            var createPromises = [];
            $scope.groupsCreated = 0;
            _.forEach(groupsToCreate, function (groupname) {
                var group = {
                    name: groupname,
                    permissions: []
                };
                createPromises.push(Group.create(group).then( function (success) {
                    $scope.groupsCreated++;
                }));
            });

            $q.all(createPromises).then(function () {
                // reload allGroups, now all new groups are created
                var allGroups = Group.getAll();
                var existingUsers = User.getAll();

                _.forEach($scope.users, function (user) {
                    if (user.selected && !user.importerror) {
                        // Assign all groups
                        _.forEach(user.groups, function(csvGroup) {
                            allGroups.forEach(function (allGroup) {
                                // check with and without translation
                                if (csvGroup === allGroup.name ||
                                    csvGroup === gettextCatalog.getString(allGroup.name)) {
                                    user.groups_id.push(allGroup.id);
                                }
                            });
                        });

                        // Do nothing on duplicateAction==duplicateActions[0] (keep original)
                        if (user.duplicate && (user.duplicateAction == $scope.duplicateActions[1])) {
                            // delete existing user
                            var deletePromises = [];
                            existingUsers.forEach(function(user_) {
                                if (user_.first_name == user.first_name &&
                                    user_.last_name == user.last_name &&
                                    user_.structure_level == user.structure_level) {
                                    deletePromises.push(User.destroy(user_.id));
                                }
                            });
                            $q.all(deletePromises).then(function() {
                                User.create(user).then(
                                    function(success) {
                                        user.imported = true;
                                    }
                                );
                            });
                        } else if (!user.duplicate ||
                                   (user.duplicateAction == $scope.duplicateActions[2])) {
                            // create user
                            User.create(user).then(
                                function(success) {
                                    user.imported = true;
                                }
                            );
                        }
                    }
                });
                $scope.csvimported = true;
            });
        };
        $scope.clear = function () {
            $scope.users = null;
        };
        // download CSV example file
        $scope.downloadCSVExample = function () {
            UserCsvExport.downloadExample();
        };
    }
])

.controller('GroupListCtrl', [
    '$scope',
    '$http',
    '$filter',
    'operator',
    'Group',
    'permissions',
    'gettext',
    'Agenda',
    'Assignment',
    'Mediafile',
    'Motion',
    'User',
    'ngDialog',
    'OpenSlidesPlugins',
    function($scope, $http, $filter, operator, Group, permissions, gettext, Agenda,
        Assignment, Mediafile, Motion, User, ngDialog, OpenSlidesPlugins) {
        $scope.permissions = permissions;

        $scope.$watch(function() {
            return Group.lastModified();
        }, function() {
            $scope.groups = $filter('orderBy')(Group.getAll(), 'id');

            // find all groups with the 2 dangerous permissions
            var groups_danger = [];
            $scope.groups.forEach(function (group) {
                if ((_.indexOf(group.permissions, 'users.can_see_name') > -1) &&
                    (_.indexOf(group.permissions, 'users.can_manage') > -1)){
                    if (operator.isInGroup(group)){
                        groups_danger.push(group);
                    }
                }
            });
            // if there is only one dangerous group, block it.
            $scope.group_danger = groups_danger.length == 1 ? groups_danger[0] : null;
        });

        // Dict to map plugin name -> display_name
        var pluginTranslation = {};
        _.forEach(OpenSlidesPlugins.getAll(), function (plugin) {
            pluginTranslation[plugin.name] = plugin.display_name;
        });
        $scope.apps = [];
        // Create the main clustering with appname->permissions
        angular.forEach(permissions, function(perm) {
            var permissionApp = perm.value.split('.')[0]; // get appname

            // To insert perm in the right spot in $scope.apps
            var insert = function (id, perm, verboseName) {
                if (!$scope.apps[id]) {
                    $scope.apps[id] = {
                        app_name: verboseName,
                        app_visible: true,
                        permissions: []
                    };
                }
                $scope.apps[id].permissions.push(perm);
            };

            switch(permissionApp) {
                case 'core': // id 0 (projector) and id 6 (general)
                    if (perm.value.indexOf('projector') > -1) {
                        insert(0, perm, gettext('Projector'));
                    } else {
                        insert(6, perm, gettext('General'));
                    }
                    break;
                case 'agenda': // id 1
                    insert(1, perm, Agenda.verboseName);
                    break;
                case 'motions': // id 2
                    insert(2, perm, Motion.verboseNamePlural);
                    break;
                case 'assignments': // id 3
                    insert(3, perm, Assignment.verboseNamePlural);
                    break;
                case 'mediafiles': // id 4
                    insert(4, perm, Mediafile.verboseNamePlural);
                    break;
                case 'users': // id 5
                    insert(5, perm, User.verboseNamePlural);
                    break;
                default: // plugins: id>5
                    var display_name = pluginTranslation[permissionApp] || permissionApp.charAt(0).toUpperCase() +
                        permissionApp.slice(1);
                    // does the app exists?
                    var result = -1;
                    angular.forEach($scope.apps, function (app, index) {
                        if (app.app_name === display_name)
                            result = index;
                    });
                    var id = result == -1 ? $scope.apps.length : result;
                    insert(id, perm, display_name);
                    break;
            }
        });

        // sort each app: first all permission with 'see', then 'manage', then the rest
        // save the permissions in different lists an concat them in the right order together
        // Special Users: the two "see"-permissions are normally swapped. To create the right
        // order, we could simply reverse the whole permissions.
        angular.forEach($scope.apps, function (app, index) {
            if(index == 5) { // users
                app.permissions.reverse();
            } else { // rest
                var see = [];
                var manage = [];
                var others = [];
                angular.forEach(app.permissions, function (perm) {
                    if (perm.value.indexOf('see') > -1) {
                        see.push(perm);
                    } else if (perm.value.indexOf('manage') > -1) {
                        manage.push(perm);
                    } else {
                        others.push(perm);
                    }
                });
                app.permissions = see.concat(manage.concat(others));
            }
        });

        // check if the given group has the given permission
        $scope.hasPerm = function (group, permission) {
            return _.indexOf(group.permissions, permission.value) > -1;
        };

        // The current user is not allowed to lock himself out of the configuration:
        // - if the permission is 'users.can_manage' or 'users.can_see'
        // - if the user is in only one group with these permissions (group_danger is set)
        $scope.danger = function (group, permission){
            if ($scope.group_danger){
                if (permission.value == 'users.can_see_name' ||
                    permission.value == 'users.can_manage'){
                    return $scope.group_danger == group;
                }
            }
            return false;
        };

        // delete selected group
        $scope.delete = function (group) {
            Group.destroy(group.id);
        };

        // save changed permission
        $scope.changePermission = function (group, perm) {
            if (!$scope.danger(group, perm)) {
                if (!$scope.hasPerm(group, perm)) { // activate perm
                    group.permissions.push(perm.value);
                } else {
                    // delete perm in group.permissions
                    group.permissions = _.filter(group.permissions, function(value) {
                        return value != perm.value; // remove perm
                    });
                }
                Group.save(group);
            }
        };

        $scope.openDialog = function (group) {
            ngDialog.open({
                template: 'static/templates/users/group-edit.html',
                controller: group ? 'GroupRenameCtrl' : 'GroupCreateCtrl',
                className: 'ngdialog-theme-default wide-form',
                closeByEscape: false,
                closeByDocument: false,
                resolve: {
                    group: function () {return group;},
                }
            });
        };
    }
])

.controller('GroupRenameCtrl', [
    '$scope',
    'Group',
    'group',
    'ErrorMessage',
    function($scope, Group, group, ErrorMessage) {
        $scope.group = group;
        $scope.new_name = group.name;

        $scope.alert = {};
        $scope.save = function() {
            var old_name = $scope.group.name;
            $scope.group.name = $scope.new_name;
            Group.save($scope.group).then(
                function (success) {
                    $scope.closeThisDialog();
                },
                function (error) {
                   $scope.alert = ErrorMessage.forAlert(error);
                   $scope.group.name = old_name;
                }
            );
        };
    }
])

.controller('GroupCreateCtrl', [
    '$scope',
    'Group',
    'ErrorMessage',
    function($scope, Group, ErrorMessage) {
        $scope.new_name = '';
        $scope.alert = {};

        $scope.save = function() {
            var group = {
                name: $scope.new_name,
                permissions: []
            };

            Group.create(group).then(
                function (success) {
                    $scope.closeThisDialog();
                },
                function (error) {
                   $scope.alert = ErrorMessage.forAlert(error);
                }
            );
        };
    }
])

.controller('userMenu', [
    '$scope',
    '$http',
    'DS',
    'User',
    'operator',
    'ngDialog',
    'UserProfileForm',
    'UserPasswordForm',
    function($scope, $http, DS, User, operator, ngDialog, UserProfileForm, UserPasswordForm) {
        $scope.logout = function () {
            $http.post('/users/logout/').then(function (response) {
                operator.setUser(null);
                window.location.reload();
            });
        };
        $scope.editProfile = function () {
            ngDialog.open(UserProfileForm.getDialog());
        };
        $scope.changePassword = function () {
            ngDialog.open(UserPasswordForm.getDialog());
        };
    }
])

.controller('LoginFormCtrl', [
    '$rootScope',
    '$scope',
    '$http',
    '$state',
    '$stateParams',
    '$q',
    'operator',
    'gettext',
    'autoupdate',
    'mainMenu',
    'DS',
    'ngDialog',
    function ($rootScope, $scope, $http, $state, $stateParams, $q, operator, gettext,
        autoupdate, mainMenu, DS, ngDialog) {
        $scope.alerts = [];

        if ($stateParams.msg) {
            $scope.alerts.push({
                type: 'danger',
                msg: $stateParams.msg,
            });
        }

        // check if guest login is allowed
        $scope.guestAllowed = $rootScope.guest_enabled;

        // get login info-text from server
        $http.get('/users/login/').then(function (success) {
            if(success.data.info_text) {
                $scope.alerts.push({
                    type: 'success',
                    msg: success.data.info_text
                });
            }
        });
        // check if cookies are enabled
        if (!navigator.cookieEnabled) {
            $scope.alerts.push({
                type: 'danger',
                msg: gettext('You have to enable cookies to use OpenSlides.'),
            });
        }

        // close alert function
        $scope.closeAlert = function(index) {
            $scope.alerts.splice(index, 1);
        };
        // login
        $scope.login = function () {
            $scope.closeThisDialog();
            $scope.alerts = [];
            var data = { 'username': $scope.username, 'password': $scope.password };
            if (!navigator.cookieEnabled) {
                data.cookies = false;
            }
            $http.post('/users/login/', data).then(
                function (response) {
                    // Success: User logged in.
                    // Clear store and reset deferred first message, if guests was enabled before.
                    DS.clear();
                    autoupdate.firstMessageDeferred = $q.defer();
                    // The next lines are partly the same lines as in core/start.js
                    autoupdate.newConnect();
                    autoupdate.firstMessageDeferred.promise.then(function () {
                        operator.setUser(response.data.user_id, response.data.user);
                        $rootScope.operator = operator;
                        mainMenu.updateMainMenu();
                        $state.go('home');
                        $rootScope.openslidesBootstrapDone = true;
                    });
                },
                function (error) {
                    // Error: Username or password is not correct.
                    $state.transitionTo($state.current, {msg: error.data.detail}, { 
                          reload: true, inherit: false, notify: true
                    });
                }
            );
        };
        // guest login
        $scope.guestLogin = function () {
            $scope.closeThisDialog();
            $state.go('home');
        };
    }
])

// Mark all users strings for translation in JavaScript.
.config([
    'gettext',
    function (gettext) {
        // permission strings (see models.py of each Django app)
        // agenda
        gettext('Can see agenda');
        gettext('Can manage agenda');
        gettext('Can see hidden items and time scheduling of agenda');
        gettext('Can put oneself on the list of speakers');
        // assignments
        gettext('Can see elections');
        gettext('Can nominate another participant');
        gettext('Can nominate oneself');
        gettext('Can manage elections');
        // core
        gettext('Can see the projector');
        gettext('Can manage the projector');
        gettext('Can see the front page');
        gettext('Can manage tags');
        gettext('Can manage configuration');
        gettext('Can use the chat');
        gettext('Can manage the chat');
        // mediafiles
        gettext('Can see the list of files');
        gettext('Can upload files');
        gettext('Can manage files');
        gettext('Can see hidden files');
        // motions
        gettext('Can see motions');
        gettext('Can create motions');
        gettext('Can support motions');
        gettext('Can manage motions');
        gettext('Can see and manage comments');
        // users
        gettext('Can see names of users');
        gettext('Can see extra data of users (e.g. present and comment)');
        gettext('Can manage users');

        // config strings in users/config_variables.py
        gettext('General');
        gettext('Sort name of participants by');
        gettext('Participants');
        gettext('Given name');
        gettext('Surname');
        gettext('PDF');
        gettext('Welcome to OpenSlides');
        gettext('Title for access data and welcome PDF');
        gettext('[Place for your welcome and help text.]');
        gettext('Help text for access data and welcome PDF');
        gettext('System URL');
        gettext('Used for QRCode in PDF of access data.');
        gettext('WLAN name (SSID)');
        gettext('Used for WLAN QRCode in PDF of access data.');
        gettext('WLAN password');
        gettext('Used for WLAN QRCode in PDF of access data.');
        gettext('WLAN encryption');
        gettext('Used for WLAN QRCode in PDF of access data.');
        gettext('WEP');
        gettext('WPA/WPA2');
        gettext('No encryption');
    }
]);


// this is code from angular.js. Find a way to call this function from this file
function getBlockNodes(nodes) {
  // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
  //             collection, otherwise update the original collection.
  var node = nodes[0];
  var endNode = nodes[nodes.length - 1];
  var blockNodes = [node];

  do {
    node = node.nextSibling;
    if (!node) break;
    blockNodes.push(node);
  } while (node !== endNode);

  return $(blockNodes);
}

}());
