/* global L, log -- eslint */

/**
 * @file These functions set up specific areas after the boot function created a basic framework.
 *       All of these functions should only ever be run once.
 * @module boot
 */

/**
 * Initializes tooltips for a specified element or the entire document if no element is provided.
 * This function sets up jQuery UI tooltips with customized behavior. It ensures that only one tooltip
 * is visible at a time by closing others when a new one opens. The content of the tooltip is derived
 * from the 'title' attribute of the HTML element and is processed by the `convertTextToTableMagic` function.
 *
 * Additionally, this function sets up a one-time event handler (if not already set) on the document
 * to remove tooltips when clicked. This is controlled by the `tooltipClearerHasBeenSetup` flag to prevent
 * multiple bindings of the event handler.
 *
 * @function setupTooltips
 * @param {jQuery|HTMLElement} [element=document] - The jQuery or DOM element to which the tooltips will be attached.
 *                                                  If not provided, the document itself is used.
 */
window.setupTooltips = function (element) {
  element = element || $(document);
  element.tooltip({
    // disable show/hide animation
    show: { effect: 'none', duration: 0, delay: 350 },
    hide: false,
    open: function (event, ui) {
      // ensure all other tooltips are closed
      $('.ui-tooltip').not(ui.tooltip).remove();
    },
    content: function () {
      var title = $(this).attr('title');
      return window.convertTextToTableMagic(title);
    },
  });

  if (!window.tooltipClearerHasBeenSetup) {
    window.tooltipClearerHasBeenSetup = true;
    $(document).on('click', '.ui-tooltip', function () {
      $(this).remove();
    });
  }
};

/**
 * Initializes Ingress markers with custom icons.
 * @function setupIngressMarkers
 */
function setupIngressMarkers() {
  L.Icon.Default.mergeOptions({
    iconUrl: '@include_img:images/marker-ingress.png@',
    iconRetinaUrl: '@include_img:images/marker-ingress-2x.png@',
    shadowUrl: '@include_img:external/images/marker-shadow.png@',
  });
  L.Icon.Default.imagePath = ' '; // in order to suppress _detectIconPath (it fails with data-urls)

  $(
    [
      '<svg>',
      // search.js, distance-to-portal.user.js, draw-tools.user.js
      '<symbol id="marker-icon" viewBox="0 0 25 41">',
      '<path d="M1.36241844765,18.67488124675 A12.5,12.5 0 1,1 23.63758155235,18.67488124675 L12.5,40.5336158073 Z" style="stroke:none;" />',
      '<path d="M1.80792170975,18.44788599685 A12,12 0 1,1 23.19207829025,18.44788599685 L12.5,39.432271175 Z" style="stroke:#000000; stroke-width:1px; stroke-opacity: 0.15; fill: none;" />',
      '<path d="M2.921679865,17.8803978722 A10.75,10.75 0 1,1 22.078320135,17.8803978722 L12.5,36.6789095943 Z" style="stroke:#ffffff; stroke-width:1.5px; stroke-opacity: 0.35; fill: none;" />',
      '<path d="M19.86121593215,17.25 L12.5,21.5 L5.13878406785,17.25 L5.13878406785,8.75 L12.5,4.5 L19.86121593215,8.75 Z M7.7368602792,10.25 L17.2631397208,10.25 L12.5,18.5 Z M12.5,13 L7.7368602792,10.25 M12.5,13 L17.2631397208,10.25 M12.5,13 L12.5,18.5 M19.86121593215,17.25 L16.39711431705,15.25 M5.13878406785,17.25 L8.60288568295,15.25 M12.5,4.5 L12.5,8.5" style="stroke:#ffffff; stroke-width:1.25px; stroke-opacity: 1; fill: none;" />',
      '</symbol>',
      '</svg>',
    ].join('\\n')
  ).appendTo('body');

  L.DivIcon.ColoredSvg = L.DivIcon.extend({
    options: {
      iconSize: [25, 41],
      iconAnchor: [12, 41],
      className: 'leaflet-div-icon-iitc-generic-marker',
      // ^ actually any name, just to prevent default
      // ^ (as it's inappropriately styled)
      svgTemplate: '<svg style="fill: {color}"><use xlink:href="#marker-icon"/></svg>',
      color: '#a24ac3', // for draw-tools:
      // L.divIcon does not use the option `color`, but we store it here to
      // be able to simply retrieve the color for serializing markers
    },
    initialize: function (color, options) {
      L.DivIcon.prototype.initialize.call(this, options);
      if (color) {
        this.options.color = color;
      }
      this.options.html = L.Util.template(this.options.svgTemplate, { color: this.options.color });
    },
  });
  L.divIcon.coloredSvg = function (color, options) {
    return new L.DivIcon.ColoredSvg(color, options);
  };
}

/**
 * Checks if the IITC is being run on the official Intel URL. If not, it displays a warning dialog.
 * @function checkingIntelURL
 */
function checkingIntelURL() {
  if (window.location.hostname !== 'intel.ingress.com' && localStorage['pass-checking-intel-url'] !== 'true') {
    window.dialog({
      title: 'IITC Warning',
      html: '<p>You are running IITC on a non-standard Intel domain. Correct behavior is not guaranteed. It is recommended to use the IITC at <a href="https://intel.ingress.com">intel.ingress.com</a></p>',
      dialogClass: 'ui-dialog-non-standard-intel',
      buttons: {
        "Don't remind me": function () {
          $(this).dialog('close');
          localStorage['pass-checking-intel-url'] = true;
        },
        Dismiss: function () {
          $(this).dialog('close');
        },
      },
    });
  }
}

/**
 * Sets up the OverlappingMarkerSpiderfier (OMS) library for handling overlapping markers on the map.
 * OMS doesn't cancel the original click event, so the topmost marker will get a click event while spiderfying.
 * Also, OMS only supports a global callback for all managed markers. Therefore, we will use a custom event that gets fired
 * for each marker.
 * @function setupOMS
 */
window.setupOMS = function () {
  window.oms = new window.OverlappingMarkerSpiderfier(window.map, {
    keepSpiderfied: true,
    legWeight: 3.5,
    legColors: {
      usual: '#FFFF00',
      highlighted: '#FF0000',
    },
  });

  window.oms.addListener('click', function (marker) {
    window.map.closePopup();
    marker.fireEvent('spiderfiedclick', { target: marker });
  });
  window.oms.addListener('spiderfy', function () {
    window.map.closePopup();
  });
  window.map._container.addEventListener(
    'keypress',
    function (ev) {
      if (ev.keyCode === 27) {
        // Esc
        window.oms.unspiderfy();
      }
    },
    false
  );
};

/**
 * Registers a marker with the OverlappingMarkerSpiderfier to manage its click events.
 * @function registerMarkerForOMS
 * @param {L.Marker} marker - The Leaflet marker to be managed by OMS.
 */
window.registerMarkerForOMS = function (marker) {
  marker.on('add', function () {
    window.oms.addMarker(marker);
  });
  marker.on('remove', function () {
    window.oms.removeMarker(marker);
  });
  if (marker._map) {
    // marker has already been added
    window.oms.addMarker(marker);
  }
};

// BOOTING ///////////////////////////////////////////////////////////

/**
 * Prepares plugins to load by sorting them based on their specified priority.
 * @function prepPluginsToLoad
 * @returns {Function} A loader function that loads plugins up to a specified priority.
 */
function prepPluginsToLoad() {
  var priorities = {
    lowest: 100,
    low: 75,
    normal: 50,
    high: 25,
    highest: 0,
    boot: -100,
  };

  function getPriority(data) {
    var v = (data && data.priority) || 'normal';
    var prio = v in priorities ? priorities[v] : v;
    if (typeof prio !== 'number') {
      log.warn('wrong plugin priority specified: ', v);
      prio = priorities.normal;
    }
    return prio;
  }

  if (!window.script_info.script) {
    log.warn('GM_info is not provided (improper userscript manager?)'); // IITC-Mobile for iOS
  }

  // executes setup function of plugin
  // and collects info for About IITC
  function safeSetup(setup) {
    if (!setup) {
      log.warn('plugin must provide setup function');
      return;
    }
    var info = setup.info;
    if (typeof info !== 'object') {
      log.warn('plugin does not have proper wrapper:', setup);
      info = {};
    }
    try {
      setup.call(this);
    } catch (err) {
      var name = (info.script && info.script.name) || info.pluginId;
      log.error('error starting plugin: ' + name, '\n' + err, '\nsetup: ', setup);
      info.error = err;
    }
    pluginsInfo.push(info);
  }

  if (window.bootPlugins) {
    // sort plugins by priority
    window.bootPlugins.sort(function (a, b) {
      return getPriority(a) - getPriority(b);
    });
  } else {
    window.bootPlugins = [];
  }

  var pluginsInfo = []; // for About IITC
  window.bootPlugins.info = pluginsInfo;

  // loader function returned
  // if called with parameter then load plugins with priorities up to specified
  return function (prio) {
    while (window.bootPlugins[0]) {
      if (prio && getPriority(window.bootPlugins[0]) > priorities[prio]) {
        break;
      }
      safeSetup(window.bootPlugins.shift());
    }
  };
}

/**
 * The main boot function that initializes IITC. It is responsible for setting up the map,
 * loading plugins, and initializing various components of IITC.
 * @function boot
 */
function boot() {
  log.log('loading done, booting. Built: ' + '@build_date@');
  if (window.deviceID) {
    log.log('Your device ID: ' + window.deviceID);
  }
  window.runOnSmartphonesBeforeBoot();
  window.runOnAppBeforeBoot();

  var loadPlugins = prepPluginsToLoad();
  loadPlugins('boot');

  window.setupDialogs();
  checkingIntelURL();
  setupIngressMarkers();
  window.extractFromStock();
  window.setupIdle();
  window.setupDataTileParams();
  window.setupMap();
  window.setupOMS();
  window.ornaments.setup();
  window.layerChooser._lastPriority = 1000; // plugins overlays have priority >1000
  window.setupTooltips();
  window.chat.setup();
  window.updateGameScore();
  window.search.setup();
  window.portalDetail.setup();
  window.setupRedeem();
  window.setupSidebar();

  loadPlugins();

  window.runOnSmartphonesAfterBoot();
  window.runOnAppAfterBoot();

  // workaround for #129. Not sure why this is required.
  // setTimeout('window.map.invalidateSize(false);', 500);

  window.iitcLoaded = true;
  window.runHooks('iitcLoaded');
}

try {
  // eslint-disable-next-line
  '@include_raw:external/autolink-min.js@';

  window.L_NO_TOUCH = navigator.maxTouchPoints === 0; // prevent mobile style on desktop https://github.com/IITC-CE/ingress-intel-total-conversion/pull/189
  // eslint-disable-next-line
  '@include_raw:external/leaflet-src.js@';
  // eslint-disable-next-line
  '@include_raw:external/L.Geodesic.js@';
  // eslint-disable-next-line
  '@include_raw:external/Leaflet.GoogleMutant.js@';
  // eslint-disable-next-line
  '@include_raw:external/oms.min.js@';
  L.CanvasIconLayer = (function (module) {
    '@include_raw:external/rbush.min.js@';
    '@include_raw:external/leaflet.canvas-markers.js@';
    return module;
  })({}).exports(L);

  // eslint-disable-next-line
  '@include_raw:external/jquery-3.6.0.min.js@';
  // eslint-disable-next-line
  '@include_raw:external/jquery-ui-1.12.1.min.js@';
  // eslint-disable-next-line
  '@include_raw:external/taphold.js@';
  // eslint-disable-next-line
  '@include_raw:external/jquery.qrcode.min.js@';
} catch (e) {
  log.error("External's js loading failed");
  throw e;
}

if (document.readyState === 'complete') {
  // IITCm
  setTimeout(boot);
} else {
  window.addEventListener('load', function () {
    setTimeout(boot);
  });
}