/* global log -- eslint */

/**
 * @file Decode the on-network array entity format into an object format closer to that used before
 * makes much more sense as an object, means that existing code didn't need to change, and it's what the
 * stock intel site does internally too (the array format is only on the network)
 *
 * @module entity_decode
 */

/**
 * @namespace window.decodeArray
 */
window.decodeArray = function () {};

/**
 * Parses a mod array into an object.
 *
 * @function parseMod
 * @param {Array} arr - The mod array.
 * @returns {Object|null} Parsed mod object or null if the input is falsy.
 */
function parseMod(arr) {
  if (!arr) {
    return null;
  }
  return {
    owner: arr[0],
    name: arr[1],
    rarity: arr[2],
    stats: arr[3],
  };
}

/**
 * Parses a resonator array into an object.
 *
 * @function parseResonator
 * @param {Array} arr - The resonator array.
 * @returns {Object|null} Parsed resonator object or null if the input is falsy.
 */
function parseResonator(arr) {
  if (!arr) {
    return null;
  }
  return {
    owner: arr[0],
    level: arr[1],
    energy: arr[2],
  };
}

/**
 * Parses an artifact brief array into an object.
 * @function parseArtifactBrief
 * @param {Array} arr - The artifact brief array.
 * @returns {Object|null} Parsed artifact brief object or null if the input is falsy.
 */
function parseArtifactBrief(arr) {
  if (!arr) {
    return null;
  }

  // array index 0 is for fragments at the portal. index 1 is for target portals
  // each of those is two dimensional - not sure why. part of this is to allow for multiple types of artifacts,
  // with their own targets, active at once - but one level for the array is enough for that

  // making a guess - first level is for different artifact types, second index would allow for
  // extra data for that artifact type

  function decodeArtifactArray(arr) {
    var result = {};
    for (var i = 0; i < arr.length; i++) {
      // we'll use the type as the key - and store any additional array values as the value
      // that will be an empty array for now, so only object keys are useful data
      result[arr[i][0]] = arr[i].slice(1);
    }
    return result;
  }

  return {
    fragment: decodeArtifactArray(arr[0]),
    target: decodeArtifactArray(arr[1]),
  };
}

/**
 * Parses an artifact detail array into an object.
 *
 * @function parseArtifactDetail
 * @param {Array} arr - The artifact detail array.
 * @returns {Object|null} Parsed artifact detail object or null if the input is falsy.
 */
function parseArtifactDetail(arr) {
  if (!arr) {
    return null;
  }
  // empty artifact data is pointless - ignore it
  if (arr.length === 3 && arr[0] === '' && arr[1] === '' && arr[2].length === 0) {
    return null;
  }
  return {
    type: arr[0],
    displayName: arr[1],
    fragments: arr[2],
  };
}

/**
 * Parses a history detail bit array into an object.
 *
 * @function parseHistoryDetail
 * @param {number} bitarray - The history detail bit array.
 * @returns {Object} Parsed history detail object.
 */
function parseHistoryDetail(bitarray) {
  return {
    _raw: bitarray,
    visited: !!(bitarray & 1),
    captured: !!(bitarray & 2),
    scoutControlled: !!(bitarray & 4),
  };
}

// there's also a 'placeholder' portal - generated from the data in links/fields. only has team/lat/lng

var CORE_PORTAL_DATA_LENGTH = 4;

/**
 * Parses the core portal data from an array.
 *
 * @function corePortalData
 * @param {Array} a - The portal data array.
 * @returns {Object} An object containing the core data of a portal.
 */
function corePortalData(a) {
  return {
    // a[0] == type (always 'p')
    team: a[1],
    latE6: a[2],
    lngE6: a[3],
  };
}

var SUMMARY_PORTAL_DATA_LENGTH = 14;
var DETAILED_PORTAL_DATA_LENGTH = SUMMARY_PORTAL_DATA_LENGTH + 4;
var EXTENDED_PORTAL_DATA_LENGTH = DETAILED_PORTAL_DATA_LENGTH + 1;

/**
 * Parses the summary portal data from an array.
 *
 * @function summaryPortalData
 * @param {Array} a - The portal data array.
 * @returns {Object} An object containing the summary data of a portal.
 */
function summaryPortalData(a) {
  return {
    level: a[4],
    health: a[5],
    resCount: a[6],
    image: a[7],
    title: a[8],
    ornaments: a[9],
    mission: a[10],
    mission50plus: a[11],
    artifactBrief: parseArtifactBrief(a[12]),
    timestamp: a[13],
  };
}

/**
 * Parses the detailed portal data from an array.
 *
 * @function detailsPortalData
 * @param {Array} a - The portal data array.
 * @returns {Object} An object containing the detailed data of a portal.
 */
function detailsPortalData(a) {
  return {
    mods: a[SUMMARY_PORTAL_DATA_LENGTH].map(parseMod),
    resonators: a[SUMMARY_PORTAL_DATA_LENGTH + 1].map(parseResonator),
    owner: a[SUMMARY_PORTAL_DATA_LENGTH + 2],
    artifactDetail: parseArtifactDetail(a[SUMMARY_PORTAL_DATA_LENGTH + 3]),
  };
}

/**
 * Parses the extended portal data from an array.
 * @function extendedPortalData
 * @param {Array} a - The portal data array.
 * @returns {Object} An object containing the extended data of a portal.
 */
function extendedPortalData(a) {
  return {
    history: parseHistoryDetail(a[DETAILED_PORTAL_DATA_LENGTH] || 0),
  };
}

window.decodeArray.dataLen = {
  core: [CORE_PORTAL_DATA_LENGTH],
  summary: [SUMMARY_PORTAL_DATA_LENGTH],
  detailed: [EXTENDED_PORTAL_DATA_LENGTH, DETAILED_PORTAL_DATA_LENGTH],
  extended: [EXTENDED_PORTAL_DATA_LENGTH, SUMMARY_PORTAL_DATA_LENGTH],
  anyknown: [CORE_PORTAL_DATA_LENGTH, SUMMARY_PORTAL_DATA_LENGTH, DETAILED_PORTAL_DATA_LENGTH, EXTENDED_PORTAL_DATA_LENGTH],
};

/**
 * Decodes an array of portal data into a more detailed object format.
 *
 * @function window.decodeArray.portal
 * @param {Array} a - Array containing portal data.
 * @param {string} [details='anyknown'] - The level of detail to decode.
 *                                        Can be 'core', 'summary', 'detailed', 'extended', or 'anyknown'.
 * @returns {Object} An object containing decoded portal data.
 */
window.decodeArray.portal = function (a, details) {
  if (!a) {
    log.warn('Argument not specified');
    return;
  }

  if (a[0] !== 'p') {
    throw new Error('decodeArray.portal: not a portal');
  }

  details = details || 'anyknown';
  var expected = window.decodeArray.dataLen[details];
  if (expected.indexOf(a.length) === -1) {
    log.warn('Unexpected portal data length: ' + a.length + ' (' + details + ')');
  }

  var data = corePortalData(a);

  if (a.length >= SUMMARY_PORTAL_DATA_LENGTH) {
    $.extend(data, summaryPortalData(a));
  }

  if (a.length >= DETAILED_PORTAL_DATA_LENGTH) {
    if (a[SUMMARY_PORTAL_DATA_LENGTH]) {
      $.extend(data, detailsPortalData(a));
    } else if (details === 'detailed') {
      log.warn('Portal details missing');
    }
  }

  if (a.length >= EXTENDED_PORTAL_DATA_LENGTH || details === 'extended' || details === 'detailed') {
    $.extend(data, extendedPortalData(a));
    if (data.history && data.history.captured && !data.history.visited) {
      log.warn('Inconsistent history data found in portal "' + data.title + '"');
    }
  }

  return data;
};

window.decodeArray.portalSummary = function (a) {
  // deprecated!!
  return window.decodeArray.portal(a, 'summary');
};

window.decodeArray.portalDetail = function (a) {
  // deprecated!!
  return window.decodeArray.portal(a, 'detailed');
};