package ru.yandex.webmaster3.core.statistic;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.BitSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.google.common.math.IntMath;

import ru.yandex.webmaster3.core.data.WebmasterHostId;

/**
 * @author aherman
 */
public class ApproximateHostCounter {
    private final BitSet bitSet;
    private final int bits;
    private final MessageDigest md5;
    private final Lock lock = new ReentrantLock();

    public ApproximateHostCounter(int bits) throws NoSuchAlgorithmException {
        this.bits = bits;
        this.bitSet = new BitSet(IntMath.pow(2, bits));
        this.md5 = MessageDigest.getInstance("MD5");
    }

    public void addHost(WebmasterHostId hostId) {
        lock.lock();
        try {
            byte[] digest = md5.digest(hostId.toStringId().getBytes());
            bitSet.set(toId(digest, bits));
        } finally {
            lock.unlock();
        }
    }

    public int getBits() {
        return bits;
    }

    public BitSet getBitSet() {
        lock.lock();
        try {
            return (BitSet) bitSet.clone();
        } finally {
            lock.unlock();
        }
    }

    private static int toId(byte[] bytes, int bits) {
        int size = bits;
        int id = 0;
        int i = bytes.length - 1;
        int c = 0;
        while (size > 7) {
            id += (0xFF & bytes[i]) << c;
            c += 8;
            size -= 8;
            i--;
        }
        if (size > 0) {
            id +=  (0xFF & bytes[i] & ((0x1 << size) - 1)) << c;
        }
        return id;
    }

}
