import 'dart:async';

import 'package:fis_common/event/event_type.dart';
import 'package:fis_common/func/func_proxy.dart';
import 'package:fis_measure/interfaces/process/player/play_controller.dart';
import 'package:fis_vid/data_host/data_host.dart';
import 'package:fis_vid/processors/index.dart';
import 'package:flutter/foundation.dart';
import 'package:vid/us/vid_us_image.dart';

import 'enums.dart';
import 'events.dart';

/// Vid播放器控制器
class VidPlayerController extends ChangeNotifier implements IPlayerController {
  /// Vid播放器控制器
  ///
  /// [dataHost] Vid文件宿主
  VidPlayerController({
    required VidDataHost dataHost,
  }) {
    _dataHost = dataHost;
  }
  // ignore: constant_identifier_names
  static const _CAN_PLAY_STATUS_ARR = [
    VidPlayStatus.ready,
    VidPlayStatus.pause
  ];

  // ignore: constant_identifier_names
  static const _HAS_VIEW_STATUS_ARR = [VidPlayStatus.play, VidPlayStatus.pause];

  @override
  var eventHandler = FEventHandler<VidPlayerEvent>();

  late final VidDataHost _dataHost;

  _PlayAssistant? _playAssistant;
  double _speed = 1.0;
  VidPlayStatus _status = VidPlayStatus.init;
  int _frameIndex = -1;
  VidUsImage? _frame;
  final Map<Type, VidFrameProcessor> _processors = {};

  /// Current play speed
  double get currentSpeed => _speed;

  @override
  VidPlayStatus get status => _status;

  /// Whether the player is playing
  bool get playing => status == VidPlayStatus.play;

  /// Whether the player can play
  bool get canPlay => _CAN_PLAY_STATUS_ARR.contains(status);

  /// Whether the player should has view
  bool get hasView => _HAS_VIEW_STATUS_ARR.contains(status);

  /// Current viewed frame index
  int get currentFrameIndex => _frameIndex;

  /// Total frames count of current vid
  int get totalFramesCount => _dataHost.frameCount;

  bool get isSingleFrame => totalFramesCount == 1;

  /// 是否播放结束
  bool get isEndOfPlay => currentFrameIndex == totalFramesCount - 1;

  /// Set play speed [0.5~2]
  ///
  /// [speed] speed value [0.5~2]
  void setSpeed(double speed) {
    if (speed < 0.5 || speed > 2) return;

    _speed = speed;
    eventHandler.emit(this, VidPlayerSpeedChangeEvent(currentSpeed));
  }

  Future<void> load() async {
    final info = await _dataHost.load();
    if (info == null) {
      _setStatus(VidPlayStatus.loadFail);
    } else {
      _setStatus(VidPlayStatus.ready);
    }
  }

  @override
  void setLoadState(bool loaded) {
    _setStatus(loaded ? VidPlayStatus.ready : VidPlayStatus.loadFail);
  }

  @override
  void play() {
    if (playing) return;
    if (!canPlay) return;

    if (isEndOfPlay) {
      _frameIndex = -1;
    }
    if (isSingleFrame) {
      gotoFrame(0);
      _setStatus(VidPlayStatus.pause);
    } else {
      _playAssistant ??= _PlayAssistant(this);
      _playAssistant!.play();

      _setStatus(VidPlayStatus.play);
    }
  }

  @override
  void pause() {
    if (!playing) return;

    _playAssistant?.pause();
    _setStatus(VidPlayStatus.pause);
  }

  /// Pause and view next frame
  Future<bool> gotoNextFrame() {
    pause();
    return gotoFrame(currentFrameIndex + 1);
  }

  /// Pause and view prev frame
  Future<bool> gotoPrevFrame() {
    pause();
    return gotoFrame(currentFrameIndex - 1);
  }

  /// View target indexed frame
  ///
  /// [index] frame index
  Future<bool> gotoFrame(int index) async {
    if (index < 0 || index >= totalFramesCount) return false;

    _frameIndex = index;
    _updateFrame();
    return true;
  }

  /// Set frame brightness
  ///
  /// [value] brightness value
  void setBrightness(int value) {
    final processor =
        _processors[VidBrightnessProcessor] as VidBrightnessProcessor?;
    final originVal = processor?.brightness;
    if (originVal == value) return;

    if (value == 0) {
      _processors.remove(VidBrightnessProcessor);
    } else {
      _processors[VidBrightnessProcessor] = VidBrightnessProcessor(value);
    }
    eventHandler.emit(this, VidPlayerBrightnessChangeEvent(value));
    _updateFrame();
  }

  /// Set frame contrast
  ///
  /// [value] contrast value
  void setContrast(int value) {
    final processor =
        _processors[VidContrastProcessor] as VidContrastProcessor?;
    final originVal = processor?.contrast;
    if (originVal == value) return;

    if (value == 0) {
      _processors.remove(VidContrastProcessor);
    } else {
      _processors[VidContrastProcessor] = VidContrastProcessor(value);
    }
    eventHandler.emit(this, VidPlayerContrastChangeEvent(value));
    _updateFrame();
  }

  /// 重置图像增益
  void resetTone() {
    setBrightness(0);
    setContrast(0);
  }

  T? getProcessor<T extends VidFrameProcessor>() {
    return _processors[T] as T?;
  }

  void _updateFrame() {
    _handleUpdateFrame
        .throttle(
          timeout: _playAssistant?._playIntervalMillSeconds ?? 200,
        )
        .call();
  }

  Future<void> _handleUpdateFrame() async {
    _frame = await _dataHost.getFrame(
      currentFrameIndex,
      processors: _processors.values.toList(),
    );
    eventHandler.emit(
      this,
      VidPlayerFrameIndexChangeEvent(
        currentFrameIndex,
        _frame!.imageData,
        _frame!.width,
        _frame!.height,
      ),
    );
  }

  void _setStatus(VidPlayStatus value) {
    _status = value;
    _notifyStatus();
  }

  void _notifyStatus() {
    eventHandler.emit(this, VidPlayerStatusChangeEvent(status));
  }

  void _stop({bool needNotify = true}) {
    _playAssistant?.pause();
    if (needNotify) {
      _setStatus(VidPlayStatus.stop);
    }
  }

  @override
  void dispose() {
    _stop(needNotify: false);
    eventHandler.dispose();
    super.dispose();
  }

  /// 已禁用，请通过eventHandler监听事件
  @override
  void addListener(VoidCallback listener) {
    throw UnsupportedError(
        "method `addListener` has been limited.Pls use `eventHandler.addListener`.");
  }

  /// 已禁用，请通过eventHandler监听事件
  @override
  void removeListener(VoidCallback listener) {
    throw UnsupportedError(
        "method `removeListener` has been limited.Pls use `eventHandler.removeListener`.");
  }
}

class _PlayAssistant {
  _PlayAssistant(this.owner);

  final VidPlayerController owner;

  bool _ready = false;

  late double _frameRate;

  int get _playInterval => 1000 * 1000 ~/ _frameRate;

  int get _playIntervalMillSeconds => _playInterval ~/ 1000.0;

  Timer? _timer;

  void play() {
    if (!_ready) {
      _prepare();
    }
    if (_timer != null) {
      pause();
    }
    final duration = Duration(microseconds: _playInterval);
    // final duration = const Duration(milliseconds: 1000 ~/ 10);
    _timer = Timer.periodic(duration, (timer) {
      owner.gotoFrame(owner.currentFrameIndex + 1);
      if (owner.currentFrameIndex == owner.totalFramesCount - 1) {
        owner.pause();
      }
    });
  }

  void pause() {
    _timer?.cancel();
    _timer = null;
  }

  void _prepare() {
    _frameRate = owner._dataHost.probe.frameRate;
    _ready = true;
  }
}
