package org.jboss.windup.rules.apps.xml.model;

import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import org.jboss.windup.util.Logging;
import org.w3c.dom.Document;

/**
 * This class provides a cache for parsed XML documents, using {@link SoftReference}s in order to avoid creating unnecessary memory pressure.
 */
public class XMLDocumentCache
{
    private static final Logger LOG = Logging.get(XMLDocumentCache.class);
    private static final Map<String, CacheDocument> map = new HashMap<>();

    /**
     * This is used to pass data back to the caller regarding the cache search.
     *
     * If a parsing failure had been cached, {@link Result#isParseFailure()} will return true, and {@link Result#getDocument()} will return false.
     *
     * A cache miss will have {@link Result#isParseFailure()} set to false and {@link Result#getDocument()} will be null.
     */
    public static class Result
    {
        private boolean parseFailure;
        private Document document;

        public Result(boolean parseFailure, Document document)
        {
            this.parseFailure = parseFailure;
            this.document = document;
        }

        public boolean isParseFailure()
        {
            return parseFailure;
        }

        public Document getDocument()
        {
            return document;
        }
    }

    /**
     * Add the provided document to the cache.
     */
    public static void cache(XmlFileModel key, Document document)
    {
        String cacheKey = getKey(key);
        map.put(cacheKey, new CacheDocument(false, document));
    }

    /**
     * Cache a parse failure for this document.
     */
    public static void cacheParseFailure(XmlFileModel key)
    {
        map.put(getKey(key), new CacheDocument(true, null));
    }

    /**
     * Retrieve the currently cached value for the given document.
     */
    public static Result get(XmlFileModel key)
    {
        String cacheKey = getKey(key);

        Result result = null;
        CacheDocument reference = map.get(cacheKey);

        if (reference == null)
            return new Result(false, null);

        if (reference.parseFailure)
            return new Result(true, null);

        Document document = reference.getDocument();
        if (document == null)
            LOG.info("Cache miss on XML document: " + cacheKey);

        return new Result(false, document);
    }

    private static String getKey(XmlFileModel key)
    {
        return key.getFilePath();
    }

    private static class CacheDocument
    {
        private boolean parseFailure;
        private SoftReference<Document> document;

        public CacheDocument(boolean parseFailure, Document document)
        {
            this.parseFailure = parseFailure;
            this.document = new SoftReference<>(document);
        }

        public Document getDocument()
        {
            return document.get();
        }
    }
}
