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

/// Buffer reader for parsing MeshCore protocol binary data
class BufferReader {
  final Uint8List _buffer;
  int _offset = 0;

  BufferReader(this._buffer);

  /// Get remaining bytes count
  int get remainingBytesCount => _buffer.length - _offset;

  /// Check if there are bytes remaining
  bool get hasRemaining => _offset < _buffer.length;

  /// Get current offset
  int get offset => _offset;

  /// Set offset
  set offset(int value) => _offset = value;

  /// Read a single byte (uint8)
  int readByte() {
    if (_offset >= _buffer.length) {
      throw Exception('Buffer overflow: attempting to read beyond buffer length');
    }
    return _buffer[_offset++];
  }

  /// Read a signed byte (int8)
  int readInt8() {
    final value = readByte();
    return value > 127 ? value - 256 : value;
  }

  /// Read unsigned 16-bit integer (little-endian)
  int readUInt16LE() {
    if (_offset + 2 > _buffer.length) {
      throw Exception('Buffer overflow: attempting to read beyond buffer length');
    }
    final value = _buffer[_offset] | (_buffer[_offset + 1] << 8);
    _offset += 2;
    return value;
  }

  /// Read signed 16-bit integer (little-endian)
  int readInt16LE() {
    final value = readUInt16LE();
    return value > 32767 ? value - 65536 : value;
  }

  /// Read unsigned 16-bit integer (big-endian)
  int readUInt16BE() {
    if (_offset + 2 > _buffer.length) {
      throw Exception('Buffer overflow: attempting to read beyond buffer length');
    }
    final value = (_buffer[_offset] << 8) | _buffer[_offset + 1];
    _offset += 2;
    return value;
  }

  /// Read signed 16-bit integer (big-endian)
  int readInt16BE() {
    final value = readUInt16BE();
    return value > 32767 ? value - 65536 : value;
  }

  /// Read unsigned 32-bit integer (little-endian)
  int readUInt32LE() {
    if (_offset + 4 > _buffer.length) {
      throw Exception('Buffer overflow: attempting to read beyond buffer length');
    }
    final value = _buffer[_offset] |
        (_buffer[_offset + 1] << 8) |
        (_buffer[_offset + 2] << 16) |
        (_buffer[_offset + 3] << 24);
    _offset += 4;
    return value;
  }

  /// Read signed 32-bit integer (little-endian)
  int readInt32LE() {
    final value = readUInt32LE();
    return value > 2147483647 ? value - 4294967296 : value;
  }

  /// Read a fixed number of bytes
  Uint8List readBytes(int length) {
    if (_offset + length > _buffer.length) {
      throw Exception('Buffer overflow: attempting to read beyond buffer length');
    }
    final bytes = _buffer.sublist(_offset, _offset + length);
    _offset += length;
    return bytes;
  }

  /// Read remaining bytes
  Uint8List readRemainingBytes() {
    final bytes = _buffer.sublist(_offset);
    _offset = _buffer.length;
    return bytes;
  }

  /// Read null-terminated string (C-string) with max length
  String readCString(int maxLength) {
    if (_offset + maxLength > _buffer.length) {
      throw Exception('Buffer overflow: attempting to read beyond buffer length');
    }

    final bytes = _buffer.sublist(_offset, _offset + maxLength);
    _offset += maxLength;

    // Find null terminator
    int nullIndex = bytes.indexOf(0);
    if (nullIndex == -1) {
      nullIndex = maxLength;
    }

    // Decode string up to null terminator
    return utf8.decode(bytes.sublist(0, nullIndex));
  }

  /// Read length-prefixed string (remaining bytes as UTF-8)
  String readString() {
    final bytes = readRemainingBytes();
    return utf8.decode(bytes);
  }

  /// Peek at next byte without advancing offset
  int peekByte() {
    if (_offset >= _buffer.length) {
      throw Exception('Buffer overflow: attempting to peek beyond buffer length');
    }
    return _buffer[_offset];
  }

  /// Skip bytes
  void skip(int count) {
    if (_offset + count > _buffer.length) {
      throw Exception('Buffer overflow: attempting to skip beyond buffer length');
    }
    _offset += count;
  }

  /// Reset offset to beginning
  void reset() {
    _offset = 0;
  }

  @override
  String toString() {
    return 'BufferReader(length: ${_buffer.length}, offset: $_offset, remaining: $remainingBytesCount)';
  }
}
