/**
 * @license
 * Copyright 2022-2025 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import {
  Days,
  FLOAT32_MAX,
  FLOAT32_MIN,
  FLOAT64_MAX,
  FLOAT64_MIN,
  INT16_MAX,
  INT16_MIN,
  INT32_MAX,
  INT32_MIN,
  INT64_MAX,
  INT64_MIN,
  INT8_MAX,
  INT8_MIN,
  ImplementationError,
  Seconds,
  UINT16_MAX,
  UINT24_MAX,
  UINT32_MAX,
  UINT64_MAX,
  UINT8_MAX,
  UnexpectedDataError,
  maxValue,
  minValue
} from "#general";
import { ValidationDatatypeMismatchError, ValidationOutOfBoundsError } from "../common/ValidationError.js";
import { BitmapSchema } from "../schema/BitmapSchema.js";
import { TlvCodec, TlvLength, TlvType } from "./TlvCodec.js";
import { TlvSchema } from "./TlvSchema.js";
import { TlvWrapper } from "./TlvWrapper.js";
class TlvNumericSchema extends TlvSchema {
  constructor(type, lengthProvider, baseTypeMin, baseTypeMax, min, max) {
    super();
    this.type = type;
    this.lengthProvider = lengthProvider;
    this.baseTypeMin = baseTypeMin;
    this.baseTypeMax = baseTypeMax;
    if (min !== void 0 && min > baseTypeMin) {
      this.#min = min;
    }
    if (max !== void 0 && max < baseTypeMax) {
      this.#max = max;
    }
  }
  #min;
  #max;
  get min() {
    return this.#min ?? this.baseTypeMin;
  }
  get max() {
    return this.#max ?? this.baseTypeMax;
  }
  encodeTlvInternal(writer, value, tag) {
    const typeLength = { type: this.type, length: this.lengthProvider(value) };
    writer.writeTag(typeLength, tag);
    writer.writePrimitive(typeLength, value);
  }
  decodeTlvInternalValue(reader, typeLength) {
    if (typeLength.type !== this.type)
      throw new UnexpectedDataError(`Unexpected type ${typeLength.type}, was expecting ${this.type}.`);
    return reader.readPrimitive(typeLength);
  }
  validate(value) {
    if (typeof value !== "number" && typeof value !== "bigint")
      throw new ValidationDatatypeMismatchError(`Expected number, got ${typeof value}.`);
    this.validateBoundaries(value);
  }
  validateBoundaries(value) {
    if (this.min !== void 0 && value < this.min)
      throw new ValidationOutOfBoundsError(`Invalid value: ${value} is below the minimum, ${this.min}.`);
    if (this.max !== void 0 && value > this.max)
      throw new ValidationOutOfBoundsError(`Invalid value: ${value} is above the maximum, ${this.max}.`);
  }
  /** Restrict value range. */
  bound({ min, max }) {
    return new TlvNumericSchema(
      this.type,
      this.lengthProvider,
      maxValue(min, this.min),
      minValue(max, this.max)
    );
  }
}
class TlvNumberSchema extends TlvNumericSchema {
  constructor(type, lengthProvider, baseTypeMin, baseTypeMax, min, max) {
    super(type, lengthProvider, baseTypeMin, baseTypeMax, min, max);
  }
  decodeTlvInternalValue(reader, typeLength) {
    const value = super.decodeTlvInternalValue(reader, typeLength);
    return typeof value === "bigint" ? Number(value) : value;
  }
  bound({ min, max }) {
    return new TlvNumberSchema(
      this.type,
      this.lengthProvider,
      this.baseTypeMin,
      this.baseTypeMax,
      maxValue(min, this.min),
      minValue(max, this.max)
    );
  }
  validate(value) {
    if (typeof value !== "number")
      throw new ValidationDatatypeMismatchError(`Expected number, got ${typeof value}.`);
    this.validateBoundaries(value);
  }
}
const TlvLongNumberSchema = TlvNumericSchema;
const TlvFloat = new TlvNumberSchema(TlvType.Float, (_value) => TlvLength.FourBytes, FLOAT32_MIN, FLOAT32_MAX);
const TlvDouble = new TlvNumberSchema(TlvType.Float, (_value) => TlvLength.EightBytes, FLOAT64_MIN, FLOAT64_MAX);
const TlvInt8 = new TlvNumberSchema(
  TlvType.SignedInt,
  (value) => TlvCodec.getIntTlvLength(value),
  INT8_MIN,
  INT8_MAX
);
const TlvInt16 = new TlvNumberSchema(
  TlvType.SignedInt,
  (value) => TlvCodec.getIntTlvLength(value),
  INT16_MIN,
  INT16_MAX
);
const TlvInt32 = new TlvNumberSchema(
  TlvType.SignedInt,
  (value) => TlvCodec.getIntTlvLength(value),
  INT32_MIN,
  INT32_MAX
);
const TlvInt64 = new TlvLongNumberSchema(
  TlvType.SignedInt,
  (value) => TlvCodec.getIntTlvLength(value),
  INT64_MIN,
  INT64_MAX
);
const TlvUInt8 = new TlvNumberSchema(
  TlvType.UnsignedInt,
  (value) => TlvCodec.getUIntTlvLength(value),
  0,
  UINT8_MAX
);
const TlvUInt16 = new TlvNumberSchema(
  TlvType.UnsignedInt,
  (value) => TlvCodec.getUIntTlvLength(value),
  0,
  UINT16_MAX
);
const TlvUInt24 = new TlvNumberSchema(
  TlvType.UnsignedInt,
  (value) => TlvCodec.getUIntTlvLength(value),
  0,
  UINT24_MAX
);
const TlvUInt32 = new TlvNumberSchema(
  TlvType.UnsignedInt,
  (value) => TlvCodec.getUIntTlvLength(value),
  0,
  UINT32_MAX
);
const TlvUInt64 = new TlvLongNumberSchema(
  TlvType.UnsignedInt,
  (value) => TlvCodec.getUIntTlvLength(value),
  0,
  UINT64_MAX
);
const TlvEnum = () => TlvUInt32;
const TlvBitmap = (underlyingSchema, bitSchema) => {
  const bitmapSchema = BitmapSchema(bitSchema);
  return new TlvWrapper(
    underlyingSchema,
    (bitmapData) => bitmapSchema.encode(bitmapData),
    (value) => bitmapSchema.decode(value)
  );
};
const TlvPercent = TlvUInt8.bound({ max: 100 });
const TlvPercent100ths = TlvUInt16.bound({ max: 1e4 });
const TlvPosixMs = TlvUInt64;
const TlvSysTimeUs = TlvUInt64;
const TlvSysTimeMS = TlvUInt64;
const MATTER_EPOCH_OFFSET = Days(10957);
const MATTER_EPOCH_OFFSET_S = Seconds.of(MATTER_EPOCH_OFFSET);
const MATTER_EPOCH_OFFSET_US = BigInt(MATTER_EPOCH_OFFSET * 1e3);
const TlvEpochS = new TlvWrapper(
  new TlvNumberSchema(
    TlvType.UnsignedInt,
    (value) => TlvCodec.getUIntTlvLength(value),
    0,
    // too low values will be caught in the wrapper
    UINT32_MAX + MATTER_EPOCH_OFFSET_S
  ),
  (unixEpoch) => {
    const value = unixEpoch - MATTER_EPOCH_OFFSET_S;
    if (value < 0) {
      throw new ImplementationError(
        "Do not convert Epoch-values yourself, use TlvEpochS directly with unix epoch values."
      );
    }
    return value;
  },
  (epochS) => epochS + MATTER_EPOCH_OFFSET_S
);
const TlvEpochUs = new TlvWrapper(
  new TlvLongNumberSchema(
    TlvType.UnsignedInt,
    (value) => TlvCodec.getUIntTlvLength(value),
    0,
    // too low values will be caught in the wrapper
    UINT64_MAX + MATTER_EPOCH_OFFSET_US
  ),
  (unixEpoch) => {
    const result = BigInt(unixEpoch) - MATTER_EPOCH_OFFSET_US;
    if (result < BigInt(0)) {
      throw new ImplementationError(
        "Do not convert Epoch-values yourself, use TlvEpochUs directly with unix epoch values."
      );
    }
    return result;
  },
  (epochUs) => BigInt(epochUs) + MATTER_EPOCH_OFFSET_US
);
export {
  MATTER_EPOCH_OFFSET,
  MATTER_EPOCH_OFFSET_S,
  MATTER_EPOCH_OFFSET_US,
  TlvBitmap,
  TlvDouble,
  TlvEnum,
  TlvEpochS,
  TlvEpochUs,
  TlvFloat,
  TlvInt16,
  TlvInt32,
  TlvInt64,
  TlvInt8,
  TlvLongNumberSchema,
  TlvNumberSchema,
  TlvNumericSchema,
  TlvPercent,
  TlvPercent100ths,
  TlvPosixMs,
  TlvSysTimeMS,
  TlvSysTimeUs,
  TlvUInt16,
  TlvUInt24,
  TlvUInt32,
  TlvUInt64,
  TlvUInt8
};
//# sourceMappingURL=TlvNumber.js.map
