/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

(function() {
  'use strict';

  /**
   * @ngInject
   */
  CalendarListController.$inject = ['$rootScope', '$scope', '$q', '$timeout', '$state', '$mdDialog', 'sgHotkeys', 'sgFocus', 'Dialog', 'Preferences', 'CalendarSettings', 'Calendar', 'Component'];
  function CalendarListController($rootScope, $scope, $q, $timeout, $state, $mdDialog, sgHotkeys, focus, Dialog, Preferences, CalendarSettings, Calendar, Component) {
    var vm = this, hotkeys = [], type, sortLabels;

    sortLabels = {
      title: 'Title',
      location: 'Location',
      calendarName: 'Calendar',
      start: 'Start',
      priority: 'Priority',
      category: 'Category',
      status: 'Status',
      events: {
        end: 'End'
      },
      tasks: {
        end: 'Due Date'
      }
    };

    vm.component = Component;
    vm.componentType = 'events';
    vm.selectedList = 0;
    vm.selectComponentType = selectComponentType;
    vm.unselectComponents = unselectComponents;
    vm.selectAll = selectAll;
    vm.searchMode = searchMode;
    vm.toggleComponentSelection = toggleComponentSelection;
    vm.confirmDeleteSelectedComponents = confirmDeleteSelectedComponents;
    vm.openEvent = openEvent;
    vm.openTask = openTask;
    vm.newComponent = newComponent;
    vm.filter = filter;
    vm.filteredBy = filteredBy;
    vm.sort = sort;
    vm.sortedBy = sortedBy;
    vm.reload = reload;
    vm.cancelSearch = cancelSearch;
    vm.mode = { search: false, multiple: 0 };
    vm.isComponentOpened = false;


    this.$onInit = function() {
      _registerHotkeys(hotkeys);

      // Select list based on user's settings
      type = 'events';
      if (Preferences.settings.Calendar.SelectedList == 'tasksListView') {
        vm.selectedList = 1;
        type = 'tasks';
      }
      selectComponentType(type, { reload: true }); // fetch events/tasks lists

      // Refresh current list when the list of calendars is modified
      $rootScope.$on('calendars:list', function() {
        Component.$filter(vm.componentType, { reload: true });
      });

      // Update the component being dragged
      $rootScope.$on('calendar:dragend', updateComponentFromGhost);

      $scope.$on('$destroy', function() {
        // Deregister hotkeys
        _.forEach(hotkeys, function(key) {
          sgHotkeys.deregisterHotkey(key);
        });
      });
    };


    function _registerHotkeys(keys) {
      keys.push(sgHotkeys.createHotkey({
        key: l('hotkey_search'),
        description: l('Search'),
        callback: searchMode
      }));
      keys.push(sgHotkeys.createHotkey({
        key: l('hotkey_create_event'),
        description: l('Create a new event'),
        callback: newComponent,
        args: 'appointment'
      }));
      keys.push(sgHotkeys.createHotkey({
        key: l('hotkey_create_task'),
        description: l('Create a new task'),
        callback: newComponent,
        args: 'task'
      }));

      // Register the hotkeys
      _.forEach(keys, function(key) {
        sgHotkeys.registerHotkey(key);
      });
    }

    // Switch between components tabs
    function selectComponentType(type, options) {
      if (options && options.reload || vm.componentType != type) {
        if (angular.isUndefined(Component['$' + type]))
          Component.$filter(type);
        vm.unselectComponents();
        vm.componentType = type;
        Component.saveSelectedList(type);
      }
    }

    function unselectComponents() {
      _.forEach(Component['$' + vm.componentType], function(component) {
        component.selected = false;
      });
      vm.mode.multiple = 0;
    }

    function selectAll() {
      _.forEach(Component['$' + vm.componentType], function(component) {
        component.selected = true;
      });
      vm.mode.multiple = Component['$' + vm.componentType].length;
    }

    function toggleComponentSelection($event, component) {
      component.selected = !component.selected;
      vm.mode.multiple += component.selected? 1 : -1;
      $event.preventDefault();
      $event.stopPropagation();
    }

    function searchMode() {
      vm.mode.search = true;
      focus('search');
    }

    function confirmDeleteSelectedComponents() {
      Dialog.confirm(l('Warning'),
                     l('Are you sure you want to delete the selected components?'),
                     { ok: l('Delete') })
        .then(function() {
          // User confirmed the deletion
          var components = _.filter(Component['$' + vm.componentType], function(component) {
            return component.selected;
          });
          Calendar.$deleteComponents(components).then(function() {
            vm.mode.multiple = 0;
            $rootScope.$emit('calendars:list');
          });
        });
    }

    function openEvent($event, event) {
      openComponent($event, event, 'appointment');
    }

    function openTask($event, task) {
      openComponent($event, task, 'task');
    }

    function eventHash(data) {
      var hash = 0, i, chr, json;
      json = JSON.stringify({
        type: data.type,
        status: data.status,
        selected: data.selected,
        repeat: data.repeat,
        pid: data.pid,
        destinationCalendar: data.destinationCalendar,
        delta: data.delta,
        classification: data.classification,
        isNew: data.isNew,
        categories: data.categories,
        alarm: data.alarm,
        type: data.type,
        summary: data.summary,
        status: data.status,
        organizer: data.organizer,
        location: data.location,
        isAllDay: data.isAllDay,
        comment: data.comment,
        attendees: data.attendees
      });

      if (json.length === 0) return hash;
      for (i = 0; i < json.length; i++) {
        chr = json.charCodeAt(i);
        hash = ((hash << 5) - hash) + chr;
        hash |= 0;
      }

      return hash;
    }

    function openComponent($event, component, type) {
      if (component.viewable) {
        var promise = $q.when();

        // Load component before opening dialog
        if (angular.isUndefined(component.$futureComponentData)) {
          component = Calendar.$get(component.pid).$getComponent(component.id, component.occurrenceId);
          promise = component.$futureComponentData;
        }
        var originalDataHash = eventHash(component);

        promise.then(function() {
          // UI/Templates/SchedulerUI/UIxAppointmentViewTemplate.wox or
          // UI/Templates/SchedulerUI/UIxTaskViewTemplate.wox
          var templateUrl = 'UIx' + type.capitalize() + 'ViewTemplate';

          if (vm.isComponentOpened) { // Prevent opening a new modal if there is already one opened (multiple clicks)
            return;
          }

          // TODO: Improve Angular implementation
          var originalCancel = $mdDialog.cancel;
          var originalDestroy = $mdDialog.destroy;
          var originalShow = $mdDialog.show;
          

          $mdDialog.show = function(p) {
            vm.isComponentOpened = true;

            $mdDialog.cancel = function () {
              var newDataHash = eventHash(component);
              vm.isComponentOpened = false;

              if (originalDataHash === newDataHash) {
                $mdDialog.cancel = originalCancel;
                return originalCancel();
              } else if (confirm(l('You have modified data unsaved. Do you want to close popup and loose data ?'))) {
                $mdDialog.cancel = originalCancel;
                return originalCancel();
              }
            };

            $mdDialog.destroy = function () {
              vm.isComponentOpened = false;
              $mdDialog.cancel = originalCancel;
              $mdDialog.destroy = originalDestroy;
              return originalDestroy();
            };

            return originalShow(p);
          };
          
          $mdDialog.show({
            parent: angular.element(document.body),
            targetEvent: $event,
            clickOutsideToClose: true,
            escapeToClose: true,
            templateUrl: templateUrl,
            controller: 'ComponentController',
            controllerAs: 'editor',
            locals: {
              stateComponent: component
            }
          });
        });
      }
    }

    function newComponent($event, type, baseComponent) {
      var component;

      if (baseComponent) {
        component = baseComponent;
        component.initAttendees();
        component.$attendees.updateFreeBusy();
      }
      else {
        component = new Component({ pid: Calendar.$defaultCalendar(), type: type });
      }

      // UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox or
      // UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox
      var templateUrl = 'UIx' + type.capitalize() + 'EditorTemplate';

      if (vm.isComponentOpened) { // Prevent opening a new modal if there is already one opened (multiple clicks)
        return;
      }

      // TODO: Improve Angular implementation
      var originalCancel = $mdDialog.cancel;
      var originalDestroy = $mdDialog.destroy;
      var originalDataHash = eventHash(component);
      vm.isComponentOpened = true;

      $mdDialog.cancel = function() {
        var newDataHash = eventHash(component);
        vm.isComponentOpened = false;

        if (originalDataHash === newDataHash) {
          $mdDialog.cancel = originalCancel;
          return originalCancel();
        } else if (confirm(l('You have modified data unsaved. Do you want to close popup and loose data ?'))) {
          $mdDialog.cancel = originalCancel;
          return originalCancel();
        }
      };

      $mdDialog.destroy = function () {
        vm.isComponentOpened = false;
        $mdDialog.cancel = originalCancel;
        $mdDialog.destroy = originalDestroy;
        return originalDestroy();
      };

      return $mdDialog.show({
        parent: angular.element(document.body),
        targetEvent: $event,
        clickOutsideToClose: true,
        escapeToClose: true,
        templateUrl: templateUrl,
        controller: 'ComponentEditorController',
        controllerAs: 'editor',
        locals: {
          stateComponent: component
        }
      });
    }

    // Adjust component or create new component through drag'n'drop
    function updateComponentFromGhost($event) {
      var component, pointerHandler, originalCoordinates, coordinates, delta, params, calendarNumber, activeCalendars;

      component = Component.$ghost.component;
      pointerHandler = Component.$ghost.pointerHandler;

      if (component.isNew) {
        originalCoordinates = pointerHandler.originalEventCoordinates;
        coordinates = pointerHandler.currentEventCoordinates;
        component.summary = '';
        if (component.isAllDay)
          coordinates.duration -= 96;
        if (coordinates.start < originalCoordinates.start)
          coordinates.duration *= -1;
        component.setDelta(coordinates.duration * 15);
        newComponent(null, 'appointment', component)
          .catch()
          .finally(function() {
            $timeout(function() {
              Component.$resetGhost();
            });
          });
      }
      else {
        delta = pointerHandler.currentEventCoordinates.getDelta(pointerHandler.originalEventCoordinates);
        params = {
          days: delta.dayNumber,
          start: delta.start * 15,
          duration: delta.duration * 15
        };
        if (pointerHandler.originalCalendar && delta.dayNumber !== 0) {
          // The day number actually represents the destination calendar among the active calendars
          calendarNumber = pointerHandler.currentEventCoordinates.dayNumber;
          activeCalendars = _.filter(Calendar.$findAll(), { active: 1 });
          params.destination = activeCalendars[calendarNumber].id;
          params.days = 0;
        }
        if (component.isException || !component.occurrenceId)
          // Component is an exception to a recurrence or is not recurrent;
          // Immediately perform the adjustments
          component.$adjust(params).then(function() {
            $rootScope.$emit('calendars:list');
            Preferences.getAlarms();
          }, function(response) {
            onComponentAdjustError(response, component, params);
          }).finally(function() {
            $timeout(function() {
              Component.$resetGhost();
            });
          });
        else if (component.occurrenceId) {
          $mdDialog.show({
            clickOutsideToClose: true,
            escapeToClose: true,
            locals: {
              component: component,
              params: params
            },
            template: [
              '<md-dialog flex="50" sm-flex="80" xs-flex="90">',
              '  <md-dialog-content class="md-dialog-content">',
              '    <p>' + l('editRepeatingItem') + '</p>',
              '  </md-dialog-content>',
              '  <md-dialog-actions>',
              '    <md-button ng-click="updateThisOccurrence()">' + l('button_thisOccurrenceOnly') + '</md-button>',
              '    <md-button ng-click="updateAllOccurrences()">' + l('button_allOccurrences') + '</md-button>',
              '  </md-dialog-actions>',
              '</md-dialog>'
            ].join(''),
            controller: RecurrentComponentDialogController
          }).then(function() {
            $rootScope.$emit('calendars:list');
          }, function() {
            // Cancel
          }).finally(function() {
            $timeout(function() {
              Component.$resetGhost();
            });
          });
        }
      }

      /**
       * @ngInject
       */
      RecurrentComponentDialogController.$inject = ['$scope', '$mdDialog', 'component', 'params'];
      function RecurrentComponentDialogController($scope, $mdDialog, component, params) {
        $scope.updateThisOccurrence = function() {
          component.$adjust(params).then($mdDialog.hide, function(response) {
            $mdDialog.cancel().then(function() {
              onComponentAdjustError(response, component, params);
            }, function() {
              // Cancel
            });
          });
        };
        $scope.updateAllOccurrences = function() {
          delete component.occurrenceId;
          component.$adjust(params).then($mdDialog.hide, function(response) {
            $mdDialog.cancel().then(function() {
              onComponentAdjustError(response, component, params);
            }, function() {
              // Cancel
            });
          });
        };
      }

      function onComponentAdjustError(response, component, params) {
        if (response.status == CalendarSettings.ConflictHTTPErrorCode &&
            response.data && response.data.message && angular.isObject(response.data.message)) {
          $mdDialog.show({
            parent: angular.element(document.body),
            clickOutsideToClose: false,
            escapeToClose: false,
            templateUrl: 'UIxAttendeeConflictDialog',
            controller: AttendeeConflictDialogController,
            controllerAs: '$AttendeeConflictDialogController',
            locals: {
              component: component,
              params: params,
              conflictError: response.data.message
            }
          }).then(function() {
            $rootScope.$emit('calendars:list');
          }, function() {
            // Cancel
          });
        }
      }

      /**
       * @ngInject
       */
      AttendeeConflictDialogController.$inject = ['$scope', '$mdDialog', 'component', 'params', 'conflictError'];
      function AttendeeConflictDialogController($scope, $mdDialog, component, params, conflictError) {
        var vm = this;

        vm.conflictError = conflictError;
        vm.cancel = $mdDialog.cancel;
        vm.save = save;

        function save() {
          component.$adjust(angular.extend({ ignoreConflicts: true }, params)).then($mdDialog.hide);
        }
      }
    }

    function filter(filterpopup) {
      if (filterpopup) {
        Component.$filter(vm.componentType, { filterpopup: filterpopup });
      }
      else {
        return Component['$query' + vm.componentType.capitalize()].filterpopup;
      }
    }

    function filteredBy(filterpopup) {
      return Component['$query' + vm.componentType.capitalize()].filterpopup == filterpopup;
    }

    function sort(field) {
      if (field) {
        Component.$filter(vm.componentType, { sort: field });
      }
      else {
        var sort = Component['$query' + vm.componentType.capitalize()].sort;
        return sortLabels[sort] || sortLabels[vm.componentType][sort];
      }
    }

    function sortedBy(field) {
      return Component['$query' + vm.componentType.capitalize()].sort == field;
    }

    this.ascending = function() {
      return Component['$query' + vm.componentType.capitalize()].asc;
    };

    function reload() {
      Component.$loaded = Component.STATUS.LOADING; // Show progress indicator
      Calendar.reloadWebCalendars().finally(function() {
        $rootScope.$emit('calendars:list');
      });
    }

    function cancelSearch() {
      vm.mode.search = false;
      Component.$filter(vm.componentType, { value: '' });
    }
  }

  angular
    .module('SOGo.SchedulerUI')
    .controller('CalendarListController', CalendarListController);
})();
