/**
 * This file is used to pad message buffer and attachments
 */
const PADDING_BYTE = 0x00;

/**
 * Unpad the buffer from its padding.
 * An error is thrown if there is no padding.
 * A padded buffer is
 *  * whatever at start
 *  * ends with 0x80 and any number of 0x00 until the end
 */
export function removeMessagePadding(paddedData: ArrayBuffer): ArrayBuffer {
  const paddedPlaintext = new Uint8Array(paddedData);
  // window?.log?.info('Removing message padding...');
  for (let i = paddedPlaintext.length - 1; i >= 0; i -= 1) {
    if (paddedPlaintext[i] === 0x80) {
      const plaintext = new Uint8Array(i);
      plaintext.set(paddedPlaintext.subarray(0, i));
      return plaintext.buffer;
    } else if (paddedPlaintext[i] !== PADDING_BYTE) {
      // window?.log?.warn('got a message without padding... Letting it through for now');
      return paddedPlaintext;
    }
  }

  throw new Error('Invalid padding');
}

/**
 * Add padding to a message buffer
 * @param messageBuffer The buffer to add padding to.
 */
export function addMessagePadding(messageBuffer: Uint8Array): Uint8Array {
  // window?.log?.info('Adding message padding...');

  const plaintext = new Uint8Array(getPaddedMessageLength(messageBuffer.byteLength + 1) - 1);
  plaintext.set(new Uint8Array(messageBuffer));
  plaintext[messageBuffer.byteLength] = 0x80;

  return plaintext;
}

function getPaddedMessageLength(originalLength: number): number {
  const messageLengthWithTerminator = originalLength + 1;
  let messagePartCount = Math.floor(messageLengthWithTerminator / 160);

  if (messageLengthWithTerminator % 160 !== 0) {
    messagePartCount += 1;
  }

  return messagePartCount * 160;
}

/*
 * If the attachment has padding, remove the padding and return the unpad attachment
 */
export function getUnpaddedAttachment(
  data: ArrayBuffer,
  unpaddedExpectedSize: number
): ArrayBuffer | null {
  window?.log?.info('Removing attachment padding...');

  // to have a padding we must have a strictly longer length expected
  if (data.byteLength <= unpaddedExpectedSize) {
    return null;
  }
  // we now consider that anything coming after the expected size is padding, no matter what there is there
  return data.slice(0, unpaddedExpectedSize);
}

export function addAttachmentPadding(data: ArrayBuffer): ArrayBuffer {
  const originalUInt = new Uint8Array(data);
  window?.log?.info('Adding attachment padding...');

  const paddedSize = Math.max(
    541,
    Math.floor(Math.pow(1.05, Math.ceil(Math.log(originalUInt.length) / Math.log(1.05))))
  );
  const paddedData = new ArrayBuffer(paddedSize);
  const paddedUInt = new Uint8Array(paddedData);

  paddedUInt.fill(PADDING_BYTE, originalUInt.length);
  paddedUInt.set(originalUInt);

  return paddedUInt.buffer;
}
