import 'dart:async';

import 'package:fis_i18n/i18n.dart';
import 'package:fis_jsonrpc/rpc.dart';
import 'package:fis_measure/interfaces/process/workspace/exam_info.dart';
import 'package:fis_measure/interfaces/process/workspace/measure_3d_view_controller.dart';
import 'package:fis_measure/interfaces/process/workspace/measure_controller.dart';
import 'package:fis_measure/process/workspace/application.dart';
import 'package:fis_measure/process/workspace/measure_3d_view_controller.dart';
import 'package:fis_measure/process/workspace/third_part/application.dart';
import 'package:fis_measure/utils/prompt_box.dart';
import 'package:fis_measure/view/player/controller.dart';
import 'package:fis_measure/view/player/enums.dart';
import 'package:fis_measure/view/player/events.dart';

import 'package:fis_measure/interfaces/process/workspace/application.dart';
import 'package:fis_measure/interfaces/process/player/play_controller.dart';
import 'package:fis_measure/interfaces/process/config/style_config.dart';
import 'package:fis_common/event/event_type.dart';
import 'package:fis_ui/index.dart';
import 'package:get/get.dart';
import 'package:vid/us/vid_us_image.dart';

import 'cross_frame.dart';
import 'measure_data_controller.dart';
import 'package:universal_html/html.dart' as html;

typedef MeasureImagesFetchFunc = Future<List<ExamImageInfo>> Function(
    String code);

class MeasureController implements IMeasureController {
  late final ExamInfo _examInfo;
  IPlayerController? _playerController;
  IApplication? _application;
  int _recursive = 0;
  CrossFrameContext? _crossFrameContext;
  final List<int> _crossFrameIndexList = [];

  late bool _enableLoopPlayback;
  final ValueCallback<bool>? onLoopPlaybackEnableChanged;

  late final MeasureImagesFetchFunc _imagesFetchFunc;
  final measureData = Get.find<MeasureDataController>();
  final measure3DViewController = Get.find<Measure3DViewController>();

  MeasureController(
    String code, {
    required MeasureImagesFetchFunc imagesFetchFunc,
    bool enableLoopPlayback = false,
    this.onLoopPlaybackEnableChanged,
  }) {
    _enableLoopPlayback = enableLoopPlayback;
    _examInfo = ExamInfo(code);
    _imagesFetchFunc = imagesFetchFunc;
  }

  @override
  ExamInfo get examInfo => _examInfo;

  @override
  String get currentVidUrl => examInfo.selectedImage.url;

  @override
  bool get canDoMeasure => playerController.status == VidPlayStatus.pause;

  @override
  IPlayerController get playerController => _playerController!;

  @override
  // TODO: implement styleConfiguration
  IStyeConfiguration get styleConfiguration => throw UnimplementedError();

  @override
  IApplication get workingApplication => _application!;

  @override
  var imageLoaded = FEventHandler<ExamImageInfo?>();

  @override
  Future<void> load() async {
    final images = await _imagesFetchFunc.call(examInfo.code);
    examInfo.images.addAll(images);
    examInfo.selectedImageChanged.addListener(_onExamImageSelectedChanged);
    // // 加载第一张图像
    // examInfo.selectedImageIndex = 0;
  }

  @override
  void dispose() {
    examInfo.selectedImageChanged.removeListener(_onExamImageSelectedChanged);
    _playerController?.dispose();
    Get.delete<IApplication>();
    Get.delete<IPlayerController>();
  }

  /// 轮询下载接口
  void _recursivePlayer(Object sender) {
    playerController.load().then((_) async {
      if (playerController.status != VidPlayStatus.ready) {
        if (_recursive <= 2) {
          _recursive += 1;
          _recursivePlayer(sender);
        } else {
          PromptBox.toast(i18nBook.common.networkUnstable.t);
          _recursive = 0;
          _recursivePlayer(sender);
          // throw FlutterError("Vid file load fail.");
        }
      } else {
        playerController.eventHandler.addListener(_onPlayerEnvent);
      }
    });
  }

  void _onExamImageSelectedChanged(Object sender, int index) async {
    final url = currentVidUrl;

    _playerController?.firstFrameLoaded.removeListener(_onFirstFrameLoaded);
    _playerController?.frameUpdated.removeListener(_onFrameUpdated);
    _playerController?.eventHandler.removeListener(_onPlayerEnvent);
    _playerController?.dispose();
    Get.delete<IPlayerController>();
    _windowSetState(index);
    _playerController = Get.put<IPlayerController>(
      VidPlayerController(url)
        ..enableLoop = _enableLoopPlayback
        ..enableLoopChanged.addListener(_onEnableLoopPlaybackChanged),
    );
    playerController.firstFrameLoaded.addListener(_onFirstFrameLoaded);
    playerController.frameUpdated.addListener(_onFrameUpdated);
    playerController.eventHandler.addListener(_onPlayerEnvent);
    _tryLoadPlayer();
  }

  void _onEnableLoopPlaybackChanged(Object _, bool e) {
    _enableLoopPlayback = e;
    onLoopPlaybackEnableChanged?.call(e);
  }

  void _tryLoadPlayer() async {
    int tryCount = 0;
    const limitCount = 5;
    bool loaded = false;
    while (loaded == false) {
      if (limitCount <= tryCount) break;
      if (playerController.disposed) break;

      loaded = await playerController.load();
      if (loaded == false) {
        if (tryCount == 0) {
          PromptBox.toast(i18nBook.common.networkUnstable.t);
        }
      }
      tryCount++;
    }

    if (playerController.disposed) return;

    if (loaded) {
      _onPlayerLoaded();
    }
  }

  void _onPlayerLoaded() {
    _buildApplication();
    // 通知图像加载完成
    imageLoaded.emit(this, examInfo.selectedImage);
  }

  void _onFirstFrameLoaded(Object sender, VidUsImage frame) {
    if (_crossFrameContext != null && _crossFrameContext!.isOver) {
      // 跨图恢复状态,切回原图时,无需重新load首帧;等待延迟load初始帧
      _crossFrameContext = null;
      return;
    }
    workingApplication.loadFrame(frame);
    if (workingApplication.canMeasure) {
      if (workingApplication.frameData!.index != 0) {
        // 非首帧,切换第一个测量项
        final items = measureData.curItemMetaList;
        if (items.isNotEmpty) {
          workingApplication.switchItem(items.first);
        }
      }
    }

    /// 非颈动脉2D模式下需要将 isAdaptiveCarotid2D 置为 false
    if (measure3DViewController.curMeasureMode != MeasureMode.carotid2DMode) {
      workingApplication.isAdaptiveCarotid2D = false;
    }

    workingApplication.updateRenderReady.emit(this, null);
  }

  void _onFrameUpdated(Object sender, VidUsImage frame) {
    if (frame.index != 0) {
      workingApplication.loadFrame(frame);
    }
    workingApplication.updateRenderReady.emit(this, null);
  }

  void _onPlayerEnvent(Object sender, VidPlayerEvent e) {
    if (e is VidPlayerStatusChangeEvent) {
      _application?.canMeasure = e.status == VidPlayStatus.pause;
    }
  }

  Future<void> _buildApplication() async {
    if (_application != null) {
      _application!.imageUrl = currentVidUrl; // 跨图时更新
      final isPass = _checkAndHandleCrossImage();
      // onCrossFramesChanged.call(_crossFrameIndexList); // 更新跨帧指示器
      // _updateCrossFrameAnchors(); //TODO 暂不支持跨图,不保留锚点
      if (!isPass) {
        // 仅切图
        return;
      }
      _disposeCurrentApp();
    }

    final dataChannel = playerController.dataChannel;
    final probe = dataChannel.probe;
    final is3rd = probe.name == Application.C_VID_THIRDPART_NAME;
    IApplication app;
    if (is3rd) {
      app = ThirdPartApplication(probe, dataChannel.extendedData);
    } else {
      app = Application(probe);
    }
    app.imageUrl = currentVidUrl;
    Get.delete<IApplication>();
    _application = Get.put<IApplication>(app);
    app.isSingleFrame = dataChannel.imageCount == 1;
    app.crossFrameTriggered.addListener(_onCrossFrameTriggered);
    app.crossFrameAdded.addListener(_onCrossFrameAdded);
    if (_crossFrameContext != null) {
      // 传递跨帧上下文
      app.crossFrameContext = _crossFrameContext;
    }
  }

  bool _checkAndHandleCrossImage() {
    if (_crossFrameContext == null) {
      return true;
    }
    final switchRst = _crossFrameContext!.switchImage(currentVidUrl);
    if (switchRst) {
      _application!.updateRenderReady.emit(this, null);
      // // 切换到指定图,恢复跨帧指示器记录
      // _crossFrameIndexList.addAll(_crossFrameContext!.recordIndexList);
      // _updateCrossFrameAnchors();
      return false;
    } else {
      // 超过跨图限制,结束之前的测量
      return true;
    }
  }

  void _disposeCurrentApp() {
    _application?.crossFrameTriggered.removeListener(_onCrossFrameTriggered);
    _application?.crossFrameAdded.removeListener(_onCrossFrameAdded);
    _application = null;
  }

  void _onCrossFrameTriggered(_, e) {
    final crossContext = CrossFrameContext(
      currentVidUrl,
      _application!.frameData!,
      _application!.activeMeasureItem!,
    );
    // crossContext.imageCount = _imageCount;
    // crossContext.probe = _probe;
    crossContext.measureAutoOver.addListener(_onCrossMeasureAutoOver);
    _crossFrameContext = crossContext;
    _application!.crossFrameContext = crossContext;
  }

  void _onCrossMeasureAutoOver(_, e) {
    _crossFrameContext!.finish();
    _crossFrameContext = null;
    _clearCrossFrameAnchors();
  }

  void _onCrossFrameAdded(_, int e) {
    if (!_crossFrameIndexList.contains(e)) {
      _crossFrameIndexList.add(e);
      _updateCrossFrameAnchors();
    }
  }

  void _updateCrossFrameAnchors() {
    final controller = playerController as VidPlayerController;
    controller.crossFrameAnchors = _crossFrameIndexList;
  }

  void _clearCrossFrameAnchors() {
    _crossFrameIndexList.clear();
    _updateCrossFrameAnchors();
  }

  void _windowSetState(int index) {
    try {
      final measureData = Get.find<MeasureDataController>();
      var remedicalList = measureData.remedicalList;
      var examCode = examInfo.code;
      if (index > remedicalList.length - 1) {
        return;
      }
      var remedicalInfo = remedicalList[index];
      if (remedicalInfo != null) {
        var searchUrl = html.window.location.search ?? '';
        var remedicalCode = remedicalInfo.remedicalCode;
        var parameters = searchUrl.split('&');
        if (parameters.isNotEmpty) {
          var target = parameters
              .firstWhereOrNull((element) => element.contains("remedicalCode"));
          if (target != null) {
            var paraneter = "remedicalCode=$remedicalCode";
            var newSearchUrl = searchUrl.replaceAll(target, paraneter);
            html.window.history.replaceState('', '', newSearchUrl);
          }
        }
      }
    } catch (e) {}
  }
}