package org.ultramine.mods.hawkeye;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ultramine.mods.hawkeye.entry.DataEntry;
import org.ultramine.mods.hawkeye.util.HawkUtil;

import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import net.minecraft.world.World;
import net.minecraftforge.common.util.BlockSnapshot;

/**
 * Runnable class for performing a data rollback. This class should always be
 * run in a separate thread to avoid impacting on server performance
 * 
 * @author oliverw92
 */
public class Rebuild
{
	private static final Logger log = LogManager.getLogger();
	private final PlayerSession session;
	private Iterator<DataEntry> rebuildQueue;
	private final List<DataEntry> undo = new ArrayList<DataEntry>();
	private int counter = 0;

	/**
	 * @param session
	 *            {@link PlayerSession} to retrieve rebuild results from
	 */
	public Rebuild(PlayerSession session)
	{
		this.session = session;
		rebuildQueue = session.getRollbackResults().iterator();

		//Check that we actually have results
		if(!rebuildQueue.hasNext())
		{
			HawkUtil.sendMessage(session.getSender(), "&cNo results found to rebuild");
			return;
		}

		HawkUtil.debug("Starting rebuild of " + session.getRollbackResults().size() + " results");

		//Start rollback
		session.setDoingRollback(true);
		HawkUtil.sendMessage(session.getSender(), "&cAttempting to rebuild &7" + session.getRollbackResults().size() + "&c results");
		FMLCommonHandler.instance().bus().register(this);

	}

	@SubscribeEvent
	public void onServerTick(TickEvent.ServerTickEvent e)
	{
		if(e.phase == TickEvent.Phase.END)
			return;
		//Start rollback process
		int i = 0;
		while(i < 200 && rebuildQueue.hasNext())
		{
			i++;

			DataEntry entry = rebuildQueue.next();

			//If the action can't be rolled back, skip this entry
			if(entry.getType() == null || !entry.getType().canRollback())
				continue;

			//If the world doesn't exist, skip this entry
			World world = HawkEye.getWorld(entry.getWorld());
			if(world == null)
				continue;

			BlockSnapshot state = BlockSnapshot.getBlockSnapshot(world, (int) entry.getX(), (int) entry.getY(), (int) entry.getZ());
			
			//Rebuild it
			boolean addUndo;
			try
			{
				addUndo = entry.rebuild(world);
			}
			catch(Exception exc)
			{
				addUndo = true;
				log.error("Error while rebuild entry at ["+entry.getWorld()+"]("+entry.getX()+", "+entry.getY()+", "+entry.getZ()+")", exc);
			}
			
			if(addUndo)
			{
				entry.setUndoState(state);
				undo.add(entry);
			}

			counter++;

		}

		//Check if rollback is finished
		if(!rebuildQueue.hasNext())
		{
			//End timer
			FMLCommonHandler.instance().bus().unregister(this);

			session.setDoingRollback(false);
			session.setRollbackResults(undo);

			HawkUtil.sendMessage(session.getSender(), "&cRebuild complete, &7" + counter + "&c edits performed");
			HawkUtil.sendMessage(session.getSender(), "&cUndo this rebuild using &7/hawk undo");

			HawkUtil.debug("Rebuild complete, " + counter + " edits performed");

		}

	}

}
