// Copyright (c) 2026, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:_internal' show patch;
import 'dart:_embedder' as embedder;
import 'dart:_wasm';

@patch
class Timer {
  @patch
  static Timer _createTimer(Duration duration, void callback()) {
    return _OneShotTimer(duration, callback);
  }

  @patch
  static Timer _createPeriodicTimer(
    Duration duration,
    void callback(Timer timer),
  ) {
    return _PeriodicTimer(duration, callback);
  }
}

abstract class _Timer implements Timer {
  final int _microseconds;
  int _tick;
  WasmExternRef? _handle;

  @override
  int get tick => _tick;

  @override
  bool get isActive => !_handle.isNull;

  _Timer(Duration duration)
    : _microseconds = duration.inMicroseconds,
      _tick = 0,
      _handle = WasmExternRef.nullRef {
    _schedule();
  }

  void _schedule();
  void _processTick();

  @override
  void cancel() {
    if (!_handle.isNull) {
      embedder.clearSchedule(_handle);
      _handle = WasmExternRef.nullRef;
    }
  }

  static void runtimeCallback(WasmAnyRef timer) {
    final dartTimer = timer.toObject() as _Timer;
    dartTimer._processTick();
  }
}

class _OneShotTimer extends _Timer {
  final void Function() _callback;

  _OneShotTimer(Duration duration, this._callback) : super(duration);

  @override
  void _schedule() {
    _handle = embedder.scheduleOnce(
      _microseconds.toWasmI64(),
      WasmFunction.fromFunction(_Timer.runtimeCallback),
      WasmAnyRef.fromObject(this),
    );
  }

  _processTick() {
    _tick++;
    _handle = WasmExternRef.nullRef;
    _callback();
  }
}

class _PeriodicTimer extends _Timer {
  final void Function(Timer) _callback;
  int _start = 0;

  _PeriodicTimer(Duration duration, this._callback) : super(duration);

  @override
  void _schedule() {
    _start = embedder.currentTimeMicros().toInt();
    _handle = embedder.scheduleRepeated(
      _microseconds.toWasmI64(),
      WasmFunction.fromFunction(_Timer.runtimeCallback),
      WasmAnyRef.fromObject(this),
    );
  }

  @override
  void _processTick() {
    _tick++;
    if (_microseconds > 0) {
      final int duration = embedder.currentTimeMicros().toInt() - _start;
      if (duration > _tick * _microseconds) {
        _tick = duration ~/ _microseconds;
      }
    }
    _callback(this);
  }
}

@patch
class _AsyncRun {
  @patch
  static void _scheduleImmediate(void callback()) {
    embedder.queueMicrotask(
      WasmFunction.fromFunction(_runtimeCallback),
      WasmAnyRef.fromObject(callback),
    );
  }

  static void _runtimeCallback(WasmAnyRef callbackFunction) {
    final function = callbackFunction.toObject() as void Function();
    function();
  }
}
