
package de.ivu.elect.jee.concurrent;

import static java.util.stream.Collectors.toSet;
import static org.apache.commons.lang3.StringUtils.isNoneBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

import java.io.Serializable;
import java.util.Collection;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import javax.enterprise.context.ApplicationScoped;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.jee.entity.EntityTechnicalNamePk;

@ApplicationScoped
public class GleichzeitigBearbeitenStore implements Serializable {

  private static final long serialVersionUID = 1L;

  private static final Logger LOG = LoggerFactory.getLogger(GleichzeitigBearbeitenStore.class);

  private final ConcurrentHashMap<String, ConcurrentHashMap<EntityTechnicalNamePk, GleichzeitigBearbeitenEntity>> entitaetenGeoeffnetPerSessionId;

  public GleichzeitigBearbeitenStore() {
    super();

    this.entitaetenGeoeffnetPerSessionId = new ConcurrentHashMap<>();
  }

  public Collection<GleichzeitigBearbeitenEntity> collectAllGleichzeitigBearbeitenEntities(EntityTechnicalNamePk entityGeoeffnet) {
    return entitaetenGeoeffnetPerSessionId.values().stream() //
        .flatMap(entryPerSessionId -> entryPerSessionId.entrySet().stream()) //
        .filter(entryPerEntityGeoeffnet -> entryPerEntityGeoeffnet.getKey().equals(entityGeoeffnet) && entryPerEntityGeoeffnet.getValue() != null) //
        .map(Entry::getValue) //
        .collect(toSet());
  }

  public void registerEntityGeoeffnetInSession(String sessionId, String geoeffnetDurch, EntityTechnicalNamePk entityGeoeffnet) {
    if (isNoneBlank(sessionId, geoeffnetDurch)) {
      collectEntitaetenGeoeffnetInSession(sessionId).put(entityGeoeffnet, new GleichzeitigBearbeitenEntity(sessionId, geoeffnetDurch, entityGeoeffnet));
    }

    log("GleichzeitigBearbeitenStore#registerEntityGeoeffnetInSession");
    dumpEntitaetenGeoeffnetPerSessionId();
  }

  public void unregisterEntityGeoeffnetInSession(String sessionId, EntityTechnicalNamePk entityGeoeffnet) {
    if (isNotBlank(sessionId)) {
      collectEntitaetenGeoeffnetInSession(sessionId).remove(entityGeoeffnet);
    }

    log("GleichzeitigBearbeitenStore#unregisterEntityGeoeffnetInSession");
    dumpEntitaetenGeoeffnetPerSessionId();
  }

  public void unregisterSession(String sessionId) {
    if (isNotBlank(sessionId)) {
      entitaetenGeoeffnetPerSessionId.remove(sessionId);
    }

    log("GleichzeitigBearbeitenStore#unregisterSession");
    dumpEntitaetenGeoeffnetPerSessionId();
  }

  private ConcurrentHashMap<EntityTechnicalNamePk, GleichzeitigBearbeitenEntity> collectEntitaetenGeoeffnetInSession(String sessionId) {
    entitaetenGeoeffnetPerSessionId.putIfAbsent(sessionId, new ConcurrentHashMap<>());

    return entitaetenGeoeffnetPerSessionId.get(sessionId);
  }

  private void dumpEntitaetenGeoeffnetPerSessionId() {
    log("GleichzeitigBearbeitenStore - Anzahl Sessions = {}", entitaetenGeoeffnetPerSessionId.size());

    for (Entry<String, ConcurrentHashMap<EntityTechnicalNamePk, GleichzeitigBearbeitenEntity>> entryPerSessionId : entitaetenGeoeffnetPerSessionId.entrySet()) {
      ConcurrentHashMap<EntityTechnicalNamePk, GleichzeitigBearbeitenEntity> entitaetenGeoeffnet = entryPerSessionId.getValue();

      log("\tSession {} - Anzahl Entitäten geöffnet = {}", entryPerSessionId.getKey(), entitaetenGeoeffnet.size());

      for (Entry<EntityTechnicalNamePk, GleichzeitigBearbeitenEntity> entryPerEntityGeoeffnet : entitaetenGeoeffnet.entrySet()) {
        GleichzeitigBearbeitenEntity gleichzeitigBearbeitenEntity = entryPerEntityGeoeffnet.getValue();
        EntityTechnicalNamePk entityGeoeffnet = gleichzeitigBearbeitenEntity.getEntityGeoeffnet();

        log("\t\t{} - id = {}, geöffnet durch = {}, geöffnet am = {}", entityGeoeffnet.getTechnicalName(), entityGeoeffnet.getId(), gleichzeitigBearbeitenEntity.getGeoeffnetDurch(),
            gleichzeitigBearbeitenEntity.getGeoeffnetAm());
      }
    }
  }

  private void log(String format, Object... params) {
    LOG.debug(format, params);
  }
}
