package org.ultramine.mods.hawkeye;

import java.util.Iterator;

import org.ultramine.mods.hawkeye.Rollback.RollbackType;
import org.ultramine.mods.hawkeye.database.DataManager;
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.entity.player.EntityPlayerMP;
import net.minecraft.network.play.server.S23PacketBlockChange;
import net.minecraftforge.common.util.BlockSnapshot;

/**
 * Runnable class for reversing a {@link Rollback}. This class should always be
 * run in a separate thread to avoid impacting on server performance
 * 
 * @author oliverw92
 */
public class Undo
{
	private final PlayerSession session;
	private Iterator<DataEntry> undoQueue;
	private int counter = 0;
	private RollbackType undoType = RollbackType.GLOBAL;

	/**
	 * @param session
	 *            {@link PlayerSession} to retrieve undo results from
	 */
	public Undo(RollbackType undoType, PlayerSession session)
	{
		this.undoType = undoType;
		this.session = session;
		undoQueue = session.getRollbackResults().iterator();

		//Check if already rolling back
		if(session.doingRollback())
		{
			HawkUtil.sendMessage(session.getSender(), "&cYour previous rollback is still processing, please wait before performing an undo!");
			return;
		}

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

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

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

	/**
	 * Run the undo. Contains appropriate methods of catching errors and
	 * notifying the player
	 */
	@SubscribeEvent
	public void onServerTick(TickEvent.ServerTickEvent e)
	{
		if(e.phase == TickEvent.Phase.END)
			return;
		//Start rollback process
		int i = 0;
		while(i < 200 && undoQueue.hasNext())
		{
			//If undo doesn't exist
			DataEntry entry = undoQueue.next();
			if(entry.getUndoState() == null)
				continue;

			//Global undo
			if(undoType == RollbackType.GLOBAL)
			{
				BlockSnapshot block = entry.getUndoState();
				block.world.restoringBlockSnapshots = true;
				block.restore(true);
				block.world.restoringBlockSnapshots = false;
				//Add back into database if delete data is on
				if(HawkEye.instance.config.general.deleteDataOnRollback)
					DataManager.addEntry(entry);
			}

			//Player undo
			else
			{
				EntityPlayerMP player = (EntityPlayerMP) session.getSender();
				BlockSnapshot block = entry.getUndoState();

				S23PacketBlockChange p = new S23PacketBlockChange(block.x, block.y, block.z, block.world);
				p.field_148883_d = block.getReplacedBlock();
				p.field_148884_e = block.meta;
				player.playerNetServerHandler.sendPacket(p);
			}

			counter++;
		}

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

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

			HawkUtil.sendMessage(session.getSender(), "&cUndo complete, &7" + counter + " &cedits performed");
			HawkUtil.debug("Undo complete, " + counter + " edits performed");

		}
	}
}