import 'dart:io';
import 'dart:ui' as Graphics show instantiateImageCodec, Codec, Image;

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:guru_utils/log/log.dart';
import 'package:guru_utils/uri/uri_utils.dart';
import 'package:guru_widgets/guru_widgets.dart';
import 'package:lottie/lottie.dart';

import 'composition_provider.dart';
import 'lottie_controller.dart';

/// Created by Haoyi on 2020/7/28

// 这里不使用lottie库中自身的LottieImageProviderFactory是因为，在库中，一旦识别了某个图
// 将没有办法动态替换，这里通过另一种办法来达到动态替换的效果
typedef LottieImageProvider = LottieImageBuilder? Function(LottieImageAsset);

typedef LottieImageBuilder = Future<Graphics.Image?> Function();

enum LottieAction {
  none,
  forward,
  reverse,
  repeat,
  forwardAndRepeat,
}

typedef LottieUpdateCallback = void Function(double progress);

class LottieWidget extends StatefulWidget {
  final String uri;
  final LottieAction action;
  final double? width;
  final double? height;
  final double repeatMin;
  final double repeatMax;
  final Duration duration;
  final LottieUpdateCallback? updateCallback;
  final VoidCallback? completedCallback;
  final LottieImageProvider? imageProviderFactory;
  final LottieDelegates? lottieDelegates;
  final Duration period;
  final LottieComposition? composition;
  final LottieController? lottieController;
  final bool autoDispose;
  final Duration delayed;
  final bool manualStart;
  final String? package;

  const LottieWidget(
      {Key? key,
      required this.uri,
      this.width,
      this.height,
      this.action = LottieAction.none,
      this.duration = const Duration(milliseconds: 800),
      this.imageProviderFactory,
      this.repeatMin = 0.0,
      this.repeatMax = 1.0,
      this.lottieDelegates,
      this.updateCallback,
      this.period = const Duration(milliseconds: 800),
      this.composition,
      this.completedCallback,
      this.lottieController,
      this.autoDispose = true,
      this.manualStart = false,
      this.delayed = const Duration(milliseconds: 0),
      this.package})
      : assert(uri != null),
        super(key: key);

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

class LottieState extends State<LottieWidget> with SingleTickerProviderStateMixin {
  late LottieController defaultLottieController;

  void onLoaded(LottieComposition composition) async {
    Log.d("composition.images:${composition.images}", tag: "lottie");

    for (LottieImageAsset lottieImageAsset in composition.images.values) {
      final builder = widget.imageProviderFactory?.call(lottieImageAsset);
      if (builder != null) {
        lottieImageAsset.loadedImage = await builder();
      }
    }
    if (!widget.manualStart) {
      switch (widget.action) {
        case LottieAction.forward:
          defaultLottieController.forward(from: 0);
          break;
        case LottieAction.reverse:
          defaultLottieController.reverse(from: 1);
          break;
        case LottieAction.repeat:
          defaultLottieController.repeat();
          break;
        case LottieAction.forwardAndRepeat:
          defaultLottieController.forward(from: 0);
          break;
        case LottieAction.none:
          break;
      }
    }
  }

  void forward({double from = 0.0}) {
    defaultLottieController.forward(from: from);
  }

  void stop({bool canceled = true}) {
    defaultLottieController.stop(canceled: canceled);
  }

  void repeat() {
    defaultLottieController.repeat();
  }

  Widget _buildLottie(String uri) {
    final uriType = UriUtils.parseUriType(uri);
    if (widget.composition != null) {
      return LottieBuilder(
          lottie: CompositionProvider(widget.composition!),
          onLoaded: onLoaded,
          controller: defaultLottieController.animationController,
          delegates: widget.lottieDelegates);
    }
    switch (uriType) {
      case UriType.assets:
        return Lottie.asset(uri,
            controller: defaultLottieController.animationController,
            onLoaded: onLoaded,
            width: widget.width,
            height: widget.height,
            delegates: widget.lottieDelegates,
            fit: BoxFit.fill,
            alignment: Alignment.center,
            package: widget.package);
      case UriType.file:
        return Lottie.file(File(uri),
            controller: defaultLottieController.animationController,
            width: widget.width,
            height: widget.height,
            onLoaded: onLoaded,
            delegates: widget.lottieDelegates);
      case UriType.http:
        return Lottie.network(uri,
            controller: defaultLottieController.animationController,
            width: widget.width,
            height: widget.height,
            onLoaded: onLoaded,
            delegates: widget.lottieDelegates);
      default:
        return const EmptySpacer();
    }
  }

  @override
  void initState() {
    super.initState();
    Log.v("initState: ${widget.uri}");
    if (widget.lottieController != null) {
      defaultLottieController = widget.lottieController!;
    } else {
      defaultLottieController = DefaultLottieController(this,
          repeatMin: widget.repeatMin,
          repeatMax: widget.repeatMax,
          lottieAction: widget.action,
          duration: widget.duration,
          period: widget.period,
          updateCallback: widget.updateCallback,
          completedCallback: widget.completedCallback,
          delayDuration:widget.delayed);
    }
  }

  @override
  void dispose() {
    if (widget.autoDispose) {
      defaultLottieController.dispose();
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return _buildLottie(widget.uri);
  }
}
