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

(function() {
  'use strict';

  /**
   * @ngInject
   */
  MessageController.$inject = ['$window', '$scope', '$q', '$state', '$mdMedia', '$mdDialog', '$mdPanel', 'sgConstant', 'stateAccounts', 'stateAccount', 'stateMailbox', 'stateMessage', 'sgHotkeys', 'encodeUriFilter', 'sgSettings', 'ImageGallery', 'sgFocus', 'Dialog', 'Preferences', 'Calendar', 'Component', 'Account', 'Mailbox', 'Message', 'AddressBook', 'Card'];
  function MessageController($window, $scope, $q, $state, $mdMedia, $mdDialog, $mdPanel, sgConstant, stateAccounts, stateAccount, stateMailbox, stateMessage, sgHotkeys, encodeUriFilter, sgSettings, ImageGallery, focus, Dialog, Preferences, Calendar, Component, Account, Mailbox, Message, AddressBook, Card) {
    var vm = this, popupWindow = null, hotkeys = [];

    this.$onInit = function() {
      var isPopupWindow = false;

      // Expose controller
      $window.$messageController = vm;

      // Initialize image gallery service
      ImageGallery.setMessage(stateMessage);

      this.$state = $state;
      this.accounts = stateAccounts;
      this.account = stateAccount;
      this.mailbox = stateMailbox;
      this.message = stateMessage;
      this.service = Message;
      this.tags = { searchText: '', selected: '' };
      this.showFlags = stateMessage.flags && stateMessage.flags.length > 0;
      this.$alwaysShowDetailedRecipients = (!stateMessage.to || stateMessage.to.length < 5) && (!stateMessage.cc || stateMessage.cc.length < 5);
      this.$showDetailedRecipients = this.$alwaysShowDetailedRecipients;
      this.showRawSource = false;

      _registerHotkeys(hotkeys);

      // Detect if this is message appears in a separate window
      try {
        isPopupWindow = $window.opener && '$mailboxController' in $window.opener;
      }
      catch (e) {}

      // One-way refresh of the parent window when modifying the message from a popup window.
      if (isPopupWindow) {
        // Update the message flags. The message must be displayed in the parent window.
        $scope.$watchCollection(function() { return vm.message.flags; }, function(newTags, oldTags) {
          var ctrls;
          if (newTags || oldTags) {
            ctrls = $parentControllers();
            if (ctrls.messageCtrl) {
              ctrls.messageCtrl.service.$timeout(function() {
                ctrls.messageCtrl.showFlags = true;
                ctrls.messageCtrl.message.flags = newTags;
              });
            }
          }
        });
        // Update the "isflagged" (star icon) of the message. The mailbox must be displayed in the parent window.
        $scope.$watch(function() { return vm.message.isflagged; }, function(isflagged, wasflagged) {
          var ctrls = $parentControllers();
          if (ctrls.mailboxCtrl) {
            ctrls.mailboxCtrl.service.$timeout(function() {
              var message = _.find(ctrls.mailboxCtrl.selectedFolder.$messages, { uid: vm.message.uid });
              message.isflagged = isflagged;
            });
          }
        });
      }
      else {
        // Flatten new tags when coming from the predefined list of tags (Message.$tags) and
        // sync tags with server when adding or removing a tag.
        $scope.$watchCollection(function() { return vm.message.flags; }, function(_newTags, _oldTags) {
          var newTags, oldTags, tags;
          if (_newTags || _oldTags) {
            newTags = _newTags || [];
            oldTags = _oldTags || [];
            _.forEach(newTags, function(tag, i) {
              if (angular.isObject(tag))
                newTags[i] = tag.name;
            });
            if (newTags.length > oldTags.length) {
              tags = _.difference(newTags, oldTags);
              _.forEach(tags, function(tag) {
                vm.message.addTag(tag);
              });
            }
            else if (newTags.length < oldTags.length) {
              tags = _.difference(oldTags, newTags);
              _.forEach(tags, function(tag) {
                vm.message.removeTag(tag);
              });
            }
          }
        });
      }

      $scope.$on('$destroy', function() {
        // Deregister hotkeys
        _.forEach(hotkeys, function(key) {
          sgHotkeys.deregisterHotkey(key);
        });
        // Cancel automatic mark as read
        if (vm.message.$markAsReadPromise)
          vm.service.$timeout.cancel(vm.message.$markAsReadPromise);
        // Remove controller from window
        delete $window.$messageController;
      });

    }; // $onInit


    /**
     * To keep track of the currently active dialog, we share a common variable with the parent controller.
     */
    function _messageDialog() {
      if ($scope.mailbox) {
        if (arguments.length > 0)
          $scope.mailbox.messageDialog = arguments[0];
        return $scope.mailbox.messageDialog;
      }
      return null;
    }

    function _unlessInDialog(callback) {
      return function() {
        // Check if a dialog is opened either from the current controller or the parent controller
        if (_messageDialog() === null)
          return callback.apply(vm, arguments);
      };
    }

    function _registerHotkeys(keys) {
      keys.push(sgHotkeys.createHotkey({
        key: l('hotkey_reply'),
        description: l('Reply to the message'),
        callback: _unlessInDialog(angular.bind(vm, vm.reply))
      }));
      keys.push(sgHotkeys.createHotkey({
        key: l('hotkey_replyall'),
        description: l('Reply to sender and all recipients'),
        callback: _unlessInDialog(angular.bind(vm, vm.replyAll))
      }));
      keys.push(sgHotkeys.createHotkey({
        key: l('hotkey_forward'),
        description: l('Forward selected message'),
        callback: _unlessInDialog(angular.bind(vm, vm.forward))
      }));
      keys.push(sgHotkeys.createHotkey({
        key: l('hotkey_flag'),
        description: l('Flagged'),
        callback: _unlessInDialog(angular.bind(stateMessage, stateMessage.toggleFlag))
      }));
      _.forEach(['backspace', 'delete'], function(hotkey) {
        keys.push(sgHotkeys.createHotkey({
          key: hotkey,
          callback: _unlessInDialog(function($event) {
            if (vm.mailbox.selectedCount() === 0)
              vm.deleteMessage();
            $event.preventDefault();
          }),
        }));
      });

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

    /**
     * If this is a popup window, retrieve the matching controllers (mailbox and message) of the parent window.
     */
    function $parentControllers() {
      var message, mailbox, ctrls = {};
      if ($window.opener) {
        // Deleting the message from a popup window
        if ('$mailboxController' in $window.opener &&
            'selectedFolder' in $window.opener.$mailboxController &&
            $window.opener.$mailboxController.selectedFolder.$id() == stateMailbox.$id()) {
            // The message mailbox is opened in the parent window
            mailbox = $window.opener.$mailboxController;
            ctrls.mailboxCtrl = mailbox;
            if ('$messageController' in $window.opener &&
                $window.opener.$messageController.message.uid == stateMessage.uid) {
              // The message is opened in the parent window
              message = $window.opener.$messageController;
              ctrls.messageCtrl = message;
            }
        }
      }
      return ctrls;
    }

    this.addFlags = function($event) {
      $event.stopPropagation();
      $event.preventDefault();
      this.showFlags = true;
      focus("flags");
    };

    this.toggleDetailedRecipients = function($event) {
      this.$showDetailedRecipients = !this.$showDetailedRecipients;
      $event.stopPropagation();
      $event.preventDefault();
    };

    this.focusChip = function($event) {
      var chipElement = $event.target;
      while (chipElement.tagName !== 'MD-CHIP') {
        chipElement = chipElement.parentNode;
      }
      chipElement.classList.add('md-focused');
    };

    this.blurChip = function($event) {
      var chipElement = $event.target;
      while (chipElement.tagName !== 'MD-CHIP') {
        chipElement = chipElement.parentNode;
      }
      chipElement.classList.remove('md-focused');
      if ($event.relatedTarget && $event.relatedTarget.tagName === 'MD-CHIP-TEMPLATE') {
        // Moving to another chip; close menu
        vm.panel.close();
      }
    };

    this.selectRecipient = function(recipient, $event) {
      // Fetch addressbooks list
      AddressBook.$findAll([]);

      var targetElement = $event.target;

      var panelPosition = $mdPanel.newPanelPosition()
          .relativeTo(targetElement)
          .addPanelPosition(
            $mdPanel.xPosition.ALIGN_START,
            $mdPanel.yPosition.ALIGN_TOPS
          );

      var panelAnimation = $mdPanel.newPanelAnimation()
          .openFrom(targetElement)
          .duration(100)
          .withAnimation($mdPanel.animation.FADE);

      var config = {
        attachTo: angular.element(document.body),
        locals: {
          recipient: recipient,
          addressbooks: AddressBook.$addressbooks,
          subscriptions: AddressBook.$subscriptions,
          newMessage: angular.bind(this, this.newMessage)
        },
        bindToController: true,
        controller: MenuController,
        controllerAs: '$menuCtrl',
        position: panelPosition,
        animation: panelAnimation,
        targetEvent: $event,
        templateUrl: 'UIxMailViewRecipientMenu',
        trapFocus: true,
        clickOutsideToClose: true,
        escapeToClose: true,
        focusOnOpen: false
      };

      $mdPanel.open(config)
        .then(function(panelRef) {
          vm.panel = panelRef;
          // Automatically close panel when clicking inside of it
          panelRef.panelEl.one('click', function() {
            panelRef.close();
          });
        });

      MenuController.$inject = ['mdPanelRef', '$state', '$mdToast'];
      function MenuController(mdPanelRef, $state, $mdToast) {
        this.onKeyDown = function($event) {
          if ($event.which === 9) { // Tab
            mdPanelRef.close();
          }
        };

        this.newCard = function(recipient, addressbookId) {
          var card = new Card({
            pid: addressbookId,
            c_cn: recipient.name,
            emails: [{ value: recipient.email }]
          });
          card.$id().then(function(id) {
            card.$save().then(function() {
              // Show success toast when action succeeds
              $mdToast.show(
                $mdToast.simple()
                  .textContent(l('Successfully created card'))
                  .position(sgConstant.toastPosition)
                  .hideDelay(2000));
            });
          });
          mdPanelRef.close();
        };
      }

      if (targetElement.tagName === 'A') {
        $event.stopPropagation();
        $event.preventDefault();
      }
    };

    this.filterMailtoLinks = function($event) {
      var href, match, to, cc, bcc, subject, body, data;
      if ($event.target.tagName == 'A' && 'href' in $event.target.attributes) {
        href = $event.target.attributes.href.value;
        match = /^mailto:([^\?]+)/.exec(href);
        if (match) {
          delete $event.target.attributes.target;
          this.newMessage($event, href); // will stop event propagation
        }
      }
    };

    this.deleteMessage = function() {
      var mailbox, message, state, nextMessage, previousMessage,
          parentCtrls = $parentControllers(),
          $timeout = this.service.$timeout;

      if (parentCtrls.messageCtrl) {
        mailbox = parentCtrls.mailboxCtrl.selectedFolder;
        message = parentCtrls.messageCtrl.message;
        state = parentCtrls.messageCtrl.$state;
      }
      else {
        mailbox = stateMailbox;
        message = stateMessage;
        state = $state;
      }
      if (Mailbox.$virtualMode) {
        mailbox = Mailbox.selectedFolder; // the VirtualMailbox instance
      }

      function _success(index) {
        var nextIndex = index;
        // Remove message object from scope
        message = null;
        if (angular.isDefined(state)) {
          // Select either the next or previous message
          if (index > 0) {
            nextIndex -= 1;
            nextMessage = mailbox.getItemAtIndex(nextIndex);
          }
          if (index < mailbox.getLength())
            previousMessage = mailbox.getItemAtIndex(index);

          if (nextMessage) {
            if (nextMessage.isread && previousMessage && !previousMessage.isread) {
              nextIndex = index;
              nextMessage = previousMessage;
            }
          }
          else if (previousMessage) {
            nextIndex = index;
            nextMessage = previousMessage;
          }

          try {
            if (nextMessage && $mdMedia(sgConstant['gt-md'])) {
              if (Mailbox.$virtualMode)
                state.go('mail.account.virtualMailbox.message', {mailboxId: encodeUriFilter(nextMessage.$mailbox.path), messageId: nextMessage.uid});
              else
                state.go('mail.account.mailbox.message', {messageId: nextMessage.uid});
              $timeout(function() {
                if (nextIndex < mailbox.$topIndex)
                  mailbox.$topIndex = nextIndex;
                else if (nextIndex > mailbox.$lastVisibleIndex)
                  mailbox.$topIndex = nextIndex - (mailbox.$lastVisibleIndex - mailbox.$topIndex);
              });
            }
            else {
              state.go('mail.account.mailbox').then(function() {
                message = null;
                delete mailbox.$selectedMessage;
              });
            }
          }
          catch (error) {}
        }
        vm.closePopup();
      }

      mailbox.$deleteMessages([message]).then(_success, function(response) {
        _messageDialog(
            Dialog.confirm(l('Warning'),
                           l('The message could not be moved to the trash folder. Would you like to delete it immediately?'),
                           { ok: l('Delete') })
            .then(function() {
              mailbox.$deleteMessages([message], { withoutTrash: true })
                .then(_success)
                .finally(function() {
                  _messageDialog(null);
                });
            })
            .finally(function() {
              _messageDialog(null);
            })
        );
      });
    };

    function _showMailEditor($event, message) {
      if (_messageDialog() === null) {
        var onCompleteDeferred = $q.defer();
        _messageDialog(
          $mdDialog
            .show({
              parent: angular.element(document.body),
              targetEvent: $event,
              clickOutsideToClose: false,
              escapeToClose: false,
              templateUrl: 'UIxMailEditor',
              controller: 'MessageEditorController',
              controllerAs: 'editor',
              onComplete: function (scope, element) {
                return onCompleteDeferred.resolve(element);
              },
              locals: {
                stateParent: $scope,
                stateAccount: vm.account,
                stateMessage: message,
                onCompletePromise: function () {
                  return onCompleteDeferred.promise;
                }
              }
            })
            .catch(_.noop) // Cancel
            .finally(function() {
              _messageDialog(null);
              vm.closePopup();
            })
        );
      }
    }

    this._showMailEditorInPopup = function(action) {
      if (!sgSettings.isPopup &&
          Preferences.defaults.SOGoMailComposeWindow == 'popup') {
        this.openInPopup(action);
        return true;
      }
      return false;
    };

    this.close = function() {
      var destination = Mailbox.$virtualMode ? 'mail.account.virtualMailbox' : 'mail.account.mailbox';
      $state.go(destination).then(function() {
        vm.message = null;
        delete stateMailbox.$selectedMessage;
      });
    };

    this.reply = function($event) {
      if (!this._showMailEditorInPopup('reply')) {
        _showMailEditor($event, this.message.$reply());
      }
    };

    this.replyAll = function($event) {
      if (!this._showMailEditorInPopup('replyall')) {
        _showMailEditor($event, this.message.$replyAll());
      }
    };

    this.forward = function($event) {
      if (!this._showMailEditorInPopup('forward')) {
        _showMailEditor($event, this.message.$forward());
      }
    };

    this.edit = function($event) {
      if (!this._showMailEditorInPopup('edit')) {
        this.message.$editableContent().then(function() {
          _showMailEditor($event, vm.message);
        });
      }
    };

    this.compose = function($event) {
      if (!this._showMailEditorInPopup('compose')) {
        _showMailEditor($event, this.message.$compose());
      }
    };

    this.openInPopup = function(action) {
      var url = [sgSettings.baseURL(),
                 'UIxMailPopupView#!/Mail',
                 this.message.accountId,
                 // The double-encoding is necessary
                 encodeUriFilter(encodeUriFilter(this.message.$mailbox.path)),
                 this.message.uid]
          .join('/'),
          wId = this.message.$absolutePath();
      if (action) {
        wId += '/' + action;
        url += '/' + action;
      }
      popupWindow = $window.open(url, wId,
                                 ["width=680",
                                  "height=520",
                                  "resizable=1",
                                  "scrollbars=1",
                                  "toolbar=0",
                                  "location=0",
                                  "directories=0",
                                  "status=0",
                                  "menubar=0",
                                  "copyhistory=0"]
                                 .join(','));
    };

    this.closePopup = function() {
      if ($window.document.body.classList.contains('popup'))
        $window.close();
    };

    this.newMessage = function($event, mailto) {
      if ($event.target.tagName === 'A') {
        $event.stopPropagation();
        $event.preventDefault();
      }
      this.account.$newMessage({ mailto: mailto }).then(function(message) {
        _showMailEditor($event, message);
      });
    };

    this.toggleRawSource = function($event) {
      if (!this.showRawSource && !this.message.$rawSource) {
        Message.$$resource.post(this.message.id, "viewsource").then(function(data) {
          vm.message.$rawSource = data;
          vm.showRawSource = true;
        });
      }
      else {
        this.showRawSource = !this.showRawSource;
      }
    };

    this.print = function($event) {
      $window.print();
    };

    this.convertToEvent = function($event) {
      return _convertToComponent($event, 'appointment');
    };

    this.convertToTask = function($event) {
      return _convertToComponent($event, 'task');
    };

    function _convertToComponent($event, type) {
      vm.message.$plainContent().then(function(data) {
        var componentData = {
          pid: Calendar.$defaultCalendar(),
          type: type,
          summary: data.subject,
          comment: data.content
        };
        var component = new Component(componentData);
        // UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox or
        // UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox
        var templateUrl = [
          sgSettings.activeUser('folderURL'),
          'Calendar',
          'UIx' + type.capitalize() + 'EditorTemplate'
        ].join('/');
        return $mdDialog.show({
          parent: angular.element(document.body),
          targetEvent: $event,
          clickOutsideToClose: true,
          escapeToClose: true,
          templateUrl: templateUrl,
          controller: 'ComponentEditorController',
          controllerAs: 'editor',
          locals: {
            stateComponent: component
          }
        });
      });
    }
  }

  angular
    .module('SOGo.MailerUI')
    .controller('MessageController', MessageController);
})();
