import ch.qos.logback.classic.turbo.TurboFilter;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.slf4j.Marker;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.core.spi.FilterReply;

import java.util.concurrent.TimeUnit;

/**
 *
 * See {@link http://logback.qos.ch/manual/filters.html#DuplicateMessageFilter}
 */
public class DuplicateMessageFilter extends TurboFilter {

    /**
     * The default cache size.
     */
    public static final int DEFAULT_CACHE_SIZE = 100;
    /**
     * The default number of allows repetitions.
     */
    public static final int DEFAULT_ALLOWED_REPETITIONS = 5;
    /**
     * The default expiration time (in sec.)
     */
    public static final int DEFAULT_EXPIRATION_TIME = 600; // (in sec)

    private int allowedRepetitions = DEFAULT_ALLOWED_REPETITIONS;
    private int cacheSize = DEFAULT_CACHE_SIZE;
    private int expirationTime = DEFAULT_EXPIRATION_TIME;

    private Cache<String, Integer> msgCache;

    @Override
    public void start() {
        msgCache = CacheBuilder
                .newBuilder()
                .expireAfterAccess(expirationTime, TimeUnit.SECONDS)
                .maximumSize(cacheSize)
                .build();
        super.start();
    }

    @Override
    public void stop() {
        msgCache.invalidateAll();
        msgCache = null;
        super.stop();
    }

    @Override
    public FilterReply decide(Marker marker, Logger logger, Level level,
                              String format, Object[] params, Throwable t) {
        int count = getMessageCountAndThenIncrement(format);
        if (count <= allowedRepetitions) {
            return FilterReply.NEUTRAL;
        } else {
            return FilterReply.DENY;
        }
    }

    public int getAllowedRepetitions() {
        return allowedRepetitions;
    }

    public void setAllowedRepetitions(int allowedRepetitions) {
        this.allowedRepetitions = allowedRepetitions;
    }

    public int getCacheSize() {
        return cacheSize;
    }

    public void setCacheSize(int cacheSize) {
        this.cacheSize = cacheSize;
    }

    public int getExpirationTime() {
        return expirationTime;
    }

    public void setExpirationTime(int expirationTime) {
        this.expirationTime = expirationTime;
    }

    int getMessageCountAndThenIncrement(String msg) {
        // don't insert null elements
        if (msg == null) {
            return 0;
        }

        Integer i;
        synchronized (this) {
            i = msgCache.getIfPresent(msg);
            if (i == null) {
                i = 0;
            } else {
                i = i + 1;
            }
            msgCache.put(msg, i);
        }
        return i;
    }

}
