import 'package:flutter/material.dart';
import 'package:oc_front/core/services/enum_service.dart';
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:oc_front/models/resources/resources.dart';

class ComputeItem extends AbstractItem<ComputePricing, ComputePartnership, ComputeInstance, ComputeItem> {
    // special attributes
  int? infrastructureEnum;
  String? architecture;

  ComputeItem({
    this.infrastructureEnum,
    this.architecture
  }): super();  

  @override String get topic => "compute";

  @override deserialize(dynamic data) {
    try { data = data as Map<String, dynamic>;
    } catch (e) { return ComputeItem(); }   
    var w = ComputeItem(
      infrastructureEnum: data.containsKey("infrastructure") ? EnumService.get("infrastructure", data["infrastructure"]) : null,
      architecture: data.containsKey("architecture") && data["architecture"] != null ? data["architecture"] : null,
    );
    w.mapFromJSON(data, ComputeInstance());
    if (w.logo != null) { // get image dimensions
      var image = Image.network(w.logo!);
      image.image
        .resolve(const ImageConfiguration())
        .addListener(
            ImageStreamListener(
                (ImageInfo info, bool _) {
                  w.width = info.image.width.toDouble();
                  w.height = info.image.height.toDouble();
                }));
    }
    return w;
  }

  @override
  Map<String, dynamic> infos() {
    return {
      "infrastructure": EnumService.enums["infrastructure"] != null 
      && EnumService.enums["infrastructure"]!["$infrastructureEnum"] != null ? EnumService.enums["infrastructure"]!["$infrastructureEnum"] : infrastructureEnum,
      "architecture": architecture,
    };
  }

  @override Map<String, dynamic> serialize() {
    Map<String, dynamic> obj = infos();
    obj["infrastructure"] = infrastructureEnum;
    obj.addAll(toJSON());
    return obj;
  }
}

class ComputeInstance extends AbstractInstance<ComputePricing, ComputePartnership> {
  String? securityLevel;
  List<String>? powerSources = [];
  double? annualEnergyConsumption;
  Map<String,CPU> cpus = {};
  Map<String,GPU> gpus = {};
  List<ComputeNode> nodes = [];

  ComputeInstance({
    this.securityLevel,
    this.powerSources = const [],
    this.annualEnergyConsumption,
    this.cpus = const {},
    this.gpus = const {},
    this.nodes = const [],
  }): super();

  @override
  Map<String, dynamic> infos() {
    return {
      "security_level": securityLevel,
      "power_sources": powerSources,
      "annual_co2_emissions": annualEnergyConsumption,
      "cpus": toMapJson(cpus),
      "gpus": toMapJson(gpus),
      "nodes": toListJson(nodes),
      "inputs": toListJson(inputs),
      "outputs": toListJson(outputs),
    };
  }

  @override
  ComputeInstance deserialize(json) {
    try { json = json as Map<String, dynamic>;
    } catch (e) { return ComputeInstance(); }
    var w = ComputeInstance(
      securityLevel: json.containsKey("security_level") && json["security_level"] != null ? json["security_level"] : null,
      powerSources: json.containsKey("power_sources") && json["power_sources"] != null ? List<String>.from(json["power_sources"]) : [],
      annualEnergyConsumption: json.containsKey("annual_co2_emissions") && json["annual_co2_emissions"] != null ? json["annual_co2_emissions"] : null,
      //cpus: json.containsKey("cpus") && json["cpus"] != null ? fromMapJson(json["cpus"], CPU()) : {},
      // gpus: json.containsKey("gpus") && json["gpus"] != null ? fromMapJson(json["gpus"], GPU()) : {},
      //nodes: json.containsKey("nodes") && json["nodes"] != null ? fromListJson(json["nodes"], ComputeNode()) : [],
    );
    w.mapFromJSON(json, ComputePartnership());
    return w;
  }

  @override
  Map<String, dynamic> serialize() {
    var obj = infos();
    obj.addAll(toJSON());
    return obj;
  }
}

class ComputePartnership extends AbstractPartnerShip<ComputePricing> {
  Map<String, dynamic> maxAllowedCPUsCores = {};
  Map<String, dynamic> maxAllowedGPUsMemoryGB = {};
  double? maxAllowedRAM;

  ComputePartnership({
    this.maxAllowedCPUsCores = const {},
    this.maxAllowedGPUsMemoryGB = const {},
    this.maxAllowedRAM,
  }): super();

  @override
  ComputePartnership deserialize(json) {
    try { json = json as Map<String, dynamic>;
    } catch (e) { return ComputePartnership(); }
    var w = ComputePartnership(
      maxAllowedCPUsCores: json.containsKey("allowed_cpus") && json["allowed_cpus"] != null ? json["allowed_cpus"] : {},
      maxAllowedGPUsMemoryGB: json.containsKey("allowed_gpus") && json["allowed_gpus"] != null ? json["allowed_gpus"] : {},
      maxAllowedRAM: json.containsKey("allowed_ram") && json["allowed_ram"] != null ? double.parse("${json["allowed_ram"]}") : null,
    );
    w.mapFromJSON(json, ComputePricing());
    return w;
  }

  @override
  Map<String, dynamic> serialize() {
    Map<String, dynamic> obj = {
      "allowed_cpus": maxAllowedCPUsCores,
      "allowed_gpus": maxAllowedGPUsMemoryGB,
      "allowed_ram": maxAllowedRAM,
    };
    obj.addAll(toJSON());
    return obj;
  }
}


class ComputePricing extends AbstractPricing {
  Map<String, dynamic> cpusPrice = {};
  Map<String, dynamic> gpusPrice = {};
  double? ramPrice;

  ComputePricing({
    this.cpusPrice = const {},
    this.gpusPrice = const {},
    this.ramPrice,
  });

  @override ComputePricing deserialize(json) {
    var w = ComputePricing(
      cpusPrice: json.containsKey("cpus") && json["cpus"] != null ? json["cpus"] : {},
      gpusPrice: json.containsKey("gpus") && json["gpus"] != null ? json["gpus"] : {},
      ramPrice: json.containsKey("ram") && json["ram"] != null ? json["ram"] : null,
    );
    w.mapFromJSON(json);
    return w;
  }
  @override
  Map<String, dynamic> serialize() {
    var obj = {
      "cpus": cpusPrice,
      "gpus": gpusPrice,
      "ram": ramPrice,
    };
    obj.addAll(toJSON());
    return obj;
  }
}

class ComputeNode extends SerializerDeserializer<ComputeNode> {
  String? name;
  int? quantity;
  Map<String,dynamic> cpus = {};
  Map<String,dynamic> gpus = {};
  RAM? ram;

  ComputeNode({
    this.cpus = const {},
    this.gpus = const {},
    this.ram,
    this.name,
    this.quantity,
  });

  @override
  ComputeNode deserialize(json) {
    try { json = json as Map<String, dynamic>;
    } catch (e) { return ComputeNode(); }
    return ComputeNode(
      name: json.containsKey("name") && json["name"] != null ? json["name"] : null,
      quantity: json.containsKey("quantity") && json["quantity"] != null ? json["quantity"] : null,
      cpus: json.containsKey("cpus") && json["cpus"] != null ? fromMapJson(json["cpus"], CPU()) : {},
      gpus: json.containsKey("gpus") && json["gpus"] != null ? fromMapJson(json["gpus"], GPU()) : {},
      ram: json.containsKey("ram") && json["ram"] != null ? RAM().deserialize(json["ram"]) : null,
    );
  }

  @override
  Map<String, dynamic> serialize() {
    return {
      "name": name,
      "quantity": quantity,
      "cpus": cpus,
      "gpus": gpus,
      "ram": ram!.serialize(),
    };
  }

}