import 'dart:convert';
import 'dart:io';
import 'dart:ui' as Graphics show instantiateImageCodec, Codec, Image, TextStyle;
import 'dart:ui';
import 'package:guru_utils/http/http_ex.dart';
import 'package:guru_utils/log/log.dart';
import 'package:guru_utils/uri/uri_utils.dart';
import 'package:lottie/lottie.dart';
import 'package:path/path.dart' as path;
import 'package:http/http.dart' as http;
import 'package:flutter/services.dart';
import 'package:image/image.dart';

/// Created by Haoyi on 2022/6/8
///
///
class ImageUtils {
  static Future<Graphics.Image> loadImageFromFile(
    String path, {
    Size? size,
    bool allowUpscaling = true,
  }) async {
    final data = await File(path).readAsBytes();
    Graphics.Codec codec = await Graphics.instantiateImageCodec(data,
        targetWidth: size?.width.toInt(),
        targetHeight: size?.height.toInt(),
        allowUpscaling: allowUpscaling);
    FrameInfo frameInfo = await codec.getNextFrame();
    return frameInfo.image;
  }

  static Future<Graphics.Image> loadImageFromAsset(String path,
      {Size? size, String? package}) async {
    final data = await rootBundle.load(package != null ? "packages/$package/$path" : path);
    Graphics.Codec codec = await Graphics.instantiateImageCodec(data.buffer.asUint8List(),
        targetWidth: size?.width.toInt(), targetHeight: size?.height.toInt());
    FrameInfo frameInfo = await codec.getNextFrame();
    return frameInfo.image;
  }

  static Future<Graphics.Image> loadImageFromHttp(String url,
      {Size? size, Duration timeout = const Duration(seconds: 30)}) async {
    final response = await HttpEx.get(Uri.parse(url)).timeout(timeout);
    final codec = await Graphics.instantiateImageCodec(response.bodyBytes,
        targetWidth: size?.width.toInt(), targetHeight: size?.height.toInt());
    final frameInfo = await codec.getNextFrame();
    return frameInfo.image;
  }

  static Future<LottieDrawable> loadLottieFromFile(String path, {Size? size}) async {
    final lottieComposition = await FileLottie(File(path)).load();
    return LottieDrawable(lottieComposition);
  }

  static Future<Uint8List> toImageData(Graphics.Image image, {format = ImageByteFormat.png}) async {
    final byteData = await image.toByteData(format: format);
    return byteData!.buffer.asUint8List();
  }

  static Future<LottieDrawable> loadLottieFromAsset(String assetName, {Size? size}) async {
    final lottieComposition = await AssetLottie(assetName).load();
    return LottieDrawable(lottieComposition);
  }

  static Future<Graphics.Image?> loadImage(String uri,
      {Size? size, Duration timeout = const Duration(seconds: 30)}) async {
    try {
      switch (UriUtils.parseUriType(uri)) {
        case UriType.assets:
          return await loadImageFromAsset(uri, size: size);
        case UriType.http:
          Log.d("loadImageFromHttp!! $uri");
          return await loadImageFromHttp(uri, size: size, timeout: timeout);
        case UriType.file:
          return await loadImageFromFile(uri, size: size);
        default:
          return null;
      }
    } catch (error) {
      Log.e("loadImage!! $error");
      return null;
    }
  }

  static Future<File> saveBase64ImageData(File file, String base64Data) async {
    final bytes = const Base64Decoder().convert(base64Data);
    await file.writeAsBytes(bytes);
    return file;
  }

  static Future<Uint8List?> loadImageData(String uri,
      {Size? size,
      Duration timeout = const Duration(seconds: 30),
      format = ImageByteFormat.png}) async {
    final image = await loadImage(uri, size: size, timeout: timeout);
    return image != null ? toImageData(image) : null;
  }

  static Future<File> toJpegAndSaveFile(File srcFile, File dstFile, {int quality = 100}) async {
    final image = await loadImageFromFile(srcFile.path);
    final data = await image.toByteData(format: ImageByteFormat.png);
    final img = decodeImage(data!.buffer.asUint8List());
    return dstFile.writeAsBytes(encodeJpg(img!, quality: quality), flush: true);
  }

  static Future<File> toJpegAndSaveFileFromImage(Graphics.Image image, File imageFile) async {
    final data = await image.toByteData(format: ImageByteFormat.png);
    final img = decodeImage(data!.buffer.asUint8List());
    return imageFile.writeAsBytes(encodeJpg(img!), flush: true);
  }

  static Future<File> saveImageToFile(Graphics.Image image, File imageFile) async {
    final data = await image.toByteData(format: ImageByteFormat.png);
    return imageFile.writeAsBytes(data!.buffer.asUint8List(), flush: true);
  }

  static Rect getBoundary(Graphics.Image image) {
    return Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble());
  }

  static recycleImage(Graphics.Image? image) {
    try {
      image?.dispose();
    } catch (_) {}
  }
}
