package com.elmakers.mine.bukkit.utilities;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.bukkit.Server;
import org.bukkit.block.Block;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitScheduler;

import com.elmakers.mine.bukkit.dao.BlockList;
import com.elmakers.mine.bukkit.plugins.magic.CleanupBlocksTask;
import com.elmakers.mine.bukkit.plugins.magic.Spells;
import com.elmakers.mine.bukkit.utilities.borrowed.ConfigurationNode;

public class UndoQueue
{
	private final LinkedList<BlockList> blockQueue = new LinkedList<BlockList>();
	private final Set<BlockList> 		scheduledBlocks = new HashSet<BlockList>();
	private int                         maxSize    = 0;

	public void add(BlockList blocks)
	{
		if (maxSize > 0 && blockQueue.size() > maxSize)
		{
			blockQueue.removeFirst();
		}
		blockQueue.add(blocks);
	}
	
	public void scheduleCleanup(Spells spells, BlockList blocks)
	{
		scheduledBlocks.add(blocks);
		
		Plugin plugin = spells.getPlugin();
		Server server = plugin.getServer();
		BukkitScheduler scheduler = server.getScheduler();

		// scheduler works in ticks- 20 ticks per second.
		long ticksToLive = blocks.getTimeToLive() * 20 / 1000;
		scheduler.scheduleSyncDelayedTask(plugin, new CleanupBlocksTask(this, spells, blocks), ticksToLive);
	}
	
	public void removeScheduledCleanup(BlockList blockList)
	{
		scheduledBlocks.remove(blockList);
	}

	public BlockList getLast()
	{
		if (blockQueue.isEmpty())
		{
			return null;
		}
		return blockQueue.getLast();
	}

	public BlockList getLast(Block target)
	{
		if (blockQueue.size() == 0)
		{
			return null;
		}
		for (BlockList blocks : blockQueue)
		{
			if (blocks.contains(target))
			{
				return blocks;
			}
		}
		return null;
	}

	public void setMaxSize(int size)
	{
		maxSize = size;
	}

	public boolean undo(Spells spells)
	{
		if (blockQueue.size() == 0)
		{
			return false;
		}

		BlockList blocks = blockQueue.removeLast();
		blocks.undo(spells);
		return true;
	}

	public boolean undo(Spells spells, Block target)
	{
		BlockList lastActionOnTarget = getLast(target);

		if (lastActionOnTarget == null)
		{
			return false;
		}

		blockQueue.remove(lastActionOnTarget);
		lastActionOnTarget.undo(spells);

		return true;
	}
	
	public void load(Spells spells, ConfigurationNode node)
	{
		try {
			if (node == null) return;
			List<ConfigurationNode> nodeList = node.getNodeList("undo", null);
			for (ConfigurationNode listNode : nodeList) {
				BlockList list = new BlockList();
				list.load(listNode);
				blockQueue.add(list);
			}
			nodeList = node.getNodeList("scheduled", null);
			for (ConfigurationNode listNode : nodeList) {
				BlockList list = new BlockList();
				list.load(listNode);
				scheduleCleanup(spells, list);
			}
		} catch (Exception ex) {
			spells.getPlugin().getLogger().warning("Failed to load undo data: " + ex.getMessage());
		}
	}
	
	public void save(Spells spells, ConfigurationNode node)
	{
		try {
			List<Map<String, Object>> nodeList = new ArrayList<Map<String, Object>>();
			for (BlockList list : blockQueue) {
				Map<String, Object> listNode = new HashMap<String, Object>();
				list.save(listNode);
				nodeList.add(listNode);
			}
			node.setProperty("undo", nodeList);
			nodeList = new ArrayList<Map<String, Object>>();
			for (BlockList list : scheduledBlocks) {
				Map<String, Object> listNode = new HashMap<String, Object>();
				list.save(listNode);
				nodeList.add(listNode);
			}
			node.setProperty("scheduled", nodeList);
		} catch (Exception ex) {
			spells.getPlugin().getLogger().warning("Failed to save undo data: " + ex.getMessage());
		}
	}
	
	public int getSize()
	{
		return blockQueue.size();
	}
	
	public void commit()
	{
		blockQueue.clear();
	}
}

@@ -2901,32 +2901,57 @@
 Exception ex) %7B%0A
+%09%09%09ex.printStackTrace();%0A
 %09%09%09spells.getPlu
@@ -3673,16 +3673,41 @@
 n ex) %7B%0A
+%09%09%09ex.printStackTrace();%0A
 %09%09%09spell

Add stack traces to an undo saving error I encountered.