import 'dart:convert';
import 'dart:typed_data';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/util/not_found_exception.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:aitrainer_app/library//network_image_to_byte.dart';
import 'dart:collection';

class ImageCache with Logging {
  static final ImageCache _singleton = ImageCache._internal();
  final LinkedHashMap<String, String> _images = LinkedHashMap();
  final LinkedHashMap<String, bool> _imageMap = LinkedHashMap();
  var downloadSize = 0;

  // Create storage
  final storage = FlutterSecureStorage();

  factory ImageCache() {
    return _singleton;
  }

  ImageCache._internal();

  LinkedHashMap getImageList() => _images;

  void clearImageList() => _images.clear();

  bool existsImageInMap(int id, String url) {
    final String imageKey = generateMd5(url + "_" + id.toString());
    return _imageMap[imageKey] != null && _imageMap[imageKey] == true;
  }

  String? getImageString(int id, String url) {
    final String imageKey = generateMd5(url + "_" + id.toString());
    return _images[imageKey];
  }

  Future<void> putImageToList(int id, String url) async {
    final String imageKey = generateMd5(url + "_" + id.toString());

    // get from storage
    final String imageString = (await getImageAs64BaseString(id, url))!;

    _images[imageKey] = imageString;
    _imageMap[imageKey] = true;

/*       final String imageString = await getImageAs64BaseString(id, url);
      if (imageString != null) {
        _imageMap[imageKey] = imageString;
        _imageDown[imageKey] = true;
      }
    }
    _images[imageKey] = imageString;
    _imageMap[imageKey] = true; */
  }

  Future<void> saveImageToPrefs(String key, String value) async {
    //log(" save the key " + key);
    await storage.write(key: key, value: value);
    return;
  }

  Future<void> emptyPrefs() async {
    await storage.deleteAll();
    return;
  }

  Future<String?> loadImageFromPrefs(String key) async {
    String? value = await storage.read(key: key);
    return value;
  }

  Future<bool> existImageInPrefs(String key) async {
    return await storage.containsKey(key: key);
  }

  // encodes bytes list as string
  static String base64String(Uint8List data) {
    return base64Encode(data);
  }

  // decode bytes from a string
  Image imageFrom64BaseString(String base64String) {
    return Image.memory(
      base64Decode(base64String),
      fit: BoxFit.fill,
    );
  }

  Future<Image?> getImage(int id, String name) async {
    if (name.length == 0) {
      return null;
    }
    final String imageKey = generateMd5(name + "_" + id.toString());
    final String? imageString = await storage.read(key: imageKey);
    if (imageString == null) {
      return null;
    }
    final Image image = imageFrom64BaseString(imageString);

    return image;
  }

  Future<String?> getImageAs64BaseString(int id, String name) async {
    final String imageKey = generateMd5(name + "_" + id.toString());
    String? imageString;
    if (await storage.containsKey(key: imageKey)) {
      //log(" .. get from storage");
      imageString = await storage.read(key: imageKey);
    } else {
      imageString = (await downloadAndSaveImage(id, name))!;
      //log(" .. downloaded");
    }
    return imageString;
  }

  Future<String?> downloadAndSaveImage(int id, String url) async {
    final String imageKey = generateMd5(url + "_" + id.toString());
    if (!await existImageInPrefs(imageKey)) {
      try {
        if (url.contains("http")) {
          log(" ... direct download " + url);
          Uint8List byteImage = await networkImageToByte(url);
          this.downloadSize += byteImage.length;
          final String imageAsString = base64String(byteImage);

          ImageCache().saveImageToPrefs(imageKey, imageAsString);
          return imageAsString;
        }
      } on NotFoundException catch (_) {
        print(url + " not found");
      } on Exception catch (e) {
        print(e);
      }
    } else {
      //log(" .. from storage");
      //await storage.read(key: imageKey);
    }
    return null;
  }

  String generateMd5(String input) {
    String converted = input.replaceAll(RegExp(r'\.'), 'a');
    converted = converted.replaceAll(RegExp(r'\/'), 'b');
    converted = converted.replaceAll(RegExp(r':'), 'c');
    //print("key: " + converted);
    return converted;
  }
}