package client.renderer.chunk;

import java.util.ArrayDeque;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import client.Client;
import client.renderer.RegionRenderCacheBuilder;
import client.renderer.RenderBuffer;
import client.renderer.VertexBuffer;
import client.renderer.chunk.ChunkBuilder.ImmediateFuture;
import client.renderer.chunk.ChunkBuilder.ListenableFuture;
import client.renderer.chunk.ChunkBuilder.ListenableFutureTask;
import common.collect.Lists;

public class ChunkRenderDispatcher
{
    private final List<ChunkRenderWorker> listThreadedWorkers = Lists.<ChunkRenderWorker>newArrayList();
    private final BlockingQueue<ChunkCompileTaskGenerator> queueChunkUpdates = new ArrayBlockingQueue<ChunkCompileTaskGenerator>(100);
    private final BlockingQueue<RegionRenderCacheBuilder> queueFreeRenderBuilders = new ArrayBlockingQueue<RegionRenderCacheBuilder>(5);
    private final Queue < ListenableFutureTask<? >> queueChunkUploads = new ArrayDeque<ListenableFutureTask<?>>();
    private final ChunkRenderWorker renderWorker;

    private VertexBuffer buffer;

    public ChunkRenderDispatcher()
    {
        for (int i = 0; i < 2; ++i)
        {
            ChunkRenderWorker chunkrenderworker = new ChunkRenderWorker(this);
            Thread thread = new Thread(chunkrenderworker, "Chunk Batcher #" + (i + 1));
            thread.setDaemon(true);
            thread.start();
            this.listThreadedWorkers.add(chunkrenderworker);
        }

        for (int j = 0; j < 5; ++j)
        {
            this.queueFreeRenderBuilders.add(new RegionRenderCacheBuilder());
        }

        this.renderWorker = new ChunkRenderWorker(this, new RegionRenderCacheBuilder());
    }
    
    public void kill() {
    	for(ChunkRenderWorker worker : this.listThreadedWorkers) {
    		worker.end();
    	}
    }

    public String getDebugInfo()
    {
        return String.format("pC: %03d, pU: %1d, aB: %1d", this.queueChunkUpdates.size(), this.queueChunkUploads.size(), this.queueFreeRenderBuilders.size());
    }

    public boolean runChunkUploads(long p_178516_1_)
    {
        boolean flag = false;

        while (true)
        {
            boolean flag1 = false;

            synchronized (this.queueChunkUploads)
            {
                if (!this.queueChunkUploads.isEmpty())
                {
                    ((ListenableFutureTask)this.queueChunkUploads.poll()).run();
                    flag1 = true;
                    flag = true;
                }
            }

            if (p_178516_1_ == 0L || !flag1)
            {
                break;
            }

            long i = p_178516_1_ - System.nanoTime();

            if (i < 0L)
            {
                break;
            }
        }

        return flag;
    }

    public boolean updateChunkLater(RenderChunk chunkRenderer)
    {
        chunkRenderer.getLockCompileTask().lock();
        boolean flag1;

        try
        {
            final ChunkCompileTaskGenerator chunkcompiletaskgenerator = chunkRenderer.makeCompileTaskChunk();
            chunkcompiletaskgenerator.addFinishRunnable(new Runnable()
            {
                public void run()
                {
                    ChunkRenderDispatcher.this.queueChunkUpdates.remove(chunkcompiletaskgenerator);
                }
            });
            boolean flag = this.queueChunkUpdates.offer(chunkcompiletaskgenerator);

            if (!flag)
            {
                chunkcompiletaskgenerator.finish();
            }

            flag1 = flag;
        }
        finally
        {
            chunkRenderer.getLockCompileTask().unlock();
        }

        return flag1;
    }

    public boolean updateChunkNow(RenderChunk chunkRenderer)
    {
        chunkRenderer.getLockCompileTask().lock();
        boolean flag;

        try
        {
            ChunkCompileTaskGenerator chunkcompiletaskgenerator = chunkRenderer.makeCompileTaskChunk();

            try
            {
                this.renderWorker.processTask(chunkcompiletaskgenerator);
            }
            catch (InterruptedException var7)
            {
                ;
            }

            flag = true;
        }
        finally
        {
            chunkRenderer.getLockCompileTask().unlock();
        }

        return flag;
    }

    public void stopChunkUpdates()
    {
        this.clearChunkUpdates();

        while (this.runChunkUploads(0L))
        {
            ;
        }

        List<RegionRenderCacheBuilder> list = Lists.<RegionRenderCacheBuilder>newArrayList();

        while (((List)list).size() != 5)
        {
            try
            {
                list.add(this.allocateRenderBuilder());
            }
            catch (InterruptedException var3)
            {
                ;
            }
        }

        this.queueFreeRenderBuilders.addAll(list);
    }

    public void freeRenderBuilder(RegionRenderCacheBuilder p_178512_1_)
    {
        this.queueFreeRenderBuilders.add(p_178512_1_);
    }

    public RegionRenderCacheBuilder allocateRenderBuilder() throws InterruptedException
    {
        return (RegionRenderCacheBuilder)this.queueFreeRenderBuilders.take();
    }

    public ChunkCompileTaskGenerator getNextChunkUpdate() throws InterruptedException
    {
        return (ChunkCompileTaskGenerator)this.queueChunkUpdates.take();
    }

    public ListenableFuture<Object> uploadChunk(final BlockLayer layer, final RenderBuffer buf, final RenderChunk renderer, final CompiledChunk compiled)
    {
        if (Client.CLIENT.isMainThread())
        {
            buf.reset();
			renderer.getVertexBufferByLayer(layer.ordinal()).bufferData(buf.getByteBuffer());

            buf.setTranslation(0.0D, 0.0D, 0.0D);
            return new ImmediateFuture();
        }
        else
        {
            ListenableFutureTask<Object> listenablefuturetask = ListenableFutureTask.<Object>create(new Runnable()
            {
                public void run()
                {
                    ChunkRenderDispatcher.this.uploadChunk(layer, buf, renderer, compiled);
                }
            }, (Object)null);

            synchronized (this.queueChunkUploads)
            {
                this.queueChunkUploads.add(listenablefuturetask);
                return listenablefuturetask;
            }
        }
    }

    public void clearChunkUpdates()
    {
        while (!this.queueChunkUpdates.isEmpty())
        {
            ChunkCompileTaskGenerator chunkcompiletaskgenerator = (ChunkCompileTaskGenerator)this.queueChunkUpdates.poll();

            if (chunkcompiletaskgenerator != null)
            {
                chunkcompiletaskgenerator.finish();
            }
        }
    }
}
