package net.i2p.router.networkdb.kademlia;

import net.i2p.data.Hash;
import net.i2p.data.TunnelId;
import net.i2p.util.ObjectCounter;
import net.i2p.util.SimpleTimer;
import net.i2p.util.SimpleTimer2;

/**
 * Count how often we have recently received a lookup request with
 * the reply specified to go to a peer/TunnelId pair.
 * This offers basic DOS protection but is not a complete solution.
 * The reply peer/tunnel could be spoofed, for example.
 * And a requestor could have up to 6 reply tunnels.
 *
 * @since 0.7.11
 */
class LookupThrottler {
    private final ObjectCounter<ReplyTunnel> counter;
    /** the id of this is -1 */
    private static final TunnelId DUMMY_ID = new TunnelId();
    /** this seems like plenty */
    private static final int MAX_LOOKUPS = 30;
    private static final long CLEAN_TIME = 2*60*1000;

    LookupThrottler() {
        this.counter = new ObjectCounter<ReplyTunnel>();
        SimpleTimer2.getInstance().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
    }

    /**
     * increments before checking
     * @param key non-null
     * @param id null if for direct lookups
     */
    boolean shouldThrottle(Hash key, TunnelId id) {
        return this.counter.increment(new ReplyTunnel(key, id)) > MAX_LOOKUPS;
    }

    private class Cleaner implements SimpleTimer.TimedEvent {
        public void timeReached() {
            LookupThrottler.this.counter.clear();
        }
    }

    /** yes, we could have a two-level lookup, or just do h.tostring() + id.tostring() */
    private static class ReplyTunnel {
        public final Hash h;
        public final TunnelId id;

        ReplyTunnel(Hash h, TunnelId id) {
            this.h = h;
            if (id != null)
                this.id = id;
            else
                this.id = DUMMY_ID;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null || !(obj instanceof ReplyTunnel))
                return false;
            return this.h.equals(((ReplyTunnel)obj).h) &&
                   this.id.equals(((ReplyTunnel)obj).id);
        }
    
        @Override
        public int hashCode() {
            return this.h.hashCode() ^ this.id.hashCode(); 
        }
    }
}
