import 'dart:typed_data';
import 'dart:math' as math;

import 'package:fis_common/env/env.dart';
import 'package:fis_common/event/event_type.dart';
import 'package:fis_common/logger/logger.dart';
import 'package:fis_lib_media_rt/implementations/nt_rtmp/values.dart';
import 'package:fis_lib_media_rt/utils/js_lib_import.dart';
import 'package:flutter/material.dart';

import 'streaming_fetcher.dart';

class ShellCameraPlayController {
  StreamingFetcher? _fetcher;
  bool _playing = false;

  /// 拉流地址
  final String url;

  final int width;

  final int height;

  /// 事件 - 收到缓冲片段
  final bufferFragmentReceived = FEventHandler<Uint8List>();

  ShellCameraPlayController(
    this.url, {
    this.width = 640,
    this.height = 480,
  });

  Future<void> init() {
    return JsLibraryImportUtil.import("./wwwroot/js/streaming_fetch.js");
  }

  /// 播放
  void play() {
    if (_playing) return;

    _buildFetcher();
    if (!FPlatform.isPureWeb) {
      _fetcher!.start();
    }

    _playing = true;

    logger.i("$TAG - ShellCamera start");
  }

  /// 停止
  void stop() {
    try {
      _fetcher?.stop();
    } catch (e) {}
    _fetcher = null;
    _playing = false;

    logger.i("$TAG - ShellCamera stop");
  }

  void _buildFetcher() {
    stop();

    _fetcher = StreamingFetcher(
      url,
      StreamingFetcherCallbacks(
        onDone: _onDone,
        onBuffer: _onBuffer,
        onError: _onError,
      ),
    );
  }

  void _onDone() {
    _playing = false;
  }

  void _onBuffer(Uint8List bytes) {
    bufferFragmentReceived.emit(this, bytes);
  }

  void _onError(String message) {
    logger.e("$TAG - StreamingFetcher error: $message");
    stop();
  }
}

class ShellCameraPlayer extends StatefulWidget {
  final ShellCameraPlayController controller;

  const ShellCameraPlayer({super.key, required this.controller});

  @override
  State<StatefulWidget> createState() => _ShellCameraPlayerState();
}

class _ShellCameraPlayerState extends State<ShellCameraPlayer> {
  static const int _RETRY_LIMIT = 10;
  Uint8List? _frameBuffer;
  late Uint8List _bufferArea;
  int _bufferPosition = 0;
  bool _hasError = false;
  int _rgbaSize = 0;
  int _retryCount = 0;

  @override
  void initState() {
    super.initState();

    _rgbaSize = 4 * widget.controller.width * widget.controller.height;
    _bufferArea = Uint8List(_rgbaSize);
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      widget.controller.bufferFragmentReceived.addListener(_onBuffer);
    });
  }

  @override
  void dispose() {
    widget.controller.bufferFragmentReceived.removeListener(_onBuffer);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    Widget child;
    if (_hasError) {
      child = const Center(
        child: Text("Error", style: TextStyle(color: Colors.white)),
      );
    } else if (_frameBuffer != null) {
      child = Image.memory(
        _frameBuffer!,
        fit: BoxFit.contain,
        gaplessPlayback: true,
      );
      // 水平翻转
      child = Transform(
        alignment: Alignment.center,
        transform: Matrix4.rotationY(math.pi),
        child: child,
      );
    } else {
      child = const SizedBox();
    }
    return Container(
      color: Colors.black,
      child: child,
    );
  }

  void _onBufferOnce(Object sender, Uint8List e) {
    _bufferArea.setAll(_bufferPosition, e);
    _bufferPosition += e.length;

    if (_bufferPosition < 8) {
      // 已缓冲数据长度 不够读取帧长度
      return;
    }

    final frameSize = readInt64V2(_bufferArea);
    final onceSize = 8 + frameSize;

    if (_bufferPosition < onceSize) {
      // 已缓冲数据 不够读取下一帧
      return;
    }

    // 提取帧数据
    final frameData = Uint8List(frameSize);
    frameData.setAll(0, _bufferArea.skip(8).take(frameSize));

    _bufferPosition -= onceSize;

    if (mounted) {
      setState(() {
        _frameBuffer = frameData;
      });
    }

    if (_bufferPosition > 0) {
      // // 剩余未读数据，重新往索引0开始写
      // final left = _bufferArea.skip(onceSize).take(_bufferPosition);
      // _bufferArea.setAll(0, left);

      // 丢弃来不及处理的帧，一直追最新帧
      _abandonBufferedFrames(onceSize);
    }
  }

  void _abandonBufferedFrames(int preFrameSize) {
    int abandonSize = preFrameSize;
    while (true) {
      if (_bufferPosition < 8) return;

      final frameSize = readInt64V2(_bufferArea);
      final onceSize = 8 + frameSize;

      if (_bufferPosition < onceSize) {
        break;
      }

      abandonSize += onceSize;
      _bufferPosition -= onceSize;
    }
    logger.i("$TAG - ShellCamera Skip frames, buffer size: $abandonSize");
    _restoreBufferAt(abandonSize);
  }

  void _restoreBufferAt(int index) {
    final left = _bufferArea.skip(index).take(_bufferPosition);
    _bufferArea.setAll(0, left);
  }

  void _onBuffer(Object sender, Uint8List e) {
    try {
      _onBufferOnce(sender, e);
    } catch (e) {
      _handleError();
    }
  }

  void _handleError() {
    widget.controller.stop();

    if (_retryCount > _RETRY_LIMIT) {
      setState(() {
        _hasError = true;
      });
    }

    Future.delayed(const Duration(milliseconds: 800), () {
      widget.controller.play();
    });
    _retryCount++;
  }

  static int readInt64(Uint8List data, [int index = 0]) {
    var intData = data.buffer.asByteData(index, 8);
    return intData.getInt64(0, Endian.little);
  }

  static int readInt64V2(Uint8List data, [int index = 0]) {
    int low = readInt(data, index);
    int high = readInt(data, index + 4);
    int value = high >> 32;
    value |= low;
    return value;
  }

  static int readInt(Uint8List data, [int index = 0]) {
    var intData = data.buffer.asByteData(index, 4);
    return intData.getInt32(0, Endian.little);
  }
}
