'use strict';

const Long = require('./long');

const MASK_8 = 0xff;
const MASK_24 = 0xffffff;
const MASK_32 = 0xffffffff;

// See http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param for the definition of these parameters;
const FNV_PRIME = new Long(16777619, 0);
const OFFSET_BASIS = new Long(2166136261, 0);
const FNV_MASK = new Long(MASK_32, 0);

/**
 * Implementation of the FNV-1a hash for a 32-bit hash value
 * Algorithm can be found here: http://www.isthe.com/chongo/tech/comp/fnv/#FNV-1a
 * @ignore
 */
function fnv1a32(input, encoding) {
  encoding = encoding || 'utf8';
  const octets = Buffer.from(input, encoding);

  let hash = OFFSET_BASIS;
  for (let i = 0; i < octets.length; i += 1) {
    hash = hash.xor(new Long(octets[i], 0));
    hash = hash.multiply(FNV_PRIME);
    hash = hash.and(FNV_MASK);
  }
  return hash.getLowBitsUnsigned();
}

/**
 * Implements FNV-1a to generate 32-bit hash, then uses xor-folding
 * to convert to a 24-bit hash. See here for more info:
 * http://www.isthe.com/chongo/tech/comp/fnv/#xor-fold
 * @ignore
 */
function fnv1a24(input, encoding) {
  const _32bit = fnv1a32(input, encoding);
  const base = _32bit & MASK_24;
  const top = (_32bit >>> 24) & MASK_8;
  const final = (base ^ top) & MASK_24;

  return final;
}

module.exports = { fnv1a24, fnv1a32 };
