content: import 'dart:async';
  import 'dart:io';
  import 'dart:math';
  import 'dart:typed_data';
  import 'dart:ui';
  import 'package:extended_image/extended_image.dart';
  import 'package:flutter/material.dart';
  import 'package:flutter_riverpod/flutter_riverpod.dart';
  import 'package:intl/intl.dart';
  import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
  import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
  import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
  import 'package:mangayomi/utils/headers.dart';
  import 'package:mangayomi/utils/reg_exp_matcher.dart';
  import 'package:path/path.dart';
  import 'package:path_provider/path_provider.dart';
  import 'package:path/path.dart' as p;
  
  extension FileFormatter on num {
    String formattedFileSize({bool base1024 = true}) {
      final base = base1024 ? 1024 : 1000;
      if (this <= 0) return "0";
      final units = ["B", "kB", "MB", "GB", "TB"];
      int digitGroups = (log(this) / log(base)).round();
      return "${NumberFormat("#,##0.#").format(this / pow(base, digitGroups))} ${units[digitGroups]}";
    }
  }
  
  extension LetExtension<T> on T {
    R let<R>(R Function(T) block) {
      return block(this);
    }
  }
  
  extension MedianExtension on List<int> {
    int median() {
      var middle = length ~/ 2;
      if (length % 2 == 1) {
        return this[middle];
      } else {
        return ((this[middle - 1] + this[middle]) / 2).round();
      }
    }
  
    int arithmeticMean() {
      return isNotEmpty ? (reduce((e1, e2) => e1 + e2) / length).round() : 0;
    }
  }
  
  extension ImageProviderExtension on ImageProvider {
    Future<Uint8List?> getBytes(
      BuildContext context, {
      ImageByteFormat format = ImageByteFormat.png,
    }) async {
      final imageStream = resolve(createLocalImageConfiguration(context));
      final Completer<Uint8List?> completer = Completer<Uint8List?>();
      final ImageStreamListener listener = ImageStreamListener((
        imageInfo,
        synchronousCall,
      ) async {
        final bytes = await imageInfo.image.toByteData(format: format);
        if (!completer.isCompleted) {
          completer.complete(bytes?.buffer.asUint8List());
        }
      });
      imageStream.addListener(listener);
      final imageBytes = await completer.future;
      imageStream.removeListener(listener);
      return imageBytes;
    }
  }
  
  extension UChapDataPreloadExtensions on UChapDataPreload {
    Future<Uint8List?> get getImageBytes async {
      Uint8List? imageBytes;
      if (archiveImage != null) {
        imageBytes = archiveImage;
      } else if (isLocale == true && directory != null && index != null) {
        imageBytes = File(
          p.join(directory!.path, "${padIndex(index!)}.jpg"),
        ).readAsBytesSync();
      } else {
        File? cachedImage;
        if (pageUrl != null) {
          cachedImage = await _getCachedImageFile(pageUrl!.url);
          if (cachedImage == null) {
            await Future.delayed(const Duration(seconds: 3));
            cachedImage = await _getCachedImageFile(pageUrl!.url);
          }
        }
        imageBytes = cachedImage?.readAsBytesSync();
      }
      return imageBytes;
    }
  
    ImageProvider<Object> getImageProvider(
      WidgetRef ref,
      bool showCloudFlareError,
    ) {
      final data = this;
  
      if (data.isTransitionPage) {
        return const AssetImage('assets/transparent.png')
            as ImageProvider<Object>;
      }
  
      final isLocale = data.isLocale!;
      final archiveImage = data.archiveImage;
      final cropBorders = ref.watch(cropBordersStateProvider);
      return cropBorders && data.cropImage != null
          ? ExtendedMemoryImageProvider(data.cropImage!)
          : (isLocale
                    ? archiveImage != null
                          ? ExtendedMemoryImageProvider(archiveImage)
                          : ExtendedFileImageProvider(
                              File(
                                p.join(
                                  data.directory!.path,
                                  "${padIndex(data.index!)}.jpg",
                                ),
                              ),
                            )
                    : CustomExtendedNetworkImageProvider(
                        data.pageUrl!.url.trim().trimLeft().trimRight(),
                        cache: true,
                        cacheMaxAge: const Duration(days: 7),
                        showCloudFlareError: showCloudFlareError,
                        imageCacheFolderName: "cacheimagemanga",
                        headers: {
                          ...data.pageUrl!.headers ?? {},
                          ...ref.watch(
                            headersProvider(
                              source: data.chapter!.manga.value!.source!,
                              lang: data.chapter!.manga.value!.lang!,
                              sourceId: data.chapter!.manga.value!.sourceId,
                            ),
                          ),
                        },
                      ))
                as ImageProvider<Object>;
    }
  }
  
  Future<File?> _getCachedImageFile(String url, {String? cacheKey}) async {
    try {
      final String key = cacheKey ?? keyToMd5(url);
      final Directory cacheImagesDirectory = Directory(
        join(
          (await getTemporaryDirectory()).path,
          'Mangayomi',
          'cacheimagemanga',
        ),
      );
      if (cacheImagesDirectory.existsSync()) {
        await for (final FileSystemEntity file in cacheImagesDirectory.list()) {
          if (file.path.endsWith(key)) {
            return File(file.path);
          }
        }
      }
    } catch (_) {
      return null;
    }
    return null;
  }
  