content: import 'dart:convert';
  import 'dart:js' as js;
  
  import 'package:flutter/foundation.dart';
  import 'package:flutter/services.dart';
  
  import 'package:flutter_hbb/models/model.dart' as model;
  
  class CursorData {
    final String key;
    final String url;
    final double hotX;
    final double hotY;
    final int width;
    final int height;
  
    CursorData({
      required this.key,
      required this.url,
      required this.hotX,
      required this.hotY,
      required this.width,
      required this.height,
    });
  }
  
  /// The cursor manager
  class CursorManager {
    final Map<String, CursorData> _cursors = <String, CursorData>{};
    String latestKey = '';
  
    CursorManager._();
    static CursorManager instance = CursorManager._();
  
    Future<void> registerCursor(CursorData data) async {
      _cursors[data.key] = data;
    }
  
    Future<void> deleteCursor(String key) async {
      _cursors.remove(key);
    }
  
    Future<void> setSystemCursor(String key) async {
      if (latestKey == key) {
        return;
      }
      latestKey = key;
  
      final CursorData? cursorData = _cursors[key];
      if (cursorData != null) {
        js.context.callMethod('setByName', [
          'cursor',
          jsonEncode({
            'url': cursorData.url,
            'hotx': cursorData.hotX.toInt(),
            'hoty': cursorData.hotY.toInt(),
          })
        ]);
      }
    }
  
    Future<void> resetSystemCursor() async {
      latestKey = '';
      js.context.callMethod('setByName', ['cursor', 'auto']);
    }
  }
  
  class FlutterCustomMemoryImageCursor extends MouseCursor {
    final String key;
    const FlutterCustomMemoryImageCursor({required this.key});
  
    @override
    MouseCursorSession createSession(int device) =>
        _FlutterCustomMemoryImageCursorSession(this, device);
  
    @override
    String get debugDescription =>
        objectRuntimeType(this, 'FlutterCustomMemoryImageCursor');
  }
  
  class _FlutterCustomMemoryImageCursorSession extends MouseCursorSession {
    _FlutterCustomMemoryImageCursorSession(
        FlutterCustomMemoryImageCursor cursor, int device)
        : super(cursor, device);
  
    @override
    FlutterCustomMemoryImageCursor get cursor =>
        super.cursor as FlutterCustomMemoryImageCursor;
  
    @override
    Future<void> activate() async {
      await CursorManager.instance.setSystemCursor(cursor.key);
    }
  
    @override
    void dispose() {}
  }
  
  deleteCustomCursor(String key) => CursorManager.instance.deleteCursor(key);
  resetSystemCursor() => CursorManager.instance.resetSystemCursor();
  
  MouseCursor buildCursorOfCache(
      model.CursorModel cursor, double scale, model.CursorData? cache) {
    if (cache == null) {
      return MouseCursor.defer;
    } else {
      final key = cache.updateGetKey(scale);
      if (!cursor.cachedKeys.contains(key)) {
        // data should be checked here, because it may be changed after `updateGetKey()`
        final data = cache.data;
        if (data == null) {
          return MouseCursor.defer;
        }
        debugPrint(
            "Register custom cursor with key $key (${cache.hotx},${cache.hoty})");
        CursorManager.instance.registerCursor(CursorData(
            key: key,
            url: 'data:image/rgba;base64,${base64Encode(data)}',
            width: (cache.width * cache.scale).toInt(),
            height: (cache.height * cache.scale).toInt(),
            hotX: cache.hotx,
            hotY: cache.hoty));
        cursor.addKey(key);
      }
      return FlutterCustomMemoryImageCursor(key: key);
    }
  }
  