package de.dhbwstuttgart.typeinference.unify;

import de.dhbwstuttgart.server.packet.dataContainers.ISerializableData;
import de.dhbwstuttgart.server.packet.dataContainers.KeyStorage;
import de.dhbwstuttgart.server.packet.dataContainers.serialized.SerialList;
import de.dhbwstuttgart.server.packet.dataContainers.serialized.SerialMap;
import de.dhbwstuttgart.server.packet.dataContainers.serialized.SerialValue;
import de.dhbwstuttgart.typeinference.unify.model.PlaceholderType;
import java.util.ArrayList;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Calculate unique placeholder names
 */
public class PlaceholderRegistry implements ISerializableData {

  private final Set<String> existingPlaceholders = ConcurrentHashMap.newKeySet();
  private final AtomicInteger placeholderCount = new AtomicInteger();
  public ArrayList<PlaceholderType> UnifyTypeFactory_PLACEHOLDERS = new ArrayList<>();

  /**
   * Add a placeholder into the list of existing ones, as soon as a new PlaceholderType is created
   *
   * @param placeholderName The placeholder to add
   */
  public void addPlaceholder(String placeholderName) {
    this.existingPlaceholders.add(placeholderName);
  }

  /**
   * Generate a random placeholder name, that is unique to this context
   *
   * @return  The generated name
   */
  public String generateFreshPlaceholderName() {
    String name;
    do {
      int pc = placeholderCount.incrementAndGet();
      name = getUppercaseTokenFromInt(pc);
    }
    while (existingPlaceholders.contains(name));
    this.addPlaceholder(name);
    return name;
  }

  public PlaceholderRegistry deepClone() {
    PlaceholderRegistry pr2 = new PlaceholderRegistry();
    this.existingPlaceholders.forEach(pr2::addPlaceholder);
    pr2.UnifyTypeFactory_PLACEHOLDERS.addAll(this.UnifyTypeFactory_PLACEHOLDERS);
    pr2.placeholderCount.set(this.placeholderCount.get());
    return pr2;
  }

  /**
   * Generate a token that consists of uppercase letters and contains the given prefix and suffix from the value i
   *
   * @param i       The value that will be represented as a token
   * @return        The generated token
   */
  private String getUppercaseTokenFromInt(int i) {
    StringBuilder sb = new StringBuilder();
    while (i >= 0) {
      sb.append((char)(i % 26 + 65));
      i = i / 26 - 1;
    }
    //sb.append(suffix);
    return sb.toString();
  }

  @Override
  public String toString() {
    return this.existingPlaceholders.toString();
  }

  @Override
  public SerialMap toSerial(KeyStorage keyStorage) {
    SerialMap serialized = new SerialMap();
    serialized.put("ph", new SerialValue<>(new ArrayList<>(this.existingPlaceholders)));
    serialized.put("factoryPh", SerialList.fromMapped(this.UnifyTypeFactory_PLACEHOLDERS, t -> t.toSerial(keyStorage)));
    return serialized;
  }
}
