import 'dart:convert';
import 'dart:typed_data';
import 'dart:math';

class DeviceConfig {
  final int systemState;
  final BigInt gameId;
  final int panId;
  final int channel;
  final String extPanId;
  final String networkKey;
  final String networkName;
  final String nodeName;

  DeviceConfig({
    required this.systemState,
    required this.gameId,
    required this.panId,
    required this.channel,
    required this.extPanId,
    required this.networkKey,
    required this.networkName,
    required this.nodeName,
  });

  static BigInt generateRandom64BitId() {
    final rand = Random.secure();
    final low = rand.nextInt(0xFFFFFFFF);
    final high = rand.nextInt(0xFFFFFFFF);
    return (BigInt.from(high) << 32) | BigInt.from(low);
  }

  // Hilfsmethode für eine leere/Standard-Config
  factory DeviceConfig.defaultConfig() {
    return DeviceConfig(
      systemState: 1, // IDLE
      gameId: BigInt.from(0),
      panId: 0xABCD,
      channel: 15,
      extPanId: "0011223344556677",
      networkKey: "00112233445566778899AABBCCDDEEFF",
      networkName: "LasertagMesh",
      nodeName: "New Device",
    );
  }

  factory DeviceConfig.fromBytes(List<int> bytes) {
    final data = ByteData.sublistView(Uint8List.fromList(bytes));

    // Manuelles Decoding von 64-Bit (2x 32-Bit), da getUint64 nicht Standard ist
    final low = data.getUint32(1, Endian.little);
    final high = data.getUint32(5, Endian.little);
    final fullGameId = (BigInt.from(high) << 32) | BigInt.from(low);

    return DeviceConfig(
      systemState: data.getUint8(0),
      gameId: fullGameId,
      panId: data.getUint16(9, Endian.little),
      channel: data.getUint8(11),
      extPanId: _bytesToHex(bytes.sublist(12, 20)),
      networkKey: _bytesToHex(bytes.sublist(20, 36)),
      networkName: _decodeString(bytes.sublist(36, 53)),
      nodeName: _decodeString(bytes.sublist(53, 86)),
    );
  }

  Uint8List toBytes() {
    final bytes = Uint8List(86);
    final data = ByteData.view(bytes.buffer);

    data.setUint8(0, systemState);

    // Encoding von 64-Bit BigInt in zwei 32-Bit Segmente
    data.setUint32(
      1,
      (gameId & BigInt.from(0xFFFFFFFF)).toInt(),
      Endian.little,
    );
    data.setUint32(5, (gameId >> 32).toInt(), Endian.little);

    data.setUint16(9, panId, Endian.little);
    data.setUint8(11, channel);

    _writeHexToBuffer(bytes, 12, extPanId);
    _writeHexToBuffer(bytes, 20, networkKey);
    _writeStringToBuffer(bytes, 36, 17, networkName);
    _writeStringToBuffer(bytes, 53, 33, nodeName);

    return bytes;
  }

  static String _decodeString(List<int> bytes) {
    int nullIdx = bytes.indexOf(0);
    return utf8
        .decode(nullIdx == -1 ? bytes : bytes.sublist(0, nullIdx))
        .trim();
  }

  static void _writeStringToBuffer(
    Uint8List buffer,
    int offset,
    int maxLen,
    String val,
  ) {
    final encoded = utf8.encode(val);
    for (int i = 0; i < maxLen - 1 && i < encoded.length; i++) {
      buffer[offset + i] = encoded[i];
    }
    // Buffer ist bereits mit 0 initialisiert, Terminator steht also am Ende
  }

  static String _bytesToHex(List<int> bytes) => bytes
      .map((b) => b.toRadixString(16).padLeft(2, '0'))
      .join()
      .toUpperCase();

  static void _writeHexToBuffer(Uint8List buffer, int offset, String hex) {
    for (int i = 0; i < hex.length; i += 2) {
      buffer[offset + (i ~/ 2)] = int.parse(hex.substring(i, i + 2), radix: 16);
    }
  }

  DeviceConfig copyWith({
    int? systemState,
    BigInt? gameId,
    int? panId,
    int? channel,
    String? extPanId,
    String? networkKey,
    String? networkName,
    String? nodeName,
  }) {
    return DeviceConfig(
      systemState: systemState ?? this.systemState,
      gameId: gameId ?? this.gameId,
      panId: panId ?? this.panId,
      channel: channel ?? this.channel,
      extPanId: extPanId ?? this.extPanId,
      networkKey: networkKey ?? this.networkKey,
      networkName: networkName ?? this.networkName,
      nodeName: nodeName ?? this.nodeName,
    );
  }
}
