/*
 * Adapted from jQuery Tags Input Revisited Plugin
 * https://github.com/underovsky/jquery-tagsinput-revisited
 *
 * MIT License
 *
 * Copyright (c) 2017 Krzysztof Rusnarczyk
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

(function($) {
  var delimiter = [];
  var inputSettings = [];
  var callbacks = [];

  $.fn.addTag = function(value, options) {
    options = jQuery.extend({
      focus: false,
      callback: true
    }, options);

    this.each(function() {
      var id = $(this).attr('id');

      var tagslist = $(this).val().split(_getDelimiter(delimiter[id]));
      if (tagslist[0] === '') tagslist = [];

      value = jQuery.trim(value);

      if ((inputSettings[id].unique && $(this).tagExist(value)) || !_validateTag(value, inputSettings[id], tagslist, delimiter[id])) {
        $('#' + id + '_tag').addClass('error');
        return false;
      }

      $('<span>', {class: 'tag'}).append(
        $('<span>', {class: 'tag-text'}).text(value),
        $('<button>', {class: 'tag-remove', title: 'Remove'}).click(function() {
          return $('#' + id).removeTag(encodeURI(value));
        }).append(
          $('<i>', {class:'fas fa-times-circle text-danger'})
        )
      ).insertBefore('#' + id + '_addTag');

      tagslist.push(value);

      $('#' + id + '_tag').val('');
      if (options.focus) {
        $('#' + id + '_tag').focus();
      } else {
        $('#' + id + '_tag').blur();
      }

      $.fn.tagsInput.updateTagsField(this, tagslist);

      if (options.callback && callbacks[id] && callbacks[id]['onAddTag']) {
        var f = callbacks[id]['onAddTag'];
        f.call(this, this, value);
      }

      if (callbacks[id] && callbacks[id]['onChange']) {
        var i = tagslist.length;
        var f = callbacks[id]['onChange'];
        f.call(this, this, value);
      }
    });

    return false;
  };

  $.fn.removeTag = function(value) {
    value = decodeURI(value);

    this.each(function() {
      var id = $(this).attr('id');

      var old = $(this).val().split(_getDelimiter(delimiter[id]));

      $('#' + id + '_tagsinput .tag').remove();

      var str = '';
      for (i = 0; i < old.length; ++i) {
        if (old[i] != value) {
          str = str + _getDelimiter(delimiter[id]) + old[i];
        }
      }

      $.fn.tagsInput.importTags(this, str);

      if (callbacks[id] && callbacks[id]['onRemoveTag']) {
        var f = callbacks[id]['onRemoveTag'];
        f.call(this, this, value);
      }
    });

    return false;
  };

  $.fn.tagExist = function(val) {
    var id = $(this).attr('id');
    var tagslist = $(this).val().split(_getDelimiter(delimiter[id]));
    return (jQuery.inArray(val, tagslist) >= 0);
  };

  $.fn.importTags = function(str) {
    var id = $(this).attr('id');
    $('#' + id + '_tagsinput .tag').remove();
    $.fn.tagsInput.importTags(this, str);
  };

  $.fn.tagsInput = function(options) {
    var settings = jQuery.extend({
      interactive: true,
      placeholder: 'Add a tag',
      minChars: 0,
      maxChars: null,
      limit: null,
      validationPattern: null,
      width: 'auto',
      height: 'auto',
      autocomplete: null,
      hide: true,
      delimiter: ',',
      unique: true,
      removeWithBackspace: true
    }, options);

    var uniqueIdCounter = 0;

    this.each(function() {
      if (typeof $(this).data('tagsinput-init') !== 'undefined') return;

      $(this).data('tagsinput-init', true);

      if (settings.hide) $(this).hide();

      var id = $(this).attr('id');
      if (!id || _getDelimiter(delimiter[$(this).attr('id')])) {
        id = $(this).attr('id', 'tags' + new Date().getTime() + (++uniqueIdCounter)).attr('id');
      }

      var data = jQuery.extend({
        pid: id,
        real_input: '#' + id,
        holder: '#' + id + '_tagsinput',
        input_wrapper: '#' + id + '_addTag',
        fake_input: '#' + id + '_tag'
      }, settings);

      delimiter[id] = data.delimiter;
      inputSettings[id] = {
        minChars: settings.minChars,
        maxChars: settings.maxChars,
        limit: settings.limit,
        validationPattern: settings.validationPattern,
        unique: settings.unique
      };

      if (settings.onAddTag || settings.onRemoveTag || settings.onChange) {
        callbacks[id] = [];
        callbacks[id]['onAddTag'] = settings.onAddTag;
        callbacks[id]['onRemoveTag'] = settings.onRemoveTag;
        callbacks[id]['onChange'] = settings.onChange;
      }

      var markup = $('<div>', {id: id + '_tagsinput', class: 'tagsinput'}).append(
        $('<div>', {id: id + '_addTag'}).append(
          settings.interactive ? $('<input>', {id: id + '_tag', class: 'tag-input', value: '', placeholder: settings.placeholder}) : null
        )
      );

      $(markup).insertAfter(this);

      $(data.holder).css('width', settings.width);
      $(data.holder).css('min-height', settings.height);
      $(data.holder).css('height', settings.height);

      if ($(data.real_input).val() !== '') {
        $.fn.tagsInput.importTags($(data.real_input), $(data.real_input).val());
      }

      // Stop here if interactive option is not chosen
      if (!settings.interactive) return;

      $(data.fake_input).val('');
      $(data.fake_input).data('pasted', false);

      $(data.fake_input).on('focus', data, function(event) {
        $(data.holder).addClass('focus');

        if ($(this).val() === '') {
          $(this).removeClass('error');
        }
      });

      $(data.fake_input).on('blur', data, function(event) {
        $(data.holder).removeClass('focus');
      });

      if (settings.autocomplete !== null && jQuery.ui.autocomplete !== undefined) {
        $(data.fake_input).autocomplete(settings.autocomplete);
        $(data.fake_input).on('autocompleteselect', data, function(event, ui) {
          $(event.data.real_input).addTag(ui.item.value, {
            focus: true,
            unique: settings.unique
          });

          return false;
        });

        $(data.fake_input).on('keypress', data, function(event) {
          if (_checkDelimiter(event)) {
            $(this).autocomplete("close");
          }
        });
      } else {
        $(data.fake_input).on('blur', data, function(event) {
          $(event.data.real_input).addTag($(event.data.fake_input).val(), {
            focus: true,
            unique: settings.unique
          });

          return false;
        });
      }

      // If a user types a delimiter create a new tag
      $(data.fake_input).on('keypress', data, function(event) {
        if (_checkDelimiter(event)) {
          event.preventDefault();

          $(event.data.real_input).addTag($(event.data.fake_input).val(), {
            focus: true,
            unique: settings.unique
          });

          return false;
        }
      });

      $(data.fake_input).on('paste', function () {
        $(this).data('pasted', true);
      });

      // If a user pastes the text check if it shouldn't be splitted into tags
      $(data.fake_input).on('input', data, function(event) {
        if (!$(this).data('pasted')) return;

        $(this).data('pasted', false);

        var value = $(event.data.fake_input).val();

        value = value.replace(/\n/g, '');
        value = value.replace(/\s/g, '');

        var tags = _splitIntoTags(event.data.delimiter, value);

        if (tags.length > 1) {
          for (var i = 0; i < tags.length; ++i) {
            $(event.data.real_input).addTag(tags[i], {
              focus: true,
              unique: settings.unique
            });
          }

          return false;
        }
      });

      // Deletes last tag on backspace
      data.removeWithBackspace && $(data.fake_input).on('keydown', function(event) {
        if (event.keyCode == 8 && $(this).val() === '') {
           event.preventDefault();
           var lastTag = $(this).closest('.tagsinput').find('.tag:last > span').text();
           var id = $(this).attr('id').replace(/_tag$/, '');
           $('#' + id).removeTag(encodeURI(lastTag));
           $(this).trigger('focus');
        }
      });

      // Removes the error class when user changes the value of the fake input
      $(data.fake_input).keydown(function(event) {
        // enter, alt, shift, esc, ctrl and arrows keys are ignored
        if (jQuery.inArray(event.keyCode, [13, 37, 38, 39, 40, 27, 16, 17, 18, 225]) === -1) {
          $(this).removeClass('error');
        }
      });
    });

    return this;
  };

  $.fn.tagsInput.updateTagsField = function(obj, tagslist) {
    var id = $(obj).attr('id');
    $(obj).val(tagslist.join(_getDelimiter(delimiter[id])));
  };

  $.fn.tagsInput.importTags = function(obj, val) {
    $(obj).val('');

    var id = $(obj).attr('id');
    var tags = _splitIntoTags(delimiter[id], val);

    for (i = 0; i < tags.length; ++i) {
      $(obj).addTag(tags[i], {
        focus: false,
        callback: false
      });
    }

    if (callbacks[id] && callbacks[id]['onChange']) {
      var f = callbacks[id]['onChange'];
      f.call(obj, obj, tags);
    }
  };

  var _getDelimiter = function(delimiter) {
    if (typeof delimiter === 'undefined') {
      return delimiter;
    } else if (typeof delimiter === 'string') {
      return delimiter;
    } else {
      return delimiter[0];
    }
  };

  var _validateTag = function(value, inputSettings, tagslist, delimiter) {
    var result = true;

    if (value === '') result = false;
    if (value.length < inputSettings.minChars) result = false;
    if (inputSettings.maxChars !== null && value.length > inputSettings.maxChars) result = false;
    if (inputSettings.limit !== null && tagslist.length >= inputSettings.limit) result = false;
    if (inputSettings.validationPattern !== null && !inputSettings.validationPattern.test(value)) result = false;

    if (typeof delimiter === 'string') {
      if (value.indexOf(delimiter) > -1) result = false;
    } else {
      $.each(delimiter, function(index, _delimiter) {
        if (value.indexOf(_delimiter) > -1) result = false;
        return false;
      });
    }

    return result;
  };

  var _checkDelimiter = function(event) {
    var found = false;

    if (event.which === 13) {
      return true;
    }

    if (typeof event.data.delimiter === 'string') {
      if (event.which === event.data.delimiter.charCodeAt(0)) {
        found = true;
      }
    } else {
      $.each(event.data.delimiter, function(index, delimiter) {
        if (event.which === delimiter.charCodeAt(0)) {
          found = true;
        }
      });
    }

    return found;
   };

   var _splitIntoTags = function(delimiter, value) {
     if (value === '') return [];

     if (typeof delimiter === 'string') {
       return value.split(delimiter);
     } else {
       var tmpDelimiter = '∞';
       var text = value;

       $.each(delimiter, function(index, _delimiter) {
         text = text.split(_delimiter).join(tmpDelimiter);
       });

       return text.split(tmpDelimiter);
     }

     return [];
   };
})(jQuery);
