var SmartAnnotation = (function() {
  'use strict';

  // utilities
  var Util = (function() {
    // helper method that binds show/hidden action
    function showHideBinding() {
      $.each(['show', 'hide'], function (i, ev) {
        var el = $.fn[ev];
        $.fn[ev] = function () {
          this.trigger(ev);
          return el.apply(this, arguments);
        };
      });
    }

    var publicApi = {
      showHideBinding: showHideBinding
    };

    return publicApi;
  })();

  // stop the user annotation popover on click propagation
  function atwhoStopPropagation(element) {
    $(element).on('click', function(e) {
      e.stopPropagation();
      e.preventDefault();
    });
  }

  function setAtWho(field) {
    var FilterTypeEnum = Object.freeze({
      USER:       {tag: "users",
                   dataUrl: $(document.body).attr('data-atwho-users-url')},
      TASK:       {tag: "tsk",
                   dataUrl: $(document.body).attr('data-atwho-task-url')},
      PROJECT:    {tag: "prj",
                   dataUrl: $(document.body).attr('data-atwho-project-url')},
      EXPERIMENT: {tag: "exp",
                   dataUrl: $(document.body).attr('data-atwho-experiment-url')},
      REPOSITORY: {tag: "rep",
                   dataUrl: $(document.body).attr('data-atwho-rep-items-url')},
      MENU:       {tag: "menu",
                   dataUrl: $(document.body).attr('data-atwho-menu-items')}
    });
    var prevAt,
        // Default selected filter when using '#'
        DEFAULT_SEARCH_FILTER = FilterTypeEnum.REPOSITORY,
        atWhoUpdating = false;

    var defaultRepId;

    // helper methods for AtWho callback
    function _templateEval(_tpl, map) {
      var res;
      try {
        if (map.no_results) {
          res = noResultsTemplate();
        } else {
          res = generateTemplate(map);
        }
      } catch (_error) {
        res = '';
      }
      return res;
    }

    function _matchHighlighter(li, query, filterType) {
      var $li, re;

      function highlight(el, sel, re) {
        var prevVal, newVal;
        prevVal = el.find(sel).html();
        newVal = prevVal.replace(re, '<strong>$&</strong>');
        el.find(sel).html(newVal);
      }

      if (!query || $(li).data('no-results')) {
        return li;
      }

      $li = $(li);
      re = new RegExp(query, 'gi');
      // search_filter is not passed for the user
      if(filterType) {
        highlight($li, '[data-val=name]', re);
      } else {
        highlight($li, '[data-val=full-name]', re);
        highlight($li, '[data-val=email]', re);
      }

      return $li[0].outerHTML
    }

    function _generateInputTag(value, li) {
      var res = '';
      res += '[#' + li.attr('data-name');
      res += '~' + li.attr('data-type');
      res += '~' + li.attr('data-id') + ']';
      return res;
    }

    // initialise dropdown dismiss button
    function initDismissButton($currentAtWho) {
      $currentAtWho.find('.dismiss').off('click')
                                    .on('click', function() {
        $(field).atwho('destroy');
        init();
      });
    }

    // Initialize or update dropdown header buttons
    function updateHeaderButtons(query, filterType) {
      var $currentAtWho = $('.atwho-view[style]');
      initDismissButton($currentAtWho);

      // Update the selected filter button when changing smart annotation type
      $currentAtWho.find('[data-filter]')
                   .removeClass('btn-primary')
                   .addClass('btn-default');
      if(filterType.tag === 'rep') {
        $currentAtWho.find('[data-rep-id="' + filterType.repository_id + '"]')
                     .removeClass('btn-default')
                     .addClass('btn-primary');
      } else {
        $currentAtWho.find('[data-filter="' + filterType.tag + '"]')
                     .removeClass('btn-default')
                     .addClass('btn-primary');
      }

      // Update the selected filter button when clicking on one of them
      $currentAtWho.find('[data-filter]').off()
                                         .on('click', function(e) {
        if($(this).hasClass('btn-primary')) {
          return;
        }
        var $selectedBtn = $(this);
        var $prevBtn = $selectedBtn.closest('.atwho-header-res')
                                   .children('.btn-primary');
        $selectedBtn.removeClass('btn-default').addClass('btn-primary');
        $prevBtn.removeClass('btn-primary').addClass('btn-default');

        // Updates query and dropdown elements; focuses input
        $(field).click().focus();
      });
    }

    // Generates suggestion dropdown filter
    function generateFilterMenu(active, res_data) {
      var rep_buttons = '';
      $.ajax({
        async: false,
        dataType: 'json',
        url: $(document.body).attr('data-atwho-repositories-url'),
        success: function(data) {
          $.each(data['repositories'], function(id, name) {
            if(defaultRepId === undefined){
              defaultRepId = id;
            }
            rep_buttons += '<button data-filter="rep" data-rep-id="' + id +
              '" class="btn btn-xs btn-primary">' + name + '</button>';
          });
       }
      });
      var header = '<div class="atwho-header-res">' +
       '<button data-filter="prj" class="btn btn-xs btn-primary' +
       '">Projects</button>' +
       '<button data-filter="exp" class="btn btn-xs btn-primary' +
       '">Experiments</button>' +
       '<button data-filter="tsk" class="btn btn-xs btn-primary' +
       '">Tasks</button>' +
       rep_buttons +
       '<div class="dismiss">' +
       '<span class="fas fa-times"></span>' +
       '</div>' +
       '<div class="help">' +
       '<div>' +
       '<strong><%= I18n.t("atwho.users.navigate_1") %></strong> ' +
       '<%= I18n.t("atwho.users.navigate_2") %>' +
       '</div>' +
       '<div><strong><%= I18n.t("atwho.users.confirm_1") %></strong> ' +
       '<%= I18n.t("atwho.users.confirm_2") %>' +
       '</div>' +
       '<div>' +
       '<strong><%= I18n.t("atwho.users.dismiss_1") %></strong> ' +
       '<%= I18n.t("atwho.users.dismiss_2") %>' +
       '</div>' +
       '</div>' +
       '</div>';

      return header;
    }

    function noResultsTemplate() {
      var res = '<div class="atwho-no-results" data-no-results="1">';
      res += '<span><%= I18n.t("atwho.no_results") %></span>';
      res += '</div>';
      return res;
    }

    // Generates resources list items
    function generateTemplate(map) {
      var res = '';
      res += '<li class="atwho-li atwho-li-res" data-name="' +
              truncateLongString(map.name,
                <%= Constants::NAME_TRUNCATION_LENGTH %>) +
              '" data-id="' + map.id + '" data-type="' +
              map.type + '">';
      switch(map.type) {
          case 'tsk':
              res += '<span data-type class="res-type">' + map.type + '</span>';
              break;
          case 'prj':
              res += '<span data-type class="res-type">' + map.type + '</span>';
              break;
          case 'exp':
              res += '<span data-type class="res-type">' + map.type + '</span>';
              break;
          case 'rep_item':
              res += '<span data-type class="res-type">' +
                     map.repository_tag + '</span>';
              break;
      }

      res += '&nbsp;';
      res += '<span data-val="name" class="res-name">';
      res += truncateLongString(map.name,
        <%= Constants::NAME_TRUNCATION_LENGTH %>);
      res += '</span>';
      if(map.archived) {
        res += '<%= I18n.t("atwho.res.archived") %></span>';
      } else {
        res += '</span>';
      }
      res += '&nbsp;';

      switch (map.type) {
        case 'tsk':

          res += '<span class="res-description">&lt; ' + map.experimentName +
                 ' &lt; ' + map.projectName + '</span>';
          break;
        case 'exp':
          res += '<span class="res-description">&lt; ' + map.projectName + '</span>';
          break;
        case 'sam':
          res += '<span class="res-description">' + map.description + '</span>';
          break;
      }

      res += '</li>';
      return res;
    }

    /**
     * Hackish wrapper function to make AtWho work when switching between
     * multiple AtWho instances (e.g. from # to task#).
     *
     * Prevents second execution of AtWho update callback, triggered when user
     * switches to different AtWho instance (e.g. from # to task#), which causes
     * both of them to be called. In such case, AtWhO modal needs to be
     * rerendered.
     */
    function atWhoSwitchHack(filterTypeTag, remoteFilterCb) {
      if(atWhoUpdating || (!$(field).length && _.isUndefined(filterTypeTag))) {
        setTimeout(function() {
          $(field).click();
        }, 100);
        return;
      }

      atWhoUpdating = true;
      setTimeout(function() {
        remoteFilterCb();
        atWhoUpdating = false;
      }, 100);
    }

    function atWhoSettings(at, defaultFilterType) {
      return {
        at: at,
        callbacks: {
          remoteFilter: function(query, callback) {
            var $currentAtWho = $('.atwho-view[style]');
            var filterTypeTag = $currentAtWho
                                 .find('.btn-primary')
                                 .data('filter');

            atWhoSwitchHack(filterTypeTag, function() {
              var filterType;
              if (_.isUndefined(filterTypeTag)) {
                // Switched smart annotation type (i.e. changed input)
                filterType = defaultFilterType;
              } else {
                // Switched filtering type (i.e. different filter button
                // pressed; works also for specific annotation types, e.g.
                // task#, and coverts to the correct annotation type on confirm)
                $.each(FilterTypeEnum, function(k, v) {
                  if (v.tag == filterTypeTag) {
                    filterType = FilterTypeEnum[k];
                    return false;
                  }
                });
              }
              if (prevAt != at) {
                // Switching smart annotation type (i.e. chaned input)

                prevAt = at;
                filterType = defaultFilterType;
                // Hide current AtWho
                $currentAtWho.removeAttr("style");
              }

              var params = { query: query };

              if(filterType.tag === 'rep') {
                params.repository_id = $currentAtWho
                                       .find('.btn-primary')
                                       .data('rep-id');
                if(params.repository_id === undefined) {
                  params.repository_id = defaultRepId;
                }
                filterType.repository_id = params.repository_id;
              }

              $.getJSON(
                filterType.dataUrl,
                params,
                function(data) {
                  // Updates dropdown
                  if (data.res.length < 1) {
                    callback([{no_results: 1}]);
                  } else {
                    callback(data.res);
                  }

                  updateHeaderButtons(query, filterType);
                }
              );
            });
          },
          sorter: function(query, items, _searchKey) {
            // Sorting is already done on server-side
            return items;
          },
          tplEval: function(_tpl, map) {
            return _templateEval(_tpl, map);
          },
          highlighter:  function(li, query) {
            return _matchHighlighter(li, query, true);
          },
          beforeInsert: function(value, li) {
            return _generateInputTag(value, li);
          },
          matcher:function(flag, subtext, should_startWithSpace, acceptSpaceBar) {
            var _a, _y, match, regexp, space;
            flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
            if (should_startWithSpace) {
              flag = '(?:^|\\s)' + flag;
            }
            _a = decodeURI("%C3%80");
            _y = decodeURI("%C3%BF");
            regexp = new RegExp(flag + "([A-Za-z" + _a + "-" + _y + "0-9_\\s\+\-\]*)$|" + flag + "([^\\x00-\\xff]*)$", 'gi');
            match = regexp.exec(subtext);
            if (match) {
              return match[2] || match[1];
            } else {
              return null;
            }
          },
        },
        headerTpl: generateFilterMenu(defaultFilterType),
        limit: <%= Constants::ATWHO_SEARCH_LIMIT %>,
        startWithSpace: true,
        acceptSpaceBar: true,
        displayTimeout: 120000
      }
    }

    function init() {
      $(field)
      .atwho({
        at: '@',
        callbacks: {
          remoteFilter: function(query, callback) {
            $.getJSON(
              FilterTypeEnum.USER.dataUrl,
              {query: query},
              function(data) {
                if (data.users.length < 1) {
                  callback([{no_results: 1}]);
                } else {
                  callback(data.users);
                }
                initDismissButton($('.atwho-view[style]'));
              }
            );
          },
          sorter: function(query, items, _searchKey) {
            // Sorting is already done on server-side
            return items;
          },
          tplEval: function(_tpl, map) {
            var res;
            try {
              if (map.no_results) {
                res = noResultsTemplate();
              } else {
                res = '';
                res += '<li class="atwho-li atwho-li-user" ';
                res += 'data-id="' + map.id + '" ';
                res += 'data-full-name="' + map.full_name + '">';
                res += '<img src="' + map.img_url + '" class="avatar" />';
                res += '<span data-val="full-name">';
                res += map.full_name;
                res += '</span>';
                res += '<small>';
                res += '&nbsp;';
                res += '&#183;';
                res += '&nbsp;';
                res += '<span data-val="email">';
                res += map.email;
                res += '</span>';
                res += '</small>';
                res += '</li>';
              }
            } catch (_error) {
              res = '';
            }
            return res;
          },
          highlighter: function(li, query) {
            return _matchHighlighter(li, query);
          },
          beforeInsert: function(value, li) {
            var res = '';
            res += '[@' + li.attr('data-full-name');
            res += '~' + li.attr('data-id') + ']';
            return res;
          }
        },
        headerTpl:
          '<div class="atwho-header-res">' +
          '<div class="title-user"><%= I18n.t("atwho.users.title") %></div>' +
          '<div class="help">' +
          '<div>' +
          '<strong><%= I18n.t("atwho.users.navigate_1") %></strong> ' +
          '<%= I18n.t("atwho.users.navigate_2") %>' +
          '</div>' +
          '<div>' +
          '<strong><%= I18n.t("atwho.users.confirm_1") %></strong> ' +
          '<%= I18n.t("atwho.users.confirm_2") %>' +
          '</div>' +
          '<div>' +
          '<strong><%= I18n.t("atwho.users.dismiss_1") %></strong> ' +
          '<%= I18n.t("atwho.users.dismiss_2") %>' +
          '</div>' +
          '</div>' +
          '<div class="dismiss">' +
          '<span class="fas fa-times"></span>' +
          '</div>' +
          '</div>',
        limit: <%= Constants::ATWHO_SEARCH_LIMIT %>,
        startsWithSpace: true,
        acceptSpaceBar: true,
        displayTimeout: 120000
      })
      .atwho(atWhoSettings('#', DEFAULT_SEARCH_FILTER))
      // .atwho(atWhoSettings('task#', FilterTypeEnum.TASK))   Waiting for better times
      // .atwho(atWhoSettings('project#', FilterTypeEnum.PROJECT))
      // .atwho(atWhoSettings('experiment#', FilterTypeEnum.EXPERIMENT))
      // .atwho(atWhoSettings('sample#', FilterTypeEnum.REPOSITORY));
    }

    return {
      init: init
    };
  }
  // Closes the atwho popup * needed in repositories to close the popup
  // if nothing is selected and the user leaves the form *
  function closePopup() {
    $('.atwho-header-res').find('.fa-times').click();
  }

  function initialize(field) {
    var atWho = new setAtWho(field);
    atWho.init();
  }

  var publicApi = Object.freeze({
    init: initialize,
    preventPropagation: atwhoStopPropagation,
    closePopup: closePopup
  });

  return publicApi;

})();


// initialize the smart annotations
(function initSmartAnnotation() {
  $(document).on('focus', '[data-atwho-edit]', function() {
    if(_.isUndefined($(this).data('atwho'))) {
      SmartAnnotation.init(this);
    }
  });
})();