// This file has been migrated.

library impl.digest.shake;

import 'dart:typed_data';

import 'package:pointycastle/api.dart';
import 'package:pointycastle/src/impl/keccak_engine.dart';
import 'package:pointycastle/src/registry/registry.dart';

///
/// implementation of SHAKE based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
///
/// Following the naming conventions used in the C source code to enable easy review of the implementation.
///
class SHAKEDigest extends KeccakEngine implements Xof {
  static final RegExp _shakeREGEX = RegExp(r'^SHAKE-([0-9]+)$');

  /// Intended for internal use.
  static final FactoryConfig factoryConfig = DynamicFactoryConfig(
      Digest,
      _shakeREGEX,
      (_, final Match match) => () {
            var bitLength = int.parse(match.group(1)!);
            return SHAKEDigest(bitLength);
          });

  SHAKEDigest([int bitLength = 256]) {
    switch (bitLength) {
      case 128:
      case 256:
        init(bitLength);
        break;
      default:
        throw StateError(
            'invalid bitLength ($bitLength) for SHAKE must only be 128 or 256');
    }
  }

  @override
  String get algorithmName => 'SHAKE-$fixedOutputLength';

  @override
  int doFinal(Uint8List out, int outOff) {
    return doFinalRange(out, digestSize, digestSize);
  }

  @override
  int doFinalRange(Uint8List out, int outOff, int outLen) {
    var length = doOutput(out, outOff, outLen);
    reset();
    return length;
  }

  int doFinalPartial(
      Uint8List out, int outOff, int outLen, int partialByte, int partialBits) {
    if (partialBits < 0 || partialBits > 7) {
      throw ArgumentError('partialBits must be in the range [0,7]');
    }

    var finalInput =
        (partialByte & ((1 << partialBits) - 1)) | (0x0F << partialBits);
    var finalBits = partialBits + 4;

    if (finalBits >= 8) {
      absorb(finalInput);
      finalBits -= 8;
      finalInput >>= 8;
    }

    if (finalBits > 0) {
      absorbBits(finalInput, finalBits);
    }

    squeeze(out, outOff, outLen * 8);

    reset();

    return outLen;
  }

  @override
  int doOutput(Uint8List out, int outOff, int outLen) {
    if (!squeezing) {
      absorbBits(0x0F, 4);
    }

    squeeze(out, outOff, outLen * 8);

    return outLen;
  }
}
