// lib/editor/input/pen_input_service.dart
//
// Dart side of the native Windows pen observer (`windows/runner/pen_channel.cpp`).
//
// Streams barrel / eraser / tilt / PRESSURE from WM_POINTER + GetPointerPenInfo.
// Flutter's PointerEvent.pressure on Windows is unreliable (often flat); native
// pressure (0..1024 → [0,1]) is preferred when [PenHardwareState.pressureValid].

import 'dart:async';

import 'package:flutter/services.dart';

import '../../diagnostics/badnote_log.dart';
import '../../diagnostics/pen_event_ring.dart';
import 'diagnostic_logger.dart';

/// Latest hardware pen state delivered by the native observer.
class PenHardwareState {
  const PenHardwareState({
    this.barrel = false,
    this.inverted = false,
    this.eraser = false,
    this.tiltX = 0.0,
    this.tiltY = 0.0,
    this.pressure = 0.0,
    this.pressureValid = false,
  });

  final bool barrel;
  final bool inverted;
  final bool eraser;
  final double tiltX;
  final double tiltY;

  /// Normalized stylus pressure in [0,1] when [pressureValid] is true.
  final double pressure;
  final bool pressureValid;

  double get tiltMagnitude {
    final t = tiltX * tiltX + tiltY * tiltY;
    return t <= 0 ? 0.0 : _sqrt(t);
  }

  static const empty = PenHardwareState();
}

double _sqrt(double v) {
  if (v <= 0) return 0;
  var x = v;
  var last = 0.0;
  for (var i = 0; i < 12 && x != last; i++) {
    last = x;
    x = 0.5 * (x + v / x);
  }
  return x;
}

class PenInputService {
  PenInputService._();

  static final PenInputService instance = PenInputService._();

  static const EventChannel _channel = EventChannel('badnote/pen');

  StreamSubscription<dynamic>? _sub;
  PenHardwareState _current = PenHardwareState.empty;

  PenHardwareState get current => _current;

  bool get isActive => _active;
  bool _active = false;

  final List<VoidCallback> _listeners = <VoidCallback>[];

  /// Notify when native hardware state changes (barrel / pressure / tilt).
  void addListener(VoidCallback listener) => _listeners.add(listener);

  void removeListener(VoidCallback listener) => _listeners.remove(listener);

  void _notifyListeners() {
    for (final l in List<VoidCallback>.of(_listeners)) {
      l();
    }
  }

  int _diagPtr = 0;
  int _diagPen = 0;
  int _diagMouse = 0;
  int _diagMsg = 0;
  int _orPtrFlags = 0;
  int _orPenFlags = 0;
  int _orPenMask = 0;
  int _btnChangeLast = 0;
  int _tiltAbsMax = 0;
  double _pressureMaxSeen = 0;

  String _hex(int v) => '0x${v.toRadixString(16)}';

  String get debugSummary => _active
      ? 'native ptr=$_diagPtr pen=$_diagPen mouse=$_diagMouse msg=${_hex(_diagMsg)}'
          '\n  orPtrFlags=${_hex(_orPtrFlags)} orPenFlags=${_hex(_orPenFlags)}'
          ' mask=${_hex(_orPenMask)} btnChg=$_btnChangeLast tiltMax=$_tiltAbsMax'
          '\n  pressure=${_current.pressureValid ? _current.pressure.toStringAsFixed(3) : "n/a"}'
          ' maxSeen=${_pressureMaxSeen.toStringAsFixed(3)}'
      : 'native: channel silent (no events)';

  void start() {
    if (_sub != null) return;
    try {
      _sub = _channel.receiveBroadcastStream().listen(
        _onEvent,
        onError: (Object _) {},
        cancelOnError: false,
      );
    } catch (_) {}
  }

  void _onEvent(dynamic event) {
    if (event is! Map) return;
    final flags = (event['flags'] as num?)?.toInt() ?? 0;
    final pressureValid = ((event['pressureValid'] as num?)?.toInt() ?? 0) != 0;
    final pressure = (event['pressure'] as num?)?.toDouble() ?? 0.0;
    _current = PenHardwareState(
      barrel: flags & 0x1 != 0,
      inverted: flags & 0x2 != 0,
      eraser: flags & 0x4 != 0,
      tiltX: (event['tiltX'] as num?)?.toDouble() ?? 0.0,
      tiltY: (event['tiltY'] as num?)?.toDouble() ?? 0.0,
      pressure: pressure.clamp(0.0, 1.0),
      pressureValid: pressureValid,
    );
    _diagPtr = (event['diagPtr'] as num?)?.toInt() ?? _diagPtr;
    _diagPen = (event['diagPen'] as num?)?.toInt() ?? _diagPen;
    _diagMouse = (event['diagMouse'] as num?)?.toInt() ?? _diagMouse;
    _diagMsg = (event['diagMsg'] as num?)?.toInt() ?? _diagMsg;
    _orPtrFlags = (event['orPtrFlags'] as num?)?.toInt() ?? _orPtrFlags;
    _orPenFlags = (event['orPenFlags'] as num?)?.toInt() ?? _orPenFlags;
    _orPenMask = (event['orPenMask'] as num?)?.toInt() ?? _orPenMask;
    _btnChangeLast = (event['btnChangeLast'] as num?)?.toInt() ?? _btnChangeLast;
    _tiltAbsMax = (event['tiltAbsMax'] as num?)?.toInt() ?? _tiltAbsMax;
    _pressureMaxSeen =
        (event['pressureMaxSeen'] as num?)?.toDouble() ?? _pressureMaxSeen;
    if (pressureValid && pressure > _pressureMaxSeen) {
      _pressureMaxSeen = pressure;
    }
    _active = true;

    final rawPtr = (event['rawPtrFlags'] as num?)?.toInt() ?? 0;
    final rawPen = (event['rawPenFlags'] as num?)?.toInt() ?? 0;
    final rawMask = (event['rawPenMask'] as num?)?.toInt() ?? 0;
    final btnChange = (event['btnChange'] as num?)?.toInt() ?? 0;
    final key =
        '$rawPtr,$rawPen,$rawMask,$btnChange,${_current.tiltX},${_current.tiltY},'
        '${pressureValid ? pressure.toStringAsFixed(2) : "x"}';
    if (key != _lastPenLogKey) {
      _lastPenLogKey = key;
      PenEventRing.instance.recordHardware(
        barrel: _current.barrel,
        eraser: _current.eraser,
        inverted: _current.inverted,
        tiltX: _current.tiltX,
        tiltY: _current.tiltY,
      );
      BadNoteLog.instance.debug(
        LogSubsystem.penNative,
        'pen_hw',
        fields: {
          'ptrFlags': '0x${rawPtr.toRadixString(16)}',
          'penFlags': '0x${rawPen.toRadixString(16)}',
          'mask': '0x${rawMask.toRadixString(16)}',
          'btnChg': btnChange,
          'tiltX': _current.tiltX,
          'tiltY': _current.tiltY,
          'pressure': pressureValid ? pressure : null,
          'pressureValid': pressureValid,
          'barrel': _current.barrel,
          'eraser': _current.eraser,
          'inverted': _current.inverted,
        },
      );
      DiagnosticLogger.instance.log(
        'PEN ptrFlags=0x${rawPtr.toRadixString(16)} '
        'penFlags=0x${rawPen.toRadixString(16)} '
        'mask=0x${rawMask.toRadixString(16)} btnChg=$btnChange '
        'tilt=${_current.tiltX.toStringAsFixed(0)},${_current.tiltY.toStringAsFixed(0)} '
        'p=${pressureValid ? pressure.toStringAsFixed(3) : "n/a"} '
        'msg=0x${_diagMsg.toRadixString(16)} resolved=0x${flags.toRadixString(16)}',
      );
    }
    _notifyListeners();
  }

  String _lastPenLogKey = '';

  void stop() {
    _sub?.cancel();
    _sub = null;
    _active = false;
    _current = PenHardwareState.empty;
  }
}
