package de.dhbwstuttgart.server.packet.dataContainers;

import de.dhbwstuttgart.server.packet.dataContainers.serialized.SerialMap;
import de.dhbwstuttgart.typeinference.unify.UnifyContext;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

public class KeyStorage implements ISerializableData {

  /**
   *  Store a unique identifier for every element, so it can be referenced in the json
   */
  protected AtomicInteger identifierCount = new AtomicInteger();
  /**
   *  Store the serialized element per identifier when serializing
   */
  protected SerialMap serializedElements = new SerialMap();
  /**
   *  Store the unserialized element per identifier when unserializing
   */
  protected Map<String, ISerializableData> unserializedElements = new HashMap<>();


  /**
   *  Retrieve or generate a new identifier for a constraint
   */
  public String getIdentifier() {
    return this.identifierCount.incrementAndGet() + "_";
  }

  /**
   *  Checks if the given element identifier belongs to an element that was already serialized
   */
  public boolean isAlreadySerialized(String identifier) {
    return this.serializedElements.containsKey(identifier);
  }

  /**
   *  Checks if the given element identifier belongs to a element that was already unserialized
   */
  public boolean isAlreadyUnserialized(String identifier) {
    return this.unserializedElements.containsKey(identifier);
  }

  /**
   *  Register a serialized element to prevent it from being serialized again
   */
  public void putSerialized(String identifier, SerialMap serializedElement) {
    this.serializedElements.put(identifier, serializedElement);
  }

  /**
   *  Retrieve a serialized element
   */
  public SerialMap getSerialized(String identifier) {
    if (!this.serializedElements.containsKey(identifier)) {
      throw new RuntimeException("No serialized element of identifier " + identifier + " available to get");
    }
    return this.serializedElements.getMap(identifier);
  }

  /**
   *  Register an unserialized element to prevent it from being unserialized again
   */
  public void putUnserialized(String identifier, ISerializableData element) {
    this.unserializedElements.put(identifier, element);
  }

  /**
   *  Retrieve an unserialized element
   */
  public <T extends ISerializableData> T getUnserialized(String identifier, Class<T> target) {
    if (!this.unserializedElements.containsKey(identifier)) {
      throw new RuntimeException("No unserialized element of identifier " + identifier + " available to get");
    }
    var element = this.unserializedElements.get(identifier);
    if (target.isInstance(element)) {
      return (T) element;
    }
    throw new RuntimeException("Failed to get unserialized element from KeyStorage. Expected instance of " +
      target.getName() + " but found " + element.getClass().getName());
  }

  @Override
  public SerialMap toSerial(KeyStorage keyStorage) {
    SerialMap serialized = new SerialMap();
    serialized.put("serializedElements", this.serializedElements);
    return serialized;
  }

  public static KeyStorage fromSerial(SerialMap data, UnifyContext context) {
    var serializedConstraintsData = data.getMap("serializedElements");
    var constraintContext = new KeyStorage();
    for (var entry : serializedConstraintsData.entrySet()) {
      if (entry.getValue() instanceof SerialMap valueMap) {
        constraintContext.putSerialized(entry.getKey(), valueMap);
      }
    }
    return constraintContext;
  }

}
