package net.tomp2p.p2p;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import net.tomp2p.peers.Number160;
import net.tomp2p.peers.PeerAddress;

public class Statistics
{
	private final static double MAX = Math.pow(2, Number160.BITS);
	private double estimatedNumberOfPeers = 1;
	private double avgGap = MAX / 2;
	//
	private final List<Map<Number160, PeerAddress>> peerMap;
	// private final Number160 remotePeer;
	private final int maxSize;
	private final int bagSize;

	private int currentSize;

	// no need to be volatile, since they are synced
	private long tcpCreationCount = 0;
	private long udpCreationCount = 0;
	private long tcpCount = 0;
	private long udpCount = 0;

	public Statistics(List<Map<Number160, PeerAddress>> peerMap,
			Number160 remotePeer, int maxSize, int bagSize)
	{
		this.peerMap = peerMap;
		// this.remotePeer = remotePeer;
		this.maxSize = maxSize;
		this.bagSize = bagSize;
	}

	// TODO: with increasing number of peers, the diff gets lower and lower
	public void triggerStatUpdate(boolean insert, int currentSize)
	{
		this.currentSize = currentSize;
	}

	public double getEstimatedNumberOfNodes()
	{
		// we know it exactly
		if (currentSize < maxSize)
		{
			estimatedNumberOfPeers = currentSize;
			return estimatedNumberOfPeers;
		}
		// otherwise we know we are full!
		double gap = 0D;
		int gapCount = 0;
		int oldNumPeers = 0;
		for (int i = Number160.BITS - 1; i >= 0; i--)
		{
			Map<Number160, PeerAddress> peers = peerMap.get(i);
			int numPeers = peers.size();
			// System.out.print(numPeers);
			// System.out.print(",");
			if (numPeers > 0 && (numPeers < bagSize || numPeers < oldNumPeers))
			{
				double currentGap = Math.pow(2, i) / numPeers;
				// System.out.print("gap("+i+"):"+currentGap+";");
				gap += currentGap * numPeers;
				gapCount += numPeers;
			}
			else
			{
				// System.out.print("ignoring "+i+";");
			}
			oldNumPeers = numPeers;
		}
		// System.out.print("\n");
		avgGap = gap / gapCount;
		estimatedNumberOfPeers = (MAX / avgGap);
		return estimatedNumberOfPeers;
	}

	public double getAvgGap()
	{
		return avgGap;
	}

	public static void tooClose(Collection<PeerAddress> collection)
	{

	}

	public void incrementTCPChannelCreation()
	{
		synchronized (this)
		{
			tcpCreationCount++;
			tcpCount++;
		}
	}

	public void incrementUDPChannelCreation()
	{
		synchronized (this)
		{
			udpCreationCount++;
			udpCount++;
		}
	}
	
	public void decrementTCPChannelCreation()
	{
		synchronized (this)
		{
			tcpCount--;
		}
	}

	public void decrementUDPChannelCreation()
	{
		synchronized (this)
		{
			udpCount--;
		}
	}

	public long getTCPChannelCreationCount()
	{
		synchronized (this)
		{
			return tcpCreationCount;
		}
	}

	public long getUDPChannelCreationCount()
	{
		synchronized (this)
		{
			return udpCreationCount;
		}
	}
	
	public long getTCPChannelCount()
	{
		synchronized (this)
		{
			return tcpCount;
		}
	}

	public long getUDPChannelCount()
	{
		synchronized (this)
		{
			return udpCount;
		}
	}
}
