
package de.ivu.elect.presentation.system;

import java.io.Serializable;
import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

import javax.annotation.Nullable;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.enterprise.context.RequestScoped;

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

import de.ivu.elect.business.system.control.CacheQueries;

/*
 * ELECT-11741
 */
public class DAOCache implements Serializable {
  private static final Logger LOG = LoggerFactory.getLogger(DAOCache.class);

  private static final long serialVersionUID = 1L;

  private static final AtomicLong CACHE_MISS = new AtomicLong();

  private static final AtomicLong CACHE_HIT = new AtomicLong();

  private static final AtomicLong CACHE_PUT = new AtomicLong();

  @Resource(mappedName = "java:comp/TransactionSynchronizationRegistry")
  private transient javax.transaction.TransactionSynchronizationRegistry transactionSynchronizationRegistry;

  private static final ThreadLocal<DAOCacheContent> threadLocalValue = new ThreadLocal<>();

  public <E> E cache(FinderKey finderKey, E result) {
    if (result != null && CacheQueries.CACHE_QUERIES_DAO) {
      if (CACHE_PUT.addAndGet(1) % 100 == 0) {
        LOG.info("CACHE_PUT = " + CACHE_PUT + ", CACHE_HIT = " + CACHE_HIT + ", CACHE_MISS = " + CACHE_MISS);
      }
      getDAOCacheContent().put(finderKey, result);
    }
    return result;
  }

  @Nullable
  public Object findInCache(FinderKey finderKey) {
    if (CacheQueries.CACHE_QUERIES_DAO) {
      Object lookup = getDAOCacheContent().lookup(finderKey);
      if (lookup == null) {
        CACHE_MISS.addAndGet(1);
      } else {
        CACHE_HIT.addAndGet(1);
      }
      return lookup;
    }
    return null;
  }

  public void clear() {
    if (CacheQueries.CACHE_QUERIES_DAO) {
      getDAOCacheContent().clear();
    }
  }

  protected DAOCacheContent getDAOCacheContent() {
    try {
      DAOCacheContent daoCacheContent = threadLocalValue.get();
      Object transactionKey = transactionSynchronizationRegistry.getTransactionKey();
      if (daoCacheContent == null || !Objects.equals(daoCacheContent.getTransactionKey(), transactionKey)) {
        if (daoCacheContent != null) {
          daoCacheContent.clear();
        }
        daoCacheContent = new DAOCacheContent(transactionKey);
        transactionSynchronizationRegistry.registerInterposedSynchronization(daoCacheContent);
        threadLocalValue.set(daoCacheContent);
      }
      return daoCacheContent;
    } catch (Exception e) {
      LOG.warn("Fehler in DAOCache: " + e.getMessage());
      threadLocalValue.remove();
      return new DAOCacheContent("DUMMY");
    }
  }
}
