(function () {

'use strict';

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

.config([
    'mainMenuProvider',
    'gettext',
    function (mainMenuProvider, gettext) {
        mainMenuProvider.register({
            'ui_sref': 'agenda.item.list',
            'img_class': 'calendar-o',
            'title': gettext('Agenda'),
            'weight': 200,
            'perm': 'agenda.can_see',
        });
    }
])

.config([
    'SearchProvider',
    'gettext',
    function (SearchProvider, gettext) {
        SearchProvider.register({
            'verboseName': gettext('Agenda'),
            'collectionName': 'agenda/item',
            'urlDetailState': 'agenda.item.detail',
            'weight': 200,
        });
    }
])

.config([
    '$stateProvider',
    'gettext',
    function($stateProvider, gettext) {
        $stateProvider
            .state('agenda', {
                url: '/agenda',
                abstract: true,
                template: "<ui-view/>",
                data: {
                    title: gettext('Agenda'),
                    basePerm: 'agenda.can_see',
                },
            })
            .state('agenda.item', {
                abstract: true,
                template: "<ui-view/>",
            })
            .state('agenda.item.list', {})
            .state('agenda.item.detail', {
                resolve: {
                    itemId: ['$stateParams', function($stateParams) {
                        return $stateParams.id;
                    }],
                }
            })
            .state('agenda.item.sort', {
                url: '/sort',
                controller: 'AgendaSortCtrl',
            })
            .state('agenda.current-list-of-speakers', {
                url: '/speakers',
                controller: 'CurrentListOfSpeakersViewCtrl',
                data: {
                    title: gettext('Current list of speakers'),
                },
            });
    }
])

.controller('ItemListCtrl', [
    '$scope',
    '$filter',
    '$http',
    '$state',
    'DS',
    'operator',
    'ngDialog',
    'Agenda',
    'TopicForm', // TODO: Remove this dependency. Use template hook for "New" and "Import" buttons.
    'AgendaTree',
    'Projector',
    'ProjectionDefault',
    'AgendaContentProvider',
    'PdfMakeDocumentProvider',
    'gettextCatalog',
    'gettext',
    'osTableFilter',
    'AgendaCsvExport',
    'PdfCreate',
    'ErrorMessage',
    function($scope, $filter, $http, $state, DS, operator, ngDialog, Agenda, TopicForm,
        AgendaTree, Projector, ProjectionDefault, AgendaContentProvider, PdfMakeDocumentProvider,
        gettextCatalog, gettext, osTableFilter, AgendaCsvExport, PdfCreate, ErrorMessage) {
        // Bind agenda tree to the scope
        $scope.$watch(function () {
            return Agenda.lastModified();
        }, function () {
            // Filter out items that doesn't have the list_item_title. This happens, if the
            // item is a hidden item but provides the list of speakers, but should not be
            // visible in the list view.
            var allowedItems = _.filter(Agenda.getAll(), function (item) {
                return item.list_view_title;
            });
            $scope.items = AgendaTree.getFlatTree(allowedItems);
            var subitems = $filter('filter')($scope.items, {'parent_id': ''});
            if (subitems.length) {
                $scope.agendaHasSubitems = true;
            }
        });
        Projector.bindAll({}, $scope, 'projectors');
        $scope.mainListTree = true;
        $scope.$watch(function () {
            return Projector.lastModified();
        }, function () {
            var projectiondefault =  ProjectionDefault.filter({name: 'agenda_all_items'})[0];
            if (projectiondefault) {
                $scope.defaultProjectorId_all_items = projectiondefault.projector_id;
            }
            $scope.projectionDefaults = ProjectionDefault.getAll();
        });
        $scope.alert = {};


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

        if (!$scope.filter.existsStorageEntry()) {
            $scope.filter.booleanFilters = {
                closed: {
                    value: undefined,
                    displayName: gettext('Closed items'),
                    choiceYes: gettext('Closed items'),
                    choiceNo: gettext('Open items'),
                },
                is_hidden: {
                    value: undefined,
                    displayName: gettext('Internal items'),
                    choiceYes: gettext('Internal items'),
                    choiceNo: gettext('No internal items'),
                    permission: 'agenda.can_see_hidden_items',
                },
            };
        }
        $scope.filter.propertyList = ['item_number', 'title', 'title_list_view', 'comment', 'duration'];
        $scope.filter.propertyFunctionList = [
            function (item) {return item.getListViewTitle();},
        ];
        $scope.filter.propertyDict = {
            'speakers' : function (speaker) {
                return '';
            },
        };

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

        // parse duration for inline editing
        $scope.generateDurationText = function (item) {
            //convert data from model format (m) to view format (hh:mm)
            if (item.duration) {
                var time = "",
                    totalminutes = item.duration;
                if (totalminutes < 0) {
                    time = "-";
                    totalminutes = -totalminutes;
                }
                var hh = Math.floor(totalminutes / 60);
                var mm = Math.floor(totalminutes % 60);
                // Add leading "0" for double digit values
                mm = ("0"+mm).slice(-2);
                time += hh + ":" + mm;
                item.durationText = time;
            } else {
                item.durationText = "";
            }
        };
        $scope.setDurationText = function (item) {
            //convert data from view format (hh:mm) to model format (m)
            var time = item.durationText.replace('h', '').split(':');
            var data;
            if (time.length > 1 && !isNaN(time[0]) && !isNaN(time[1])) {
                data = (+time[0]) * 60 + (+time[1]);
                if (data < 0) {
                    data = "-"+data;
                }
                item.duration = parseInt(data);
            } else if (time.length == 1 && !isNaN(time[0])) {
                data = (+time[0]);
                item.duration = parseInt(data);
            } else {
                item.duration = 0;
            }
            $scope.save(item);
        };

        /** Duration calculations **/
        $scope.sumDurations = function () {
            var totalDuration = 0;
            $scope.items.forEach(function (item) {
                if (item.duration) {
                    totalDuration += item.duration;
                }
            });
            return totalDuration;
        };
        $scope.calculateEndTime = function () {
            var totalDuration = $scope.sumDurations();
            var startTimestamp = $scope.config('agenda_start_event_date_time');
            if (startTimestamp) {
                var endTimestamp = startTimestamp + totalDuration * 60 * 1000;
                var endDate = new Date(endTimestamp);
                var mm = ("0" + endDate.getMinutes()).slice(-2);
                var dateStr = endDate.getHours() + ':' + mm;
                return dateStr;
            } else {
                return '';
            }
        };

        /** Agenda item functions **/
        // open dialog for new topics // TODO Remove this. Don't forget import button in template.
        $scope.newDialog = function () {
            ngDialog.open(TopicForm.getDialog());
        };
        // save changed item
        $scope.save = function (item) {
            Agenda.save(item).then(
                function (success) {
                    $scope.alert.show = false;
                },
                function (error) {
                    $scope.alert = ErrorMessage.forAlert(error);
                });
        };
        // delete related item
        $scope.deleteRelatedItem = function (item) {
            DS.destroy(item.content_object.collection, item.content_object.id);
        };
        // auto numbering of agenda items
        $scope.autoNumbering = function() {
            $http.post('/rest/agenda/item/numbering/', {});
        };
        // check open permission
        // TODO: Use generic solution here.
        $scope.isAllowedToSeeOpenLink = function (item) {
            var collection = item.content_object.collection;
            switch (collection) {
                case 'topics/topic':
                    return operator.hasPerms('agenda.can_see');
                case 'motions/motion':
                    return operator.hasPerms('motions.can_see');
                case 'motions/motion-block':
                    return operator.hasPerms('motions.can_see');
                case 'assignments/assignment':
                    return operator.hasPerms('assignments.can_see');
                default:
                    return false;
            }
        };
        $scope.edit = function (item) {
            ngDialog.open(item.getContentObjectForm().getDialog({id: item.content_object.id}));
        };
        // export
        $scope.pdfExport = function () {
            var filename = gettextCatalog.getString('Agenda') + '.pdf';
            var agendaContentProvider = AgendaContentProvider.createInstance($scope.itemsFiltered);
            var documentProvider = PdfMakeDocumentProvider.createInstance(agendaContentProvider);
            PdfCreate.download(documentProvider.getDocument(), filename);
        };
        $scope.csvExport = function () {
            AgendaCsvExport.export($scope.itemsFiltered);
        };

        /** select mode functions **/
        $scope.isSelectMode = false;
        // check all checkboxes
        $scope.checkAll = function () {
            $scope.selectedAll = !$scope.selectedAll;
            angular.forEach($scope.items, function (item) {
                item.selected = $scope.selectedAll;
            });
        };
        // uncheck all checkboxes if isDeleteMode is closed
        $scope.uncheckAll = function () {
            if (!$scope.isSelectMode) {
                $scope.selectedAll = false;
                angular.forEach($scope.items, function (item) {
                    item.selected = false;
                });
            }
        };
        // delete selected items
        $scope.deleteMultiple = function () {
            angular.forEach($scope.items, function (item) {
                if (item.selected) {
                    DS.destroy(item.content_object.collection, item.content_object.id);
                }
            });
            $scope.isSelectMode = false;
            $scope.uncheckAll();
        };

        /** Project functions **/
        // get ProjectionDefault for item
        $scope.getProjectionDefault = function (item) {
            if (item.tree) {
                return $scope.defaultProjectorId_all_items;
            } else {
                var app_name = item.content_object.collection.split('/')[0];
                var id = 1;
                $scope.projectionDefaults.forEach(function (projectionDefault) {
                    if (projectionDefault.name == app_name) {
                        id = projectionDefault.projector_id;
                    }
                });
                return id;
            }
        };
        // project agenda
        $scope.projectAgenda = function (projectorId, tree, id) {
            var isAgendaProjectedIds = $scope.isAgendaProjected($scope.mainListTree);
            _.forEach(isAgendaProjectedIds, function (id) {
                $http.post('/rest/core/projector/' + id + '/clear_elements/');
            });
            if (_.indexOf(isAgendaProjectedIds, projectorId) == -1) {
                $http.post('/rest/core/projector/' + projectorId + '/prune_elements/',
                    [{name: 'agenda/item-list', tree: tree, id: id}]);
            }
        };
        // change whether all items or only main items should be projected
        $scope.changeMainListTree = function () {
            var isAgendaProjectedId = $scope.isAgendaProjected($scope.mainListTree);
            $scope.mainListTree = !$scope.mainListTree;
            if (isAgendaProjectedId > 0) {
                $scope.projectAgenda(isAgendaProjectedId, $scope.mainListTree);
            }
        };
        // change whether one item or all subitems should be projected
        $scope.changeItemTree = function (item) {
            var isProjected = item.isProjected(item.tree);
            if (isProjected > 0) {
                // Deactivate and reactivate
                item.project(isProjected, item.tree);
                item.project(isProjected, !item.tree);
            }
            item.tree = !item.tree;
        };
        // check if agenda is projected
        $scope.isAgendaProjected = function (tree) {
            // Returns the ids of all projectors with an element with
            // the name 'agenda/item-list'. Else returns an empty list.
            var predicate = function (element) {
                var value;
                if (tree) {
                    // tree with all agenda items
                    value = element.name == 'agenda/item-list' &&
                        typeof element.id === 'undefined' &&
                        element.tree;
                } else {
                    // only main agenda items
                    value = element.name == 'agenda/item-list' &&
                        typeof element.id === 'undefined' &&
                        !element.tree;
                }
                return value;
            };
            var projectorIds = [];
            $scope.projectors.forEach(function (projector) {
                if (typeof _.findKey(projector.elements, predicate) === 'string') {
                    projectorIds.push(projector.id);
                }
            });
            return projectorIds;
        };
    }
])

.controller('ItemDetailCtrl', [
    '$scope',
    '$filter',
    'Agenda',
    'itemId',
    'Projector',
    'ProjectionDefault',
    'ErrorMessage',
    function ($scope, $filter, Agenda, itemId, Projector, ProjectionDefault, ErrorMessage) {
        $scope.alert = {};

        $scope.$watch(function () {
            return Agenda.lastModified(itemId);
        }, function () {
            $scope.item = Agenda.get(itemId);
            // all speakers
            $scope.speakers = $filter('orderBy')($scope.item.speakers, 'weight');
            // next speakers
            $scope.nextSpeakers = $filter('filter')($scope.speakers, {'begin_time': null});
            // current speaker
            $scope.currentSpeaker = $filter('filter')($scope.speakers, {'begin_time': '!!', 'end_time': null});
            // last speakers
            $scope.lastSpeakers = $filter('filter')($scope.speakers, {'end_time': '!!'});
        });
        $scope.$watch(function () {
            return Projector.lastModified();
        }, function () {
            var item_app_name = $scope.item.content_object.collection.split('/')[0];
            var projectiondefaultItem = ProjectionDefault.filter({name: item_app_name})[0];
            if (projectiondefaultItem) {
                $scope.defaultProjectorItemId = projectiondefaultItem.projector_id;
            }
            var projectiondefaultListOfSpeakers = ProjectionDefault.filter({name: 'agenda_list_of_speakers'})[0];
            if (projectiondefaultListOfSpeakers) {
                $scope.defaultProjectorListOfSpeakersId = projectiondefaultListOfSpeakers.projector_id;
            }
        });
    }
])

/* This is the controller for the list of speakers partial management template.
 * The parent controller needs to provide a $scope.item, $scope.speakers, $scope.nextSpeakers,
 * $scope.currentSpeakers, $scope.lastSpeakers. See (as example) ItemDetailCtrl. */
.controller('ListOfSpeakersManagementCtrl', [
    '$scope',
    '$http',
    '$filter',
    'Agenda',
    'User',
    'operator',
    'ErrorMessage',
    function ($scope, $http, $filter, Agenda, User, operator, ErrorMessage) {
        User.bindAll({}, $scope, 'users');
        $scope.speakerSelectBox = {};

        // close/open list of speakers of current item
        $scope.closeList = function (listClosed) {
            $scope.item.speaker_list_closed = listClosed;
            Agenda.save($scope.item);
        };

        // add user to list of speakers
        $scope.addSpeaker = function (userId) {
            $http.post('/rest/agenda/item/' + $scope.item.id + '/manage_speaker/', {'user': userId}).then(
                function (success) {
                    $scope.alert.show = false;
                    $scope.speakers = $scope.item.speakers;
                    $scope.speakerSelectBox = {};
                }, function (error) {
                    $scope.alert = ErrorMessage.forAlert(error);
                    $scope.speakerSelectBox = {};
                }
            );
        };

        // delete speaker(!) from list of speakers
        $scope.removeSpeaker = function (speakerId) {
            $http.delete(
                '/rest/agenda/item/' + $scope.item.id + '/manage_speaker/',
                {headers: {'Content-Type': 'application/json'},
                 data: JSON.stringify({speaker: speakerId})}
            )
            .then(function (success) {
                $scope.speakers = $scope.item.speakers;
            }, function (error) {
                $scope.alert = ErrorMessage.forAlert(error);
            });
            $scope.speakers = $scope.item.speakers;
        };

        //delete all speakers from list of speakers
        $scope.removeAllSpeakers = function () {
            var speakersOnList = [];
            angular.forEach($scope.item.speakers, function (speaker) {
                speakersOnList.push(speaker.id);
            });
            $http.delete(
                '/rest/agenda/item/' + $scope.item.id + '/manage_speaker/',
                {headers: {'Content-Type': 'application/json'},
                 data: JSON.stringify({speaker: speakersOnList})}
            )
            .then(function (success) {
                $scope.speakers = $scope.item.speakers;
            }, function (error) {
                $scope.alert = ErrorMessage.forAlert(error);
            });
            $scope.speakers = $scope.item.speakers;
        };

        // Return true if the requested user is allowed to do a specific action
        // and see the corresponding button (e.g. 'add me' or 'remove me').
        $scope.isAllowed = function (action) {
            var nextUsers = [];
            angular.forEach($scope.nextSpeakers, function (speaker) {
                nextUsers.push(speaker.user_id);
            });
            switch (action) {
                case 'add':
                    return (operator.hasPerms('agenda.can_be_speaker') &&
                            !$scope.item.speaker_list_closed &&
                            $.inArray(operator.user.id, nextUsers) == -1);
                case 'remove':
                    if (operator.user) {
                        return ($.inArray(operator.user.id, nextUsers) != -1);
                    }
                    return false;
                case 'removeAll':
                    return (operator.hasPerms('agenda.can_manage') &&
                            $scope.speakers.length > 0);
                case 'beginNextSpeech':
                    return (operator.hasPerms('agenda.can_manage') &&
                            $scope.nextSpeakers.length > 0);
                case 'endCurrentSpeech':
                    return (operator.hasPerms('agenda.can_manage') &&
                            $scope.currentSpeaker.length > 0);
                case 'showLastSpeakers':
                    return $scope.lastSpeakers.length > 0;
            }
        };

        // begin speech of selected/next speaker
        $scope.beginSpeech = function (speakerId) {
            $http.put('/rest/agenda/item/' + $scope.item.id + '/speak/', {'speaker': speakerId})
            .then(function (success) {
                $scope.alert.show = false;
            }, function (error) {
                $scope.alert = ErrorMessage.forAlert(error);
            });
        };

        // end speech of current speaker
        $scope.endSpeech = function () {
            $http.delete(
                '/rest/agenda/item/' + $scope.item.id + '/speak/',
                {headers: {'Content-Type': 'application/json'}, data: {}}
            ).then(
                function (success) {},
                function (error) {
                    $scope.alert = ErrorMessage.forAlert(error);
                }
            );
        };
        // gets speech duration of selected speaker in seconds
        $scope.getDuration = function (speaker) {
            var beginTimestamp = new Date(speaker.begin_time).getTime();
            var endTimestamp = new Date(speaker.end_time).getTime();
            // calculate duration in seconds
            return Math.floor((endTimestamp - beginTimestamp) / 1000);

        };
        // save reordered list of speakers
        $scope.treeOptions = {
            dropped: function (event) {
                var sortedSpeakers = [];
                var nextSpeakers = $filter('filter')($scope.speakers, {'begin_time': null});
                angular.forEach(nextSpeakers, function (speaker) {
                    sortedSpeakers.push(speaker.id);
                });
                $http.post('/rest/agenda/item/' + $scope.item.id + '/sort_speakers/',
                    {speakers: sortedSpeakers}
                );
            }
        };
    }
])

.controller('AgendaSortCtrl', [
    '$scope',
    '$http',
    'Agenda',
    'AgendaTree',
    'ErrorMessage',
    function($scope, $http, Agenda, AgendaTree, ErrorMessage) {
        // Bind agenda tree to the scope
        $scope.$watch(function () {
            return Agenda.lastModified();
        }, function () {
            $scope.items = AgendaTree.getTree(Agenda.getAll());
        });
        $scope.showInternalItems = true;
        $scope.alert = {};

        // save parent and weight of moved agenda item (and all items on same level)
        $scope.treeOptions = {
            dropped: function(event) {
                var parentID = null;
                var droppedItemID = event.source.nodeScope.$modelValue.id;
                if (event.dest.nodesScope.item) {
                    parentID = event.dest.nodesScope.item.id;
                }
                $http.post('/rest/agenda/item/sort/', {
                    nodes: event.dest.nodesScope.$modelValue,
                    parent_id: parentID}
                ).then(
                    function(success) {},
                    function(error){
                        $scope.alert = ErrorMessage.forAlert(error);
                    }
                );
            }
        };
    }
])

.controller('CurrentListOfSpeakersViewCtrl', [
    '$scope',
    '$http',
    '$filter',
    'Projector',
    'ProjectionDefault',
    'Agenda',
    'Config',
    'CurrentListOfSpeakersItem',
    'CurrentListOfSpeakersSlide',
    function($scope, $http, $filter, Projector, ProjectionDefault, Agenda, Config,
        CurrentListOfSpeakersItem, CurrentListOfSpeakersSlide) {
        $scope.alert = {};
        $scope.currentListOfSpeakers = CurrentListOfSpeakersSlide;

        // Watch for changes in the current list of speakers reference
        $scope.$watch(function () {
            return Config.lastModified('projector_currentListOfSpeakers_reference');
        }, function () {
            $scope.currentListOfSpeakersReference = $scope.config('projector_currentListOfSpeakers_reference');
            $scope.updateCurrentListOfSpeakersItem();
        });
        $scope.$watch(function () {
            return Projector.lastModified();
        }, function() {
            $scope.projectors = Projector.getAll();
            // If there is just one projector we provide just the overlay.
            if ($scope.projectors.length === 1) {
                $scope.currentListOfSpeakersAsOverlay = true;
            }
            $scope.updateCurrentListOfSpeakersItem();

            $scope.listOfSpeakersDefaultProjectorId = ProjectionDefault.filter({name: 'agenda_current_list_of_speakers'})[0].projector_id;
        });

        $scope.$watch(function () {
            return $scope.item ? Agenda.lastModified($scope.item.id) : void 0;
        }, function () {
            $scope.updateCurrentListOfSpeakersItem();
        });

        $scope.updateCurrentListOfSpeakersItem = function () {
            $scope.item = CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference);
            if ($scope.item) {
                // all speakers
                $scope.speakers = $filter('orderBy')($scope.item.speakers, 'weight');
                // next speakers
                $scope.nextSpeakers = $filter('filter')($scope.speakers, {'begin_time': null});
                // current speaker
                $scope.currentSpeaker = $filter('filter')($scope.speakers, {'begin_time': '!!', 'end_time': null});
                // last speakers
                $scope.lastSpeakers = $filter('filter')($scope.speakers, {'end_time': '!!'});
            } else {
                $scope.speakers = void 0;
                $scope.nextSpeakers = void 0;
                $scope.currentSpeaker = void 0;
                $scope.lastSpeakers = void 0;
            }
        };

        // Set the current overlay status
        if ($scope.currentListOfSpeakers.isProjected().length) {
            var isProjected = $scope.currentListOfSpeakers.isProjectedWithOverlayStatus();
            $scope.currentListOfSpeakersAsOverlay = isProjected[0].overlay;
        } else {
            $scope.currentListOfSpeakersAsOverlay = true;
        }
        $scope.setOverlay = function (overlay) {
            $scope.currentListOfSpeakersAsOverlay = overlay;
            var isProjected = $scope.currentListOfSpeakers.isProjectedWithOverlayStatus();
            if (isProjected.length) {
                _.forEach(isProjected, function (mapping) {
                    if (mapping.overlay != overlay) { // change the overlay if it is different
                        $scope.currentListOfSpeakers.project(mapping.projectorId, overlay);
                    }
                });
            }
        };
    }
])

//mark all agenda config strings for translation with Javascript
.config([
    'gettext',
    function (gettext) {
        gettext('Numbering prefix for agenda items');
        gettext('This prefix will be set if you run the automatic agenda numbering.');
        gettext('Agenda');
        gettext('Invalid input.');
        gettext('Numeral system for agenda items');
        gettext('Arabic');
        gettext('Roman');
        gettext('Begin of event');
        gettext('Input format: DD.MM.YYYY HH:MM');
        gettext('Number of last speakers to be shown on the projector');
        gettext('List of speakers');
        gettext('Show orange countdown in the last x seconds of speaking time');
        gettext('Enter duration in seconds. Choose 0 to disable warning color.');
        gettext('Couple countdown with the list of speakers');
        gettext('[Begin speech] starts the countdown, [End speech] stops the ' +
                'countdown.');
    }
 ]);

}());
