import 'dart:async';
import 'dart:io';
import 'dart:math';

import 'package:camera/camera.dart';
import 'package:get/get.dart';
import 'package:myhr_facescan/active_liveness/enum.dart';
import 'package:myhr_facescan/active_liveness/face_gesture_verifier.dart';
import 'package:myhr_facescan/models/template.dart';
import 'package:myhr_facescan/luxand_controller.dart';
import 'package:myhr_facescan/utils/camera.dart';
import 'package:myhr_facescan/utils/throttler.dart';

class ActiveLivenessController extends GetxController {
  ActiveLivenessController({
    required String licenseKey,
    required void Function(bool, TemplateModel?) onFinish,
    required int imageStreamMilliSecond,
    required int failAcceptableInSecond,
  })  : _imageStreamMilliSecond = imageStreamMilliSecond,
        _onFinish = onFinish,
        _licenseKey = licenseKey {
    _throttler = Throttler(milliSeconds: _imageStreamMilliSecond);
    _luxand = Get.put(LuxandController(licenseKey: _licenseKey));
    _commands = getCommands();
    _failRatio = 1 ~/ (_imageStreamMilliSecond / 1000) * failAcceptableInSecond;
  }

  CameraController? camController;
  double previewScale = 0;
  late Throttler _throttler;
  int failFrame = 0;
  // StreamSubscription<int> _timer = null;

  final String _licenseKey;
  final void Function(bool, TemplateModel?) _onFinish;
  final int _imageStreamMilliSecond;
  late List<EFaceGesture> _commands;
  late int _failRatio;

  var currentCommand = 0.obs;
  var isCameraReady = false.obs;

  var isWaitNextCommand = false.obs;
  late LuxandController _luxand;
  final verifier = FaceGestureVerifier();

  @override
  void onInit() async {
    super.onInit();

    await _luxand.initialize();
    await initCamera();

    camController!.initialize().then((_) {
      isCameraReady.value = true;
      previewScale = getCameraPreviewScale(camController!.value.aspectRatio);

      // Only open and close camera in iOS for low-tier device
      if (Platform.isIOS) {
        // _timer = Stream.periodic(const Duration(milliseconds: 500), (v) => v)
        //     .listen((count) async {
        //   _throttler.run(() async {
        //     controller.startImageStream((image) async {});

        //     Future.delayed(const Duration(milliseconds: 50), () async {
        //       await controller.stopImageStream();
        //     });
        //   });
        // });
      } else {
        camController!.startImageStream(_handleCameraAndroid);
      }
    });
  }

  initCamera() async {
    camController = await getCameraController();
  }

  @override
  void onClose() async {
    super.onClose();
    await _clearResource();
  }

  get commandName {
    switch (_commands[currentCommand.value]) {
      case EFaceGesture.lookStraight:
        return 'มองตรง';
      case EFaceGesture.turnLeft:
        return 'หันไปทางซ้าย';
      case EFaceGesture.turnRight:
        return 'หันไปทางขวา';
      case EFaceGesture.lookUp:
        return 'เงยหน้า';
      case EFaceGesture.lookDown:
        return 'ก้มหน้า';
      case EFaceGesture.smile:
        return 'ยิ้ม';
      default:
        return 'N/A';
    }
  }

  _clearResource() async {
    isCameraReady.value = false;
    await camController?.stopImageStream();
    await camController?.dispose();

    await _luxand.freeResource();
  }

  _handleCameraAndroid(CameraImage image) async {
    _throttler.run(() async {
      if (isWaitNextCommand.value) {
        return;
      }

      var command = _commands[currentCommand.value];
      var isExpression = command == EFaceGesture.smile;
      var attr = await _luxand.getFaceAttribute(image, isExpression);

      if (attr.isEmpty || (attr.isNotEmpty && attr[0] == 0 && attr[1] == 0)) {
        await _handleFail();
        return;
      }

      var isMatch = verifier.isMatch(attr[0], attr[1], command);

      if (isMatch) {
        await approveCommand();
      }
    });
  }

  _handleFail() async {
    failFrame++;

    if (failFrame >= _failRatio) {
      _onFinish(false, null);
      await _clearResource();
    }
  }

  approveCommand() async {
    if (currentCommand.value + 1 >= _commands.length) {
      _onFinish(true, null);
      await _clearResource();
    } else {
      currentCommand.value += 1;
    }

    isWaitNextCommand.value = true;

    Timer(const Duration(seconds: 2), () => isWaitNextCommand.value = false);
  }

  List<EFaceGesture> getCommands() {
    var result = <EFaceGesture>[];

    while (true) {
      if (result.contains(EFaceGesture.lookUp) &&
          result.contains(EFaceGesture.lookDown) &&
          result.contains(EFaceGesture.turnLeft) &&
          result.contains(EFaceGesture.turnRight) &&
          result.contains(EFaceGesture.smile)) {
        break;
      }

      do {
        var cm = EFaceGesture.fromInt(Random().nextInt(6));

        if (!result.contains(cm) && cm != EFaceGesture.lookStraight) {
          result.add(cm);
          break;
        }
      } while (true);
    }

    result.insert(2, EFaceGesture.lookStraight);

    return result;
  }
}
