package net.minecraftforge.server;

import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.MapMaker;

import net.minecraft.entity.Entity;
import net.minecraft.tileentity.TileEntity;

public class ForgeTimeTracker
{
    public static boolean tileEntityTracking;
    public static int tileEntityTrackingDuration;
    public static long tileEntityTrackingTime;

    private Map<TileEntity, int[]> tileEntityTimings;
    private Map<Entity, int[]> entityTimings;

    private static final ForgeTimeTracker INSTANCE = new ForgeTimeTracker();

    private WeakReference<TileEntity> tile;
    private WeakReference<Entity> entity;

    private long timing;

    private ForgeTimeTracker()
    {
        MapMaker mm = new MapMaker();
        mm.weakKeys();
        tileEntityTimings = mm.makeMap();
        entityTimings = mm.makeMap();
    }

    private void trackTileStart(TileEntity tileEntity, long nanoTime)
    {
        if (tileEntityTrackingTime == 0)
        {
            tileEntityTrackingTime = nanoTime;
        }
        else if (tileEntityTrackingTime + tileEntityTrackingDuration < nanoTime)
        {
            tileEntityTracking = false;
            tileEntityTrackingTime = 0;
            return;
        }

        tile = new WeakReference<TileEntity>(tileEntity);
        timing = nanoTime;
    }

    private void trackTileEnd(TileEntity tileEntity, long nanoTime)
    {
        if (tile == null || tile.get() != tileEntity)
        {
            tile = null;
            // race, exit
            return;
        }

        int[] timings = tileEntityTimings.get(tileEntity);

        if (timings == null)
        {
            timings = new int[101];
            tileEntityTimings.put(tileEntity, timings);
        }

        int idx = timings[100] = (timings[100] + 1) % 100;
        timings[idx] = (int)(nanoTime - timing);
    }

    public static ImmutableMap<TileEntity, int[]> getTileTimings()
    {
        return INSTANCE.buildImmutableTileEntityTimingMap();
    }

    private ImmutableMap<TileEntity, int[]> buildImmutableTileEntityTimingMap()
    {
        Builder<TileEntity, int[]> builder = ImmutableMap.<TileEntity, int[]>builder();

        for (Entry<TileEntity, int[]> entry : tileEntityTimings.entrySet())
        {
            builder.put(entry.getKey(), Arrays.copyOfRange(entry.getValue(), 0, 100));
        }

        return builder.build();
    }

    public static void trackStart(TileEntity tileEntity)
    {
        if (!tileEntityTracking)
        {
            return;
        }

        INSTANCE.trackTileStart(tileEntity, System.nanoTime());
    }

    public static void trackEnd(TileEntity tileEntity)
    {
        if (!tileEntityTracking)
        {
            return;
        }

        INSTANCE.trackTileEnd(tileEntity, System.nanoTime());
    }

    public static void trackStart(Entity par1Entity)
    {
    }

    public static void trackEnd(Entity par1Entity)
    {
    }
}
