/**
This is a timing-attack resistant comparison construct that is used excessively in pycryptodome.
    The solution works around the fact that it is hard to implement and test a truly constant-time comparison
    for various reasons. Instead it calculates randomized internal secure hashes that it can compare in any way.
    This is possible because the secret is hidden from the caller and the comparison is done on values that are
    seemingly independent of the inputs for each invocation.
    NOTE: There is also hmac.compare_digest() available in newer Python versions, but that would still need
    the inputs hashed, so it's unclear what the advantage would be using that.
 *
 */
function crypto_safe_equals(a: Uint8Array, b: Uint8Array) {
  // TODO IMP
}

/**
 *
 * @param args (string | Uint8Array)[]
 * @return concatenated array of the args, but converted into Uint8Array via TextEncoder
 */
export function makeInfoByteArray(args: any[]): Uint8Array {
  let size = 0;
  let convertedArgs = [];
  let encoder = new TextEncoder();

  for (let i = 0; i < args.length; i++) {
    if (typeof args[i] == 'string') {
      convertedArgs[i] = encoder.encode(args[i]);
      size += convertedArgs[i].byteLength;
    } else if (args[i] instanceof Uint8Array) {
      size += args[i].byteLength;
    } else {
      console.error('concat error', args[i]);
      throw 'Not supported concat parameter';
    }
  }

  let result = new Uint8Array(size);

  let index = 0;

  for (let i = 0; i < args.length; i++) {
    let el;
    if (convertedArgs[i]) {
      el = convertedArgs[i];
    } else {
      el = args[i];
    }

    result.set(el, index);
    index += el.byteLength;
  }

  return result;
}

/**
 * Concat all the Uint8Array into one big Uint8Array
 * @param params Uint8Arrays
 * @returns Uint8Array
 */
export function concatByteArray(...params): Uint8Array {
  let size = 0;
  for (let i = 0; i < params.length; i++) {
    size += params[i].byteLength;
  }

  let result = new Uint8Array(size);
  let index = 0;

  for (let i = 0; i < params.length; i++) {
    result.set(params[i], index);
    index += params[i].byteLength;
  }

  return result;
}

export class ValueError extends Error {}

export function concatArrayBuffer(buffs: ArrayBuffer[]): ArrayBuffer {
  let size = 0;

  buffs.forEach((b) => {
    size += b.byteLength;
  });

  const dataView = new DataView(new ArrayBuffer(size));
  let offset = 0;

  buffs.forEach((b) => {
    const currentBufferDataView = new DataView(b);
    for (let i = 0; i < currentBufferDataView.byteLength; i++) {
      dataView.setUint8(offset++, currentBufferDataView.getUint8(i));
    }
  });

  return dataView.buffer;
}

export function compareByteArray(arr1: Uint8Array, arr2: Uint8Array): boolean {
  if (arr1.length === arr2.length) {
    for (let i = 0; i < arr1.length; i++) {
      if (arr1[i] !== arr2[i]) return false;
    }
    return true;
  }
  return false;
}

export function startswithByteArray(needle: Uint8Array, haystack: Uint8Array): boolean {
  if (haystack.length >= needle.length) {
    for (let i = 0; i < needle.length; i++) {
      if (haystack[i] !== needle[i]) return false;
    }
    return true;
  }
  return false;
}
