import 'dart:ffi';

import 'dart:isolate';

import 'app_player.dart';

class VpDartExecuteArgsT extends Struct {
  external Pointer<NativeFunction<Void Function(Pointer<Void>)>> function;

  external Pointer<Void> args;

  external Pointer<Void> syncLock;
}

typedef ProgressListener = void Function(dynamic data);

typedef GPSListener = void Function(dynamic data);

typedef VideoHeadListener = void Function(dynamic data);

typedef DrawListener = void Function(dynamic data);

class AppDart {
  static final AppDart _instance = AppDart._internal();

  factory AppDart() => _instance;

  int Function(Pointer<NativeFunction<Void Function(Pointer<Void>)>>,
      Pointer<VpDartExecuteArgsT>)? _vp_dart_execute;
  int Function(Pointer<Void>, int)? _vp_dart_init;
  void Function(
      Pointer<
          NativeFunction<
              Void Function(Uint64, Uint32, Uint32, Uint32, Uint32, Uint32,
                  Uint32, Uint32, Uint64, Uint64)>>)? _app_player_listener;
  void Function(
          Pointer<
              NativeFunction<
                  Void Function(
                      Uint64, Uint32, Uint32, Float, Float, Float, Float)>>)?
      _app_player_gps_listener;
  void Function(
          Pointer<
              NativeFunction<Void Function(Uint64, Uint32, Uint32, Uint32)>>)?
      _app_player_head_listener;

  void Function(
      Pointer<
          NativeFunction<
              Void Function(Uint64, Uint32, Uint32, Uint32, Float, Float, Float,
                  Float)>>)? _app_player_draw_listener;

  static void _playerCallback(
      int textureId,
      int total_duration,
      int play_duration,
      int cache_progress,
      int loading_status,
      int velocity,
      int focal,
      int version,
      int timestamp,
      int mainFrameCount) {
    final listeners = _listenersByTexture[textureId];
    if (listeners == null) return;
    final data = [
      textureId,
      total_duration,
      play_duration,
      cache_progress,
      loading_status,
      velocity,
      focal,
      version,
      timestamp,
      mainFrameCount,
    ];
    for (final listener in listeners) {
      listener(data);
    }
  }

  static void _playerGPSCallback(
      int textureId,
      int fix_status,
      int satellites_inview,
      double latitude,
      double logitude,
      double speed,
      double coursel) {
    final gpsListeners = _gpsListenersByTexture[textureId];
    if (gpsListeners == null) return;
    final gpsData = [
      textureId,
      fix_status,
      satellites_inview,
      latitude,
      logitude,
      speed,
      coursel,
    ];
    for (final listener in gpsListeners) {
      listener(gpsData);
    }
  }

  static void _playerDrawCallback(
      int textureId,
      int width,
      int height,
      int draw_type,
      double percent_x1,
      double percent_y1,
      double percent_x2,
      double percent_y2) {
    final drawListeners = _drawListenersByTexture[textureId];
    if (drawListeners == null) return;
    final drawData = [
      textureId,
      width,
      height,
      draw_type,
      percent_x1,
      percent_y1,
      percent_x2,
      percent_y2,
    ];
    for (final listener in drawListeners) {
      listener(drawData);
    }
  }

  static void _playerVideoHeadCallback(
      int textureId, int resolution, int channel, int type) {
    final headListeners = _headListenersByTexture[textureId];
    if (headListeners == null) return;
    final headData = [textureId, resolution, channel, type];
    for (final listener in headListeners) {
      listener(headData);
    }
  }

  final ReceivePort receivePort = ReceivePort("vp_dart_port");

  void _receiveOnData(message) {
    if (message is int && _vp_dart_execute != null) {
      final args = Pointer<VpDartExecuteArgsT>.fromAddress(message);
      _vp_dart_execute!(args.ref.function, args);
    }
  }

  AppDart._internal() {
    _vp_dart_execute = AppPlayerController.playerLib
        .lookup<
            NativeFunction<
                Int32 Function(
                    Pointer<NativeFunction<Void Function(Pointer<Void>)>>,
                    Pointer<VpDartExecuteArgsT>)>>("vp_dart_execute")
        .asFunction();
    _vp_dart_init = AppPlayerController.playerLib
        .lookup<NativeFunction<Int32 Function(Pointer<Void>, Int64)>>(
            "vp_dart_init")
        .asFunction();
    _app_player_listener = AppPlayerController.playerLib
        .lookup<
            NativeFunction<
                Void Function(
                    Pointer<
                        NativeFunction<
                            Void Function(
                                Uint64,
                                Uint32,
                                Uint32,
                                Uint32,
                                Uint32,
                                Uint32,
                                Uint32,
                                Uint32,
                                Uint64,
                                Uint64)>>)>>("app_player_listener")
        .asFunction();
    _app_player_gps_listener = AppPlayerController.playerLib
        .lookup<
            NativeFunction<
                Void Function(
                    Pointer<
                        NativeFunction<
                            Void Function(Uint64, Uint32, Uint32, Float, Float,
                                Float, Float)>>)>>("app_player_gps_listener")
        .asFunction();
    _app_player_head_listener = AppPlayerController.playerLib
        .lookup<
            NativeFunction<
                Void Function(
                    Pointer<
                        NativeFunction<
                            Void Function(Uint64, Uint32, Uint32,
                                Uint32)>>)>>("app_player_head_listener")
        .asFunction();

    _app_player_draw_listener = AppPlayerController.playerLib
        .lookup<
            NativeFunction<
                Void Function(
                    Pointer<
                        NativeFunction<
                            Void Function(
                                Uint64,
                                Uint32,
                                Uint32,
                                Uint32,
                                Float,
                                Float,
                                Float,
                                Float)>>)>>("app_player_draw_listener")
        .asFunction();

    receivePort.listen(_receiveOnData, onDone: () {
      print("vp_dart_port done");
    }, onError: (error) {
      print("vp_dart_port error:$error");
    });
    print(
        "data:${NativeApi.initializeApiDLData} nativePort:${receivePort.sendPort.nativePort}");
    _vp_dart_init!(
        NativeApi.initializeApiDLData, receivePort.sendPort.nativePort);
    _app_player_listener!(Pointer.fromFunction(_playerCallback));
    _app_player_gps_listener!(Pointer.fromFunction(_playerGPSCallback));
    _app_player_head_listener!(Pointer.fromFunction(_playerVideoHeadCallback));
    _app_player_draw_listener!(Pointer.fromFunction(_playerDrawCallback));
  }

  static final Map<int, List<ProgressListener>> _listenersByTexture = {};

  void addListener(int textureId, ProgressListener listener) {
    _listenersByTexture.putIfAbsent(textureId, () => []).add(listener);
  }

  void rmvListener(int textureId, ProgressListener listener) {
    _listenersByTexture[textureId]?.remove(listener);
    if (_listenersByTexture[textureId]?.isEmpty ?? false) {
      _listenersByTexture.remove(textureId);
    }
  }

  static final Map<int, List<GPSListener>> _gpsListenersByTexture = {};

  void addGPSListener(int textureId, GPSListener listener) {
    _gpsListenersByTexture.putIfAbsent(textureId, () => []).add(listener);
  }

  void rmvGPSListener(int textureId, GPSListener listener) {
    _gpsListenersByTexture[textureId]?.remove(listener);
    if (_gpsListenersByTexture[textureId]?.isEmpty ?? false) {
      _gpsListenersByTexture.remove(textureId);
    }
  }

  static final Map<int, List<VideoHeadListener>> _headListenersByTexture = {};

  void addVideoHeadListener(int textureId, VideoHeadListener listener) {
    _headListenersByTexture.putIfAbsent(textureId, () => []).add(listener);
  }

  void rmvVideoHeadListener(int textureId, VideoHeadListener listener) {
    _headListenersByTexture[textureId]?.remove(listener);
    if (_headListenersByTexture[textureId]?.isEmpty ?? false) {
      _headListenersByTexture.remove(textureId);
    }
  }

  static final Map<int, List<DrawListener>> _drawListenersByTexture = {};

  void addDrawListener(int textureId, DrawListener listener) {
    _drawListenersByTexture.putIfAbsent(textureId, () => []).add(listener);
  }

  void rmvDrawListener(int textureId, DrawListener listener) {
    _drawListenersByTexture[textureId]?.remove(listener);
    if (_drawListenersByTexture[textureId]?.isEmpty ?? false) {
      _drawListenersByTexture.remove(textureId);
    }
  }
}
