// This file has been migrated.

library impl.digest.cshake;

import 'dart:typed_data';

import 'package:pointycastle/api.dart';
import 'package:pointycastle/digests/shake.dart';
import 'package:pointycastle/digests/xof_utils.dart';
import 'package:pointycastle/src/registry/registry.dart';
import 'package:pointycastle/src/utils.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 CSHAKEDigest extends SHAKEDigest implements Xof {
  static final RegExp _cshakeREGEX = RegExp(r'^CSHAKE-([0-9]+)$');

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

  Uint8List? _diff;
  final _padding = Uint8List(100);

  CSHAKEDigest([int bitLength = 256, Uint8List? N, Uint8List? S]) {
    switch (bitLength) {
      case 128:
      case 256:
        init(bitLength);
        if ((N == null || N.isEmpty) && (S == null || S.isEmpty)) {
          _diff = null;
        } else {
          _diff = concatUint8List([
            XofUtils.leftEncode(rate ~/ 8),
            _encodeString(N),
            _encodeString(S)
          ]);
          _diffPadAndAbsorb();
        }

        break;
      default:
        throw StateError(
            'invalid bitLength ($bitLength) for CSHAKE must only be 128 or 256');
    }
  }

  @override
  String get algorithmName => 'CSHAKE-$fixedOutputLength';
  @override
  int doOutput(Uint8List out, int outOff, int outLen) {
    if (_diff != null) {
      if (!squeezing) {
        absorbBits(0x00, 2);
      }

      squeeze(out, outOff, outLen * 8);

      return outLen;
    } else {
      return super.doOutput(out, outOff, outLen);
    }
  }

  @override
  void update(Uint8List inp, int inpOff, int len) {
    absorbRange(inp, inpOff, len);
  }

  @override
  void reset() {
    super.reset();

    if (_diff != null) {
      _diffPadAndAbsorb();
    }
  }

  // bytepad in SP 800-185
  void _diffPadAndAbsorb() {
    var blockSize = rate ~/ 8;
    absorbRange(_diff!, 0, _diff!.length);

    var delta = _diff!.length % blockSize;

    // only add padding if needed
    if (delta != 0) {
      var required = blockSize - delta;

      while (required > _padding.length) {
        absorbRange(_padding, 0, _padding.length);
        required -= _padding.length;
      }

      absorbRange(_padding, 0, required);
    }
  }

  Uint8List _encodeString(Uint8List? str) {
    if (str == null || str.isEmpty) {
      return XofUtils.leftEncode(0);
    }

    return concatUint8List([XofUtils.leftEncode(str.length * 8), str]);
  }
}
