//////////////////////////////////////////////////////////////////////
// This file is part of Remere's Map Editor
//////////////////////////////////////////////////////////////////////

#include "main.h"

#include <sstream>
#include <time.h>
#include <wx/wfstream.h>

#include "gui.h"
#include "editor.h"
#include "brush.h"
#include "sprites.h"
#include "map.h"
#include "tile.h"
#include "old_properties_window.h"
#include "properties_window.h"
#include "palette_window.h"
#include "map_display.h"
#include "map_drawer.h"
#include "application.h"
#include "live_server.h"

#include "doodad_brush.h"
#include "house_exit_brush.h"
#include "house_brush.h"
#include "wall_brush.h"
#include "spawn_brush.h"
#include "creature_brush.h"
#include "ground_brush.h"
#include "waypoint_brush.h"
#include "raw_brush.h"
#include "carpet_brush.h"
#include "table_brush.h"


BEGIN_EVENT_TABLE(MapCanvas, wxGLCanvas)
	EVT_KEY_DOWN(MapCanvas::OnKeyDown)

	// Mouse events
	EVT_MOTION(MapCanvas::OnMouseMove)
	EVT_LEFT_UP(MapCanvas::OnMouseLeftRelease)
	EVT_LEFT_DOWN(MapCanvas::OnMouseLeftClick)
	EVT_LEFT_DCLICK(MapCanvas::OnMouseLeftDoubleClick)
	EVT_MIDDLE_DOWN(MapCanvas::OnMouseCenterClick)
	EVT_MIDDLE_UP(MapCanvas::OnMouseCenterRelease)
	EVT_RIGHT_DOWN(MapCanvas::OnMouseRightClick)
	EVT_RIGHT_UP(MapCanvas::OnMouseRightRelease)
	EVT_MOUSEWHEEL(MapCanvas::OnWheel)
	EVT_ENTER_WINDOW(MapCanvas::OnGainMouse)
	EVT_LEAVE_WINDOW(MapCanvas::OnLoseMouse)

	//Drawing events
	EVT_PAINT(MapCanvas::OnPaint)
	EVT_ERASE_BACKGROUND(MapCanvas::OnEraseBackground)

	// Menu events
	EVT_MENU(MAP_POPUP_MENU_CUT, MapCanvas::OnCut)
	EVT_MENU(MAP_POPUP_MENU_COPY, MapCanvas::OnCopy)
	EVT_MENU(MAP_POPUP_MENU_COPY_POSITION, MapCanvas::OnCopyPosition)
	EVT_MENU(MAP_POPUP_MENU_PASTE, MapCanvas::OnPaste)
	EVT_MENU(MAP_POPUP_MENU_DELETE, MapCanvas::OnDelete)
	// ----
	EVT_MENU(MAP_POPUP_MENU_ROTATE, MapCanvas::OnRotateItem)
	EVT_MENU(MAP_POPUP_MENU_GOTO, MapCanvas::OnGotoDestination)
	EVT_MENU(MAP_POPUP_MENU_SWITCH_DOOR, MapCanvas::OnSwitchDoor)
	// ----
	EVT_MENU(MAP_POPUP_MENU_SELECT_RAW_BRUSH, MapCanvas::OnSelectRAWBrush)
	EVT_MENU(MAP_POPUP_MENU_SELECT_GROUND_BRUSH, MapCanvas::OnSelectGroundBrush)
	EVT_MENU(MAP_POPUP_MENU_SELECT_DOODAD_BRUSH, MapCanvas::OnSelectDoodadBrush)
	EVT_MENU(MAP_POPUP_MENU_SELECT_DOOR_BRUSH, MapCanvas::OnSelectDoorBrush)
	EVT_MENU(MAP_POPUP_MENU_SELECT_WALL_BRUSH, MapCanvas::OnSelectWallBrush)
	EVT_MENU(MAP_POPUP_MENU_SELECT_CARPET_BRUSH, MapCanvas::OnSelectCarpetBrush)
	EVT_MENU(MAP_POPUP_MENU_SELECT_TABLE_BRUSH, MapCanvas::OnSelectTableBrush)
	EVT_MENU(MAP_POPUP_MENU_SELECT_CREATURE_BRUSH, MapCanvas::OnSelectCreatureBrush)
	EVT_MENU(MAP_POPUP_MENU_SELECT_SPAWN_BRUSH, MapCanvas::OnSelectSpawnBrush)
	EVT_MENU(MAP_POPUP_MENU_SELECT_HOUSE_BRUSH, MapCanvas::OnSelectHouseBrush)
	// ----
	EVT_MENU(MAP_POPUP_MENU_PROPERTIES, MapCanvas::OnProperties)
END_EVENT_TABLE()

MapCanvas::MapCanvas(MapWindow* parent, Editor& editor, int* attriblist) :
	wxGLCanvas(parent, wxID_ANY, nullptr, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS),
	editor(editor),
	floor(7),
	zoom(1.0),
	cursor_x(-1),
	cursor_y(-1),
	dragging(false),
	boundbox_selection(false),
	screendragging(false),
	drawing(false),
	dragging_draw(false),
	replace_dragging(false),

	screenshot_buffer(nullptr),

	drag_start_x(-1),
	drag_start_y(-1),
	drag_start_z(-1),

	last_cursor_map_x(-1),
	last_cursor_map_y(-1),
	last_cursor_map_z(-1),

	last_click_map_x(-1),
	last_click_map_y(-1),
	last_click_map_z(-1),
	last_click_abs_x(-1),
	last_click_abs_y(-1),
	last_click_x(-1),
	last_click_y(-1),

	last_mmb_click_x(-1),
	last_mmb_click_y(-1)
{
	popup_menu = newd MapPopupMenu(editor);
}

MapCanvas::~MapCanvas()
{
	delete popup_menu;
	free(screenshot_buffer);
}

void MapCanvas::Refresh()
{
	if(refresh_watch.Time() > settings.getInteger(Config::HARD_REFRESH_RATE))
{
		refresh_watch.Start();
		wxGLCanvas::Update();
	}
	wxGLCanvas::Refresh();
}

void MapCanvas::GetViewBox(int* view_scroll_x, int* view_scroll_y, int* screensize_x, int* screensize_y) const
{
	static_cast<MapWindow*>(GetParent())->GetViewSize(screensize_x, screensize_y);
	static_cast<MapWindow*>(GetParent())->GetViewStart(view_scroll_x, view_scroll_y);
}

void MapCanvas::OnPaint(wxPaintEvent& event)
{
	wxPaintDC pdc(this);

	SetCurrent(*gui.GetGLContext(this));

	if(gui.IsRenderingEnabled())
	{
		DrawingOptions options;
		if(screenshot_buffer != nullptr)
		{
			options.SetIngame();
		}
		else
		{
			options.transparent_floors = settings.getBoolean(Config::TRANSPARENT_FLOORS);
			options.transparent_items = settings.getBoolean(Config::TRANSPARENT_ITEMS);
			options.show_ingame_box = settings.getBoolean(Config::SHOW_INGAME_BOX);
			options.show_grid = settings.getInteger(Config::SHOW_GRID);
			options.ingame = !settings.getBoolean(Config::SHOW_EXTRA);
			options.show_all_floors = settings.getBoolean(Config::SHOW_ALL_FLOORS);
			options.show_creatures = settings.getBoolean(Config::SHOW_CREATURES);
			options.show_spawns = settings.getBoolean(Config::SHOW_SPAWNS);
			options.show_houses = settings.getBoolean(Config::SHOW_HOUSES);
			options.show_shade = settings.getBoolean(Config::SHOW_SHADE);
			options.show_special_tiles = settings.getBoolean(Config::SHOW_SPECIAL_TILES);
			options.show_items = settings.getBoolean(Config::SHOW_ITEMS);
			options.highlight_items = settings.getBoolean(Config::HIGHLIGHT_ITEMS);
			options.show_blocking = settings.getBoolean(Config::SHOW_BLOCKING);
			options.show_only_colors = settings.getBoolean(Config::SHOW_ONLY_TILEFLAGS);
			options.show_only_modified = settings.getBoolean(Config::SHOW_ONLY_MODIFIED_TILES);
			options.hide_items_when_zoomed = settings.getBoolean(Config::HIDE_ITEMS_WHEN_ZOOMED);
		}

		options.dragging = boundbox_selection;

		MapDrawer drawer(options, this, pdc);

		drawer.Draw();

		if(screenshot_buffer != nullptr)
			drawer.TakeScreenshot(screenshot_buffer);
	}

	// Clean unused textures
	gui.gfx.garbageCollection();

	// Swap buffer
	SwapBuffers();

	// Send newd node requests
	editor.SendNodeRequests();
}

void MapCanvas::TakeScreenshot(wxFileName path, wxString format)
{
	int screensize_x, screensize_y;
	GetViewBox(&view_scroll_x, &view_scroll_y, &screensize_x, &screensize_y);

	delete[] screenshot_buffer;
	screenshot_buffer = newd uint8_t[3 * screensize_x * screensize_y];

	// Draw the window
	Refresh();
	wxGLCanvas::Update(); // Forces immediate redraws the window.

	// screenshot_buffer should now contain the screenbuffer
	if(screenshot_buffer == nullptr)
	{
		gui.PopupDialog(wxT("Capture failed"), wxT("Image capture failed. Old Video Driver?"), wxOK);
	}
	else
	{
		// We got the shit
		int screensize_x, screensize_y;
		static_cast<MapWindow*>(GetParent())->GetViewSize(&screensize_x, &screensize_y);
		wxImage screenshot(screensize_x, screensize_y, screenshot_buffer);

		time_t t = time(nullptr);
		struct tm* current_time = localtime(&t);
		ASSERT(current_time);

		wxString date;
		date << wxT("screenshot_") << (1900 + current_time->tm_year);
		if(current_time->tm_mon < 9)
			date << wxT("-") << wxT("0") << current_time->tm_mon+1;
		else
			date << wxT("-") << current_time->tm_mon+1;
		date << wxT("-") << current_time->tm_mday;
		date << wxT("-") << current_time->tm_hour;
		date << wxT("-") << current_time->tm_min;
		date << wxT("-") << current_time->tm_sec;
		
		int type = 0;
		path.SetName(date);
		if(format == wxT("bmp"))
		{
			path.SetExt(format);
			type = wxBITMAP_TYPE_BMP;
		}
		else if(format == wxT("png"))
		{
			path.SetExt(format);
			type = wxBITMAP_TYPE_PNG;
		}
		else if(format == wxT("jpg") || format == wxT("jpeg"))
		{
			path.SetExt(format);
			type = wxBITMAP_TYPE_JPEG;
		}
		else if(format == wxT("tga"))
		{
			path.SetExt(format);
			type = wxBITMAP_TYPE_TGA;
		}
		else
		{
			gui.SetStatusText(wxT("Unknown screenshot format \'") + format + wxT("\", switching to default (png)"));
			path.SetExt(wxT("png"));;
			type = wxBITMAP_TYPE_PNG;
		}

		path.Mkdir(0755, wxPATH_MKDIR_FULL);
		wxFileOutputStream of(path.GetFullPath());
		if(of.IsOk())
		{
			if(screenshot.SaveFile(of, static_cast<wxBitmapType>(type)))
				gui.SetStatusText(wxT("Took screenshot and saved as ") + path.GetFullName());
			else
				gui.PopupDialog(wxT("File error"), wxT("Couldn't save image file correctly."), wxOK);
		}
		else
		{
			gui.PopupDialog(wxT("File error"), wxT("Couldn't open file ") + path.GetFullPath() + wxT(" for writing."), wxOK);
		}
		
	}

	Refresh();

	screenshot_buffer = nullptr;
}

void MapCanvas::ScreenToMap(int screen_x, int screen_y, int* map_x, int* map_y)
{
	int start_x, start_y;
	static_cast<MapWindow*>(GetParent())->GetViewStart(&start_x, &start_y);

	if (screen_x < 0) {
		*map_x = (start_x + screen_x) / 32;
	} else {
		*map_x = int(start_x + (screen_x*zoom)) / 32;
	}

	if (screen_y < 0) {
		*map_y = (start_y + screen_y) / 32;
	} else {
		*map_y = int(start_y + (screen_y*zoom)) / 32;
	}

	if(floor <= 7) {
		*map_x += 7 - floor;
		*map_y += 7 - floor;
	}/* else {
		*map_x += 15-floor;
		*map_y += 15-floor;
	}*/
}

void MapCanvas::GetScreenCenter(int* map_x, int* map_y)
{
	int width, height;
	static_cast<MapWindow*>(GetParent())->GetViewSize(&width, &height);
	return ScreenToMap(width/2, height/2, map_x, map_y);
}

Position MapCanvas::GetCursorPosition() const {
	return Position(last_cursor_map_x, last_cursor_map_y, floor);
}

void MapCanvas::UpdatePositionStatus(int x, int y)
{
	if(x == -1) x = cursor_x;
	if(y == -1) y = cursor_y;

	int map_x, map_y;
	ScreenToMap(x, y, &map_x, &map_y);

	wxString ss;
	ss << wxT("x: ") << map_x << wxT(" y:") << map_y << wxT(" z:") << floor;
	gui.root->SetStatusText(ss,2);

	ss = wxT("");
	Tile* tile = editor.map.getTile(map_x, map_y, floor);
	if(tile)
	{
		if(tile->spawn && settings.getInteger(Config::SHOW_SPAWNS))
		{
			ss << wxT("Spawn radius: ") << tile->spawn->getSize();
		} else if(tile->creature && settings.getInteger(Config::SHOW_CREATURES))
		{
			ss << (tile->creature->isNpc()? wxT("NPC") : wxT("Monster"));
			ss << wxT(" \"") << wxstr(tile->creature->getName()) << wxT("\" spawntime: ") << tile->creature->getSpawnTime();
		} else if(Item* item = tile->getTopItem())
		{
			ss << wxT("Item \"") << wxstr(item->getName()) << wxT("\"");
			ss << wxT(" id:") << item->getID();
			ss << wxT(" cid:") << item->getClientID();
			if(item->getUniqueID()) ss << wxT(" uid:") << item->getUniqueID();
			if(item->getActionID()) ss << wxT(" aid:") << item->getActionID();
			if(item->hasWeight())
			{
				wxString s;
				s.Printf(wxT("%.2f"), item->getWeight());
				ss << wxT(" weight: ") << s;
			}
		} else {
			ss << wxT("Nothing");
		}
	} else {
		ss << wxT("Nothing");
	}

	if (editor.IsLive()) {
		editor.GetLive().updateCursor(Position(map_x, map_y, floor));
	}

	gui.root->SetStatusText(ss, 1);
}

void MapCanvas::OnMouseMove(wxMouseEvent& event)
{
	if(screendragging)
	{
		static_cast<MapWindow*>(GetParent())->ScrollRelative(int(settings.getFloat(Config::SCROLL_SPEED) * zoom*(event.GetX() - cursor_x)), int(settings.getFloat(Config::SCROLL_SPEED) * zoom*(event.GetY() - cursor_y)));
		Refresh();
	}

	cursor_x = event.GetX();
	cursor_y = event.GetY();

	int mouse_map_x, mouse_map_y;
	MouseToMap(&mouse_map_x,&mouse_map_y);

	bool map_update = false;
	if(last_cursor_map_x != mouse_map_x || last_cursor_map_y != mouse_map_y || last_cursor_map_z != floor)
	{
		map_update = true;
	}
	last_cursor_map_x = mouse_map_x;
	last_cursor_map_y = mouse_map_y;
	last_cursor_map_z = floor;

	if(map_update)
	{
		UpdatePositionStatus(cursor_x, cursor_y);
	}

	if(gui.IsSelectionMode())
	{
		if(map_update && isPasting())
		{
			Refresh();
		} else if(map_update && dragging)
		{
			wxString ss;

			int move_x = drag_start_x - mouse_map_x;
			int move_y = drag_start_y - mouse_map_y;
			int move_z = drag_start_z - floor;
			ss << wxT("Dragging ") << -move_x << wxT(":") << -move_y << wxT(":") << -move_z;
			gui.SetStatusText(ss);

			Refresh();
		} else if(boundbox_selection)
		{
			if(map_update)
			{
				wxString ss;

				int move_x = std::abs(last_click_map_x - mouse_map_x);
				int move_y = std::abs(last_click_map_y - mouse_map_y);
				ss << wxT("Selection ") << move_x+1 << wxT(":") << move_y+1;
				gui.SetStatusText(ss);
			}

			Refresh();
		}
	} else { // Drawing mode
		if(map_update && drawing && gui.GetCurrentBrush())
		{
			if(dynamic_cast<DoodadBrush*>(gui.GetCurrentBrush()))
			{
				if(event.ControlDown())
				{
					PositionVector tilestodraw;
					getTilesToDraw(mouse_map_x, mouse_map_y, floor, &tilestodraw, nullptr);
					editor.undraw(tilestodraw, event.ShiftDown() || event.AltDown());
				} else {
					editor.draw(Position(mouse_map_x, mouse_map_y, floor), event.ShiftDown() || event.AltDown());
				}
			} else if(dynamic_cast<DoorBrush*>(gui.GetCurrentBrush()))
			{
				if(gui.GetCurrentBrush()->canDraw(&editor.map, Position(mouse_map_x, mouse_map_y, floor)) == false)
				{
					// We don't have to waste an action in this case...
				} else {
					PositionVector tilestodraw;
					PositionVector tilestoborder;

					tilestodraw.push_back(Position(mouse_map_x, mouse_map_y, floor));

					tilestoborder.push_back(Position(mouse_map_x    , mouse_map_y - 1, floor));
					tilestoborder.push_back(Position(mouse_map_x - 1, mouse_map_y    , floor));
					tilestoborder.push_back(Position(mouse_map_x    , mouse_map_y + 1, floor));
					tilestoborder.push_back(Position(mouse_map_x + 1, mouse_map_y    , floor));

					if(event.ControlDown())
					{
						editor.undraw(tilestodraw, tilestoborder, event.AltDown());
					} else {
						editor.draw(tilestodraw, tilestoborder, event.AltDown());
					}
				}
			} else if(gui.GetCurrentBrush()->needBorders())
			{
				PositionVector tilestodraw, tilestoborder;

				getTilesToDraw(mouse_map_x, mouse_map_y, floor, &tilestodraw, &tilestoborder);

				if(event.ControlDown())
				{
					editor.undraw(tilestodraw, tilestoborder, event.AltDown());
				} else {
					editor.draw(tilestodraw, tilestoborder, event.AltDown());
				}
			} else if(gui.GetCurrentBrush()->oneSizeFitsAll())
			{
				drawing = true;
				PositionVector tilestodraw;
				tilestodraw.push_back(Position(mouse_map_x,mouse_map_y, floor));

				if(event.ControlDown())
				{
					editor.undraw(tilestodraw, event.AltDown());
				} else {
					editor.draw(tilestodraw, event.AltDown());
				}
			} else { // No borders
				PositionVector tilestodraw;

				for(int y = -gui.GetBrushSize(); y <= gui.GetBrushSize(); y++)
				{
					for(int x = -gui.GetBrushSize(); x <= gui.GetBrushSize(); x++)
					{
						if(gui.GetBrushShape() == BRUSHSHAPE_SQUARE)
						{
							tilestodraw.push_back(Position(mouse_map_x+x,mouse_map_y+y, floor));
						} else if(gui.GetBrushShape() == BRUSHSHAPE_CIRCLE)
						{
							double distance = sqrt(double(x*x) + double(y*y));
							if(distance < gui.GetBrushSize()+0.005)
							{
								tilestodraw.push_back(Position(mouse_map_x+x,mouse_map_y+y, floor));
							}
						}
					}
				}
				if(event.ControlDown())
				{
					editor.undraw(tilestodraw, event.AltDown());
				} else {
					editor.draw(tilestodraw, event.AltDown());
				}
			}

			// Create newd doodad layout (does nothing if a non-doodad brush is selected)
			gui.FillDoodadPreviewBuffer();

			gui.RefreshView();
		} else if(dragging_draw)
		{
			gui.RefreshView();
		} else if(map_update && gui.GetCurrentBrush())
		{
			Refresh();
		}
	}
}

void MapCanvas::OnMouseLeftRelease(wxMouseEvent& event)
{
	OnMouseActionRelease(event);
}

void MapCanvas::OnMouseLeftClick(wxMouseEvent& event)
{
	OnMouseActionClick(event);
}

void MapCanvas::OnMouseLeftDoubleClick(wxMouseEvent& event)
{
	if(settings.getInteger(Config::DOUBLECLICK_PROPERTIES))
	{
		int mouse_map_x, mouse_map_y;
		ScreenToMap(event.GetX(), event.GetY(), &mouse_map_x, &mouse_map_y);
		Tile* tile = editor.map.getTile(mouse_map_x, mouse_map_y, floor);

		if(tile && tile->size() > 0)
		{
			Tile* new_tile = tile->deepCopy(editor.map);
			wxDialog* w = nullptr;
			if(new_tile->spawn && settings.getInteger(Config::SHOW_SPAWNS))
				w = newd OldPropertiesWindow(gui.root, &editor.map, new_tile, new_tile->spawn);
			else if(new_tile->creature && settings.getInteger(Config::SHOW_CREATURES))
				w = newd OldPropertiesWindow(gui.root, &editor.map, new_tile, new_tile->creature);
			else if (Item* item = new_tile->getTopItem())
			{
				if (editor.map.getVersion().otbm >= MAP_OTBM_4)
					w = newd PropertiesWindow(gui.root, &editor.map, new_tile, item);
				else
					w = newd OldPropertiesWindow(gui.root, &editor.map, new_tile, item);
			}
			else
				return;

			int ret = w->ShowModal();
			if(ret != 0)
			{
				Action* action = editor.actionQueue->createAction(ACTION_CHANGE_PROPERTIES);
				action->addChange(newd Change(new_tile));
				editor.addAction(action);
			}
			else
			{
				// Cancel!
				delete new_tile;
			}
			w->Destroy();
		}
	}
}

void MapCanvas::OnMouseCenterClick(wxMouseEvent& event)
{
	if(settings.getInteger(Config::SWITCH_MOUSEBUTTONS))
	{
		OnMousePropertiesClick(event);
	} else {
		OnMouseCameraClick(event);
	}
}

void MapCanvas::OnMouseCenterRelease(wxMouseEvent& event)
{
	if(settings.getInteger(Config::SWITCH_MOUSEBUTTONS))
	{
		OnMousePropertiesRelease(event);
	} else {
		OnMouseCameraRelease(event);
	}
}

void MapCanvas::OnMouseRightClick(wxMouseEvent& event)
{
	if(settings.getInteger(Config::SWITCH_MOUSEBUTTONS))
	{
		OnMouseCameraClick(event);
	} else {
		OnMousePropertiesClick(event);
	}
}

void MapCanvas::OnMouseRightRelease(wxMouseEvent& event)
{
	if(settings.getInteger(Config::SWITCH_MOUSEBUTTONS))
	{
		OnMouseCameraRelease(event);
	} else {
		OnMousePropertiesRelease(event);
	}
}

void MapCanvas::OnMouseActionClick(wxMouseEvent& event)
{
	SetFocus();

	int mouse_map_x, mouse_map_y;
	ScreenToMap(event.GetX(), event.GetY(), &mouse_map_x, &mouse_map_y);

	if(gui.IsSelectionMode())
	{
		if(isPasting())
		{
			// Set paste to false (no rendering etc.)
			EndPasting();

			// Paste to the map
			editor.copybuffer.paste(editor, Position(mouse_map_x, mouse_map_y, floor));

			// Start dragging
			dragging = true;
			drag_start_x = mouse_map_x;
			drag_start_y = mouse_map_y;
			drag_start_z = floor;
		} else do {
			boundbox_selection = false;
			if(event.ShiftDown())
			{
				boundbox_selection = true;

				if(!event.ControlDown())
				{
					editor.selection.start(); // Start selection session
					editor.selection.clear(); // Clear out selection
					editor.selection.finish(); // End selection session
					editor.selection.updateSelectionCount();
				}
			} else if(event.ControlDown())
			{
				Tile* tile = editor.map.getTile(mouse_map_x, mouse_map_y, floor);
				if(tile)
				{
					if(tile->spawn && settings.getInteger(Config::SHOW_SPAWNS))
					{
						editor.selection.start(); // Start selection session
						if(tile->spawn->isSelected())
						{
							editor.selection.remove(tile, tile->spawn);
						} else {
							editor.selection.add(tile, tile->spawn);
						}
						editor.selection.finish(); // Finish selection session
						editor.selection.updateSelectionCount();
					} else if(tile->creature && settings.getInteger(Config::SHOW_CREATURES))
					{
						editor.selection.start(); // Start selection session
						if(tile->creature->isSelected())
						{
							editor.selection.remove(tile, tile->creature);
						} else {
							editor.selection.add(tile, tile->creature);
						}
						editor.selection.finish(); // Finish selection session
						editor.selection.updateSelectionCount();
					} else {
						Item* item = tile->getTopItem();
						if(item)
						{
							editor.selection.start(); // Start selection session
							if(item->isSelected())
							{
								editor.selection.remove(tile, item);
							} else {
								editor.selection.add(tile, item);
							}
							editor.selection.finish(); // Finish selection session
							editor.selection.updateSelectionCount();
						}
					}
				}
			} else {
				Tile* tile = editor.map.getTile(mouse_map_x, mouse_map_y, floor);
				if(!tile)
				{
					editor.selection.start(); // Start selection session
					editor.selection.clear(); // Clear out selection
					editor.selection.finish(); // End selection session
					editor.selection.updateSelectionCount();
				} else if(tile->isSelected())
				{
					dragging = true;
					drag_start_x = mouse_map_x;
					drag_start_y = mouse_map_y;
					drag_start_z = floor;
				} else {
					editor.selection.start(); // Start a selection session
					editor.selection.clear();
					editor.selection.commit();
					if(tile->spawn && settings.getInteger(Config::SHOW_SPAWNS))
					{
						editor.selection.add(tile, tile->spawn);
						dragging = true;
						drag_start_x = mouse_map_x;
						drag_start_y = mouse_map_y;
						drag_start_z = floor;
					} else if(tile->creature && settings.getInteger(Config::SHOW_CREATURES))
					{
						editor.selection.add(tile, tile->creature);
						dragging = true;
						drag_start_x = mouse_map_x;
						drag_start_y = mouse_map_y;
						drag_start_z = floor;
					} else {
						Item* item = tile->getTopItem();
						if(item)
						{
							editor.selection.add(tile, item);
							dragging = true;
							drag_start_x = mouse_map_x;
							drag_start_y = mouse_map_y;
							drag_start_z = floor;
						}
					}
					editor.selection.finish(); // Finish the selection session
					editor.selection.updateSelectionCount();
				}
			}
		} while(false);
	} else if(gui.GetCurrentBrush())
	{ // Drawing mode
		if(event.ShiftDown() && gui.GetCurrentBrush()->canDrag())
		{
			dragging_draw = true;
		} else {
			if(gui.GetBrushSize() == 0 && !gui.GetCurrentBrush()->oneSizeFitsAll())
			{
				drawing = true;
			} else {
				drawing = gui.GetCurrentBrush()->canSmear();
			}
			if(dynamic_cast<WallBrush*>(gui.GetCurrentBrush()))
			{
				if(event.AltDown() && gui.GetBrushSize() == 0)
				{
					// z0mg, just clicked a tile, shift variaton.
					if(event.ControlDown())
					{
						editor.undraw(Position(mouse_map_x, mouse_map_y, floor), event.AltDown());
					} else {
						editor.draw(Position(mouse_map_x, mouse_map_y, floor), event.AltDown());
					}
				} else {
					PositionVector tilestodraw;
					PositionVector tilestoborder;

					int start_map_x = mouse_map_x - gui.GetBrushSize();
					int start_map_y = mouse_map_y - gui.GetBrushSize();
					int end_map_x   = mouse_map_x + gui.GetBrushSize();
					int end_map_y   = mouse_map_y + gui.GetBrushSize();

					for(int y = start_map_y -1; y <= end_map_y + 1; ++y)
					{
						for(int x = start_map_x - 1; x <= end_map_x + 1; ++x)
						{
							if(
								(x <= start_map_x+1 || x >= end_map_x-1) ||
								(y <= start_map_y+1 || y >= end_map_y-1)
								)
							{
								tilestoborder.push_back(Position(x,y,floor));
							}
							if(
								(
								(x == start_map_x || x == end_map_x) ||
								(y == start_map_y || y == end_map_y)
								) && (
								(x >= start_map_x && x <= end_map_x) &&
								(y >= start_map_y && y <= end_map_y)
								)
								)
							{
								tilestodraw.push_back(Position(x,y,floor));
							}
						}
					}
					if(event.ControlDown())
					{
						editor.undraw(tilestodraw, tilestoborder, event.AltDown());
					} else {
						editor.draw(tilestodraw, tilestoborder, event.AltDown());
					}
				}
			} else if(dynamic_cast<DoorBrush*>(gui.GetCurrentBrush()))
			{
				PositionVector tilestodraw;
				PositionVector tilestoborder;

				tilestodraw.push_back(Position(mouse_map_x, mouse_map_y, floor));

				tilestoborder.push_back(Position(mouse_map_x    , mouse_map_y - 1, floor));
				tilestoborder.push_back(Position(mouse_map_x - 1, mouse_map_y    , floor));
				tilestoborder.push_back(Position(mouse_map_x    , mouse_map_y + 1, floor));
				tilestoborder.push_back(Position(mouse_map_x + 1, mouse_map_y    , floor));

				if(event.ControlDown())
				{
					editor.undraw(tilestodraw, tilestoborder, event.AltDown());
				} else {
					editor.draw(tilestodraw, tilestoborder, event.AltDown());
				}
			} else if(dynamic_cast<DoodadBrush*>(gui.GetCurrentBrush()) || dynamic_cast<SpawnBrush*>(gui.GetCurrentBrush()) || dynamic_cast<CreatureBrush*>(gui.GetCurrentBrush()))
			{
				if(event.ControlDown())
				{
					if(dynamic_cast<DoodadBrush*>(gui.GetCurrentBrush()))
					{
						PositionVector tilestodraw;
						getTilesToDraw(mouse_map_x, mouse_map_y, floor, &tilestodraw, nullptr);
						editor.undraw(tilestodraw, event.AltDown());
					} else {
						editor.undraw(Position(mouse_map_x, mouse_map_y, floor), event.ShiftDown() || event.AltDown());
					}
				} else {
					editor.draw(Position(mouse_map_x, mouse_map_y, floor), event.ShiftDown() || event.AltDown());
				}
			} else {
				GroundBrush* gbrush = dynamic_cast<GroundBrush*>(gui.GetCurrentBrush());

				if(gbrush && event.AltDown())
				{
					replace_dragging = true;
					Tile* draw_tile = editor.map.getTile(mouse_map_x, mouse_map_y, floor);
					if(draw_tile)
					{
						editor.replace_brush = draw_tile->getGroundBrush();
					} else {
						editor.replace_brush = nullptr;
					}
				}

				if(gui.GetCurrentBrush()->needBorders())
				{
					PositionVector tilestodraw;
					PositionVector tilestoborder;

					getTilesToDraw(mouse_map_x, mouse_map_y, floor, &tilestodraw, &tilestoborder);

					if(event.ControlDown())
					{
						editor.undraw(tilestodraw, tilestoborder, event.AltDown());
					} else {
						editor.draw(tilestodraw, tilestoborder, event.AltDown());
					}
				} else if(gui.GetCurrentBrush()->oneSizeFitsAll())
				{
					if(dynamic_cast<HouseExitBrush*>(gui.GetCurrentBrush()) || dynamic_cast<WaypointBrush*>(gui.GetCurrentBrush()))
					{
						editor.draw(Position(mouse_map_x, mouse_map_y, floor), event.AltDown());
					} else {
						PositionVector tilestodraw;
						tilestodraw.push_back(Position(mouse_map_x,mouse_map_y, floor));
						if(event.ControlDown())
						{
							editor.undraw(tilestodraw, event.AltDown());
						} else {
							editor.draw(tilestodraw, event.AltDown());
						}
					}
				} else {
					PositionVector tilestodraw;

					getTilesToDraw(mouse_map_x, mouse_map_y, floor, &tilestodraw, nullptr);

					if(event.ControlDown())
					{
						editor.undraw(tilestodraw, event.AltDown());
					} else {
						editor.draw(tilestodraw, event.AltDown());
					}
				}
			}
			// Change the doodad layout brush
			gui.FillDoodadPreviewBuffer();
		}
	}
	last_click_x = int(event.GetX()*zoom);
	last_click_y = int(event.GetY()*zoom);

	int start_x, start_y;
	static_cast<MapWindow*>(GetParent())->GetViewStart(&start_x, &start_y);
	last_click_abs_x = last_click_x + start_x;
	last_click_abs_y = last_click_y + start_y;

	last_click_map_x = mouse_map_x;
	last_click_map_y = mouse_map_y;
	last_click_map_z = floor;
	gui.RefreshView();
	gui.UpdateMinimap();
}

void MapCanvas::OnMouseActionRelease(wxMouseEvent& event)
{
	int mouse_map_x, mouse_map_y;
	ScreenToMap(event.GetX(), event.GetY(), &mouse_map_x, &mouse_map_y);

	int move_x = last_click_map_x - mouse_map_x;
	int move_y = last_click_map_y - mouse_map_y;
	int move_z = last_click_map_z - floor;

	if(gui.IsSelectionMode())
	{
		if(dragging && (move_x != 0 || move_y != 0 || move_z != 0))
		{
			editor.moveSelection(Position(move_x, move_y, move_z));
		} else {
			if(boundbox_selection)
			{
				if(mouse_map_x == last_click_map_x && mouse_map_y == last_click_map_y && event.ControlDown())
				{
					// Mouse hasn't moved, do control+shift thingy!
					Tile* tile = editor.map.getTile(mouse_map_x, mouse_map_y, floor);
					if(tile)
					{
						editor.selection.start(); // Start a selection session
						if(tile->isSelected())
						{
							editor.selection.remove(tile);
						} else {
							editor.selection.add(tile);
						}
						editor.selection.finish(); // Finish the selection session
						editor.selection.updateSelectionCount();
					}
				} else {
					// The cursor has moved, do some boundboxing!
					if(last_click_map_x > mouse_map_x)
					{int tmp = mouse_map_x; mouse_map_x = last_click_map_x; last_click_map_x = tmp;}
					if(last_click_map_y > mouse_map_y)
					{int tmp = mouse_map_y; mouse_map_y = last_click_map_y; last_click_map_y = tmp;}

					int numtiles = 0;
					int threadcount = std::max(settings.getInteger(Config::WORKER_THREADS), 1);

					int start_x = 0, start_y = 0, start_z = 0;
					int end_x = 0, end_y = 0, end_z = 0;

					switch(settings.getInteger(Config::SELECTION_TYPE))
					{
					case SELECT_CURRENT_FLOOR:
						{
							start_z = end_z = floor;
							start_x = last_click_map_x;
							start_y = last_click_map_y;
							end_x = mouse_map_x;
							end_y = mouse_map_y;
							break;
						}
					case SELECT_ALL_FLOORS:
						{
							start_x = last_click_map_x;
							start_y = last_click_map_y;
							start_z = MAP_HEIGHT-1;
							end_x = mouse_map_x;
							end_y = mouse_map_y;
							end_z = floor;

							if(settings.getInteger(Config::COMPENSATED_SELECT))
							{
								start_x -= (floor < 7? 7-floor : 0);
								start_y -= (floor < 7? 7-floor : 0);

								end_x -= (floor < 7? 7-floor : 0);
								end_y -= (floor < 7? 7-floor : 0);
							}

							numtiles = (start_z - end_z) * (end_x - start_x) * (end_y - start_y);
							break;
						}
					case SELECT_VISIBLE_FLOORS:
						{
							start_x = last_click_map_x;
							start_y = last_click_map_y;
							if(floor < 8)
							{
								start_z = 7;
							} else {
								start_z = std::min(15, floor + 2);
							}
							end_x = mouse_map_x;
							end_y = mouse_map_y;
							end_z = floor;

							if(settings.getInteger(Config::COMPENSATED_SELECT))
							{
								start_x -= (floor < 7? 7-floor : 0);
								start_y -= (floor < 7? 7-floor : 0);

								end_x -= (floor < 7? 7-floor : 0);
								end_y -= (floor < 7? 7-floor : 0);
							}
							break;
						}
					}
					if(numtiles < 500)
					{
						// No point in threading for such a small set.
						threadcount = 1;
					}
					// Subdivide the selection area
					// We know it's a square, just split it into several areas
					int width = end_x - start_x;
					if(width < threadcount)
					{
						threadcount = min(1, width);
					}
					// Let's divide!
					int remainder = width;
					int cleared = 0;
					std::vector<SelectionThread*> threads;
					if(width == 0)
					{
						threads.push_back(newd SelectionThread(editor, Position(start_x, start_y, start_z), Position(start_x, end_y, end_z)));
					} else {
						for(int i = 0; i < threadcount; ++i)
						{
							int chunksize = width / threadcount;
							// The last threads takes all the remainder
							if(i == threadcount - 1)
							{
								chunksize = remainder;
							}
							threads.push_back(newd SelectionThread(editor, Position(start_x + cleared, start_y, start_z), Position(start_x + cleared + chunksize, end_y, end_z)));
							cleared += chunksize;
							remainder -= chunksize;
						}
					}
					ASSERT(cleared == width);
					ASSERT(remainder == 0);

					editor.selection.start(); // Start a selection session
					for(std::vector<SelectionThread*>::iterator iter = threads.begin(); iter != threads.end(); ++iter)
					{
						(*iter)->Execute();
					}
					for(std::vector<SelectionThread*>::iterator iter = threads.begin(); iter != threads.end(); ++iter)
					{
						editor.selection.join(*iter);
					}
					editor.selection.finish(); // Finish the selection session
					editor.selection.updateSelectionCount();
				}
			} else if(event.ControlDown())
			{
			} else {
				// User hasn't moved anything, meaning selection/deselection
				Tile* tile = editor.map.getTile(mouse_map_x, mouse_map_y, floor);
				if(tile)
				{
					if(tile->spawn && settings.getInteger(Config::SHOW_SPAWNS))
					{
						if(!tile->spawn->isSelected())
						{
							editor.selection.start(); // Start a selection session
							editor.selection.add(tile, tile->spawn);
							editor.selection.finish(); // Finish the selection session
							editor.selection.updateSelectionCount();
						}
					} else if(tile->creature && settings.getInteger(Config::SHOW_CREATURES))
					{
						if(!tile->creature->isSelected())
						{
							editor.selection.start(); // Start a selection session
							editor.selection.add(tile, tile->creature);
							editor.selection.finish(); // Finish the selection session
							editor.selection.updateSelectionCount();
						}
					} else {
						Item* item = tile->getTopItem();
						if(item && !item->isSelected())
						{
							editor.selection.start(); // Start a selection session
							editor.selection.add(tile, item);
							editor.selection.finish(); // Finish the selection session
							editor.selection.updateSelectionCount();
						}
					}
				}
			}
		}
		editor.actionQueue->resetTimer();
		dragging = false;
		boundbox_selection = false;
	} else { // Drawing mode
		if(dragging_draw)
		{
			if(dynamic_cast<SpawnBrush*>(gui.GetCurrentBrush()))
			{
				int start_map_x = std::min(last_click_map_x, mouse_map_x);
				int start_map_y = std::min(last_click_map_y, mouse_map_y);
				int end_map_x   = std::max(last_click_map_x, mouse_map_x);
				int end_map_y   = std::max(last_click_map_y, mouse_map_y);

				int map_x = start_map_x + (end_map_x - start_map_x)/2;
				int map_y = start_map_y + (end_map_y - start_map_y)/2;

				int width = min(settings.getInteger(Config::MAX_SPAWN_RADIUS), ((end_map_x - start_map_x)/2 + (end_map_y - start_map_y)/2)/2);
				int old = gui.GetBrushSize();
				gui.SetBrushSize(width);
				editor.draw(Position(map_x, map_y, floor), event.AltDown());
				gui.SetBrushSize(old);
			} else {
				PositionVector tilestodraw;
				PositionVector tilestoborder;
				if(dynamic_cast<WallBrush*>(gui.GetCurrentBrush()))
				{
					int start_map_x = std::min(last_click_map_x, mouse_map_x);
					int start_map_y = std::min(last_click_map_y, mouse_map_y);
					int end_map_x   = std::max(last_click_map_x, mouse_map_x);
					int end_map_y   = std::max(last_click_map_y, mouse_map_y);

					for(int y = start_map_y-1; y <= end_map_y+1; y ++)
					{
						for(int x = start_map_x-1; x <= end_map_x+1; x++)
						{
							if(
								(x <= start_map_x+1 || x >= end_map_x-1) ||
								(y <= start_map_y+1 || y >= end_map_y-1)
								)
							{
								tilestoborder.push_back(Position(x,y,floor));
							}
							if(
								(
								(x == start_map_x || x == end_map_x) ||
								(y == start_map_y || y == end_map_y)
								) && (
								(x >= start_map_x && x <= end_map_x) &&
								(y >= start_map_y && y <= end_map_y)
								)
								)
							{
								tilestodraw.push_back(Position(x,y,floor));
							}
						}
					}
				} else {
					if(gui.GetBrushShape() == BRUSHSHAPE_SQUARE)
					{
						if(last_click_map_x > mouse_map_x)
						{int tmp = mouse_map_x; mouse_map_x = last_click_map_x; last_click_map_x = tmp;}
						if(last_click_map_y > mouse_map_y)
						{int tmp = mouse_map_y; mouse_map_y = last_click_map_y; last_click_map_y = tmp;}

						for(int x = last_click_map_x-1; x <= mouse_map_x+1; x++)
						{
							for(int y = last_click_map_y-1; y <= mouse_map_y+1; y ++)
							{
								if(
									(x <= last_click_map_x || x >= mouse_map_x) ||
									(y <= last_click_map_y || y >= mouse_map_y)
									)
								{
									tilestoborder.push_back(Position(x,y,floor));
								}
								if(
									(x >= last_click_map_x && x <= mouse_map_x) &&
									(y >= last_click_map_y && y <= mouse_map_y)
									)
								{
									tilestodraw.push_back(Position(x,y,floor));
								}
							}
						}
					} else {
						int start_x, end_x;
						int start_y, end_y;
						int width = std::max(
							std::abs(
							std::max(mouse_map_y, last_click_map_y) -
							std::min(mouse_map_y, last_click_map_y)
							),
							std::abs(
							std::max(mouse_map_x, last_click_map_x) -
							std::min(mouse_map_x, last_click_map_x)
							)
							);
						if(mouse_map_x < last_click_map_x)
						{
							start_x = last_click_map_x - width;
							end_x = last_click_map_x;
						} else {
							start_x = last_click_map_x;
							end_x = last_click_map_x + width;
						}
						if(mouse_map_y < last_click_map_y)
						{
							start_y = last_click_map_y - width;
							end_y = last_click_map_y;
						} else {
							start_y = last_click_map_y;
							end_y = last_click_map_y + width;
						}

						int center_x = start_x + (end_x - start_x) / 2;
						int center_y = start_y + (end_y - start_y) / 2;
						float radii = width / 2.0f + 0.005f;

						for(int y = start_y-1; y <= end_y+1; y++)
						{
							float dy = center_y - y;
							for(int x = start_x-1; x <= end_x+1; x++)
							{
								float dx = center_x - x;
								//printf("%f;%f\n", dx, dy);
								float distance = sqrt(dx*dx + dy*dy);
								if(distance < radii)
								{
									tilestodraw.push_back(Position(x,y,floor));
								}
								if(std::abs(distance - radii) < 1.5)
								{
									tilestoborder.push_back(Position(x,y,floor));
								}
							}
						}
					}
				}
				if(event.ControlDown())
				{
					editor.undraw(tilestodraw, tilestoborder, event.AltDown());
				} else {
					editor.draw(tilestodraw, tilestoborder, event.AltDown());
				}
			}
		}
		editor.actionQueue->resetTimer();
		drawing = false;
		dragging_draw = false;
		replace_dragging = false;
		editor.replace_brush = nullptr;
	}
	gui.RefreshView();
	gui.UpdateMinimap();
}

void MapCanvas::OnMouseCameraClick(wxMouseEvent& event)
{
	SetFocus();

	last_mmb_click_x = event.GetX();
	last_mmb_click_y = event.GetY();
	if(event.ControlDown())
	{
		int screensize_x, screensize_y;
		static_cast<MapWindow*>(GetParent())->GetViewSize(&screensize_x, &screensize_y);

		static_cast<MapWindow*>(GetParent())->ScrollRelative(
			int(-screensize_x * (1.0 - zoom) * (std::max(cursor_x, 1) / double(screensize_x))),
			int(-screensize_y * (1.0 - zoom) * (std::max(cursor_y, 1) / double(screensize_y)))
			);
		zoom = 1.0;
		Refresh();
	} else {
		screendragging = true;
	}
}

void MapCanvas::OnMouseCameraRelease(wxMouseEvent& event)
{
	SetFocus();
	screendragging = false;
	if(event.ControlDown())
	{
		// ...
		// Haven't moved much, it's a click!
	} else if(last_mmb_click_x > event.GetX() - 3 && last_mmb_click_x < event.GetX() + 3 &&
		last_mmb_click_y > event.GetY() - 3 && last_mmb_click_y < event.GetY() + 3)
	{
		int screensize_x, screensize_y;
		static_cast<MapWindow*>(GetParent())->GetViewSize(&screensize_x, &screensize_y);
		static_cast<MapWindow*>(GetParent())->ScrollRelative(
			int(zoom * (2*cursor_x - screensize_x)),
			int(zoom * (2*cursor_y - screensize_y))
			);
		Refresh();
	}
}

void MapCanvas::OnMousePropertiesClick(wxMouseEvent& event)
{
	SetFocus();

	int mouse_map_x, mouse_map_y;
	ScreenToMap(event.GetX(), event.GetY(), &mouse_map_x, &mouse_map_y);
	Tile* tile = editor.map.getTile(mouse_map_x, mouse_map_y, floor);

	if(gui.IsDrawingMode())
	{
		gui.SetSelectionMode();
	}

	EndPasting();

	boundbox_selection = false;
	if(event.ShiftDown())
	{
		boundbox_selection = true;

		if(!event.ControlDown())
		{
			editor.selection.start(); // Start selection session
			editor.selection.clear(); // Clear out selection
			editor.selection.finish(); // End selection session
			editor.selection.updateSelectionCount();
		}
	} else if(!tile)
	{
		editor.selection.start(); // Start selection session
		editor.selection.clear(); // Clear out selection
		editor.selection.finish(); // End selection session
		editor.selection.updateSelectionCount();
	} else if(tile->isSelected())
	{
		// Do nothing!
	} else {
		editor.selection.start(); // Start a selection session
		editor.selection.clear();
		editor.selection.commit();
		if(tile->spawn && settings.getInteger(Config::SHOW_SPAWNS))
		{
			editor.selection.add(tile, tile->spawn);
		} else if(tile->creature && settings.getInteger(Config::SHOW_CREATURES))
		{
			editor.selection.add(tile, tile->creature);
		} else {
			Item* item = tile->getTopItem();
			if(item)
			{
				editor.selection.add(tile, item);
			}
		}
		editor.selection.finish(); // Finish the selection session
		editor.selection.updateSelectionCount();
	}

	last_click_x = int(event.GetX()*zoom);
	last_click_y = int(event.GetY()*zoom);

	int start_x, start_y;
	static_cast<MapWindow*>(GetParent())->GetViewStart(&start_x, &start_y);
	last_click_abs_x = last_click_x + start_x;
	last_click_abs_y = last_click_y + start_y;

	last_click_map_x = mouse_map_x;
	last_click_map_y = mouse_map_y;
	gui.RefreshView();
}

void MapCanvas::OnMousePropertiesRelease(wxMouseEvent& event)
{
	int mouse_map_x, mouse_map_y;
	ScreenToMap(event.GetX(), event.GetY(), &mouse_map_x, &mouse_map_y);

	if(gui.IsDrawingMode())
	{
		gui.SetSelectionMode();
	}

	if(boundbox_selection)
	{
		if(mouse_map_x == last_click_map_x && mouse_map_y == last_click_map_y && event.ControlDown())
		{
			// Mouse hasn't move, do control+shift thingy!
			Tile* tile = editor.map.getTile(mouse_map_x, mouse_map_y, floor);
			if(tile)
			{
				editor.selection.start(); // Start a selection session
				if(tile->isSelected())
				{
					editor.selection.remove(tile);
				} else {
					editor.selection.add(tile);
				}
				editor.selection.finish(); // Finish the selection session
				editor.selection.updateSelectionCount();
			}
		} else {
			// The cursor has moved, do some boundboxing!
			if(last_click_map_x > mouse_map_x)
			{int tmp = mouse_map_x; mouse_map_x = last_click_map_x; last_click_map_x = tmp;}
			if(last_click_map_y > mouse_map_y)
			{int tmp = mouse_map_y; mouse_map_y = last_click_map_y; last_click_map_y = tmp;}

			editor.selection.start(); // Start a selection session
			switch(settings.getInteger(Config::SELECTION_TYPE))
			{
			case SELECT_CURRENT_FLOOR:
				{
					for(int x = last_click_map_x; x <= mouse_map_x; x++)
					{
						for(int y = last_click_map_y; y <= mouse_map_y; y ++)
						{
							Tile* tile = editor.map.getTile(x, y, floor);
							if(!tile) continue;
							editor.selection.add(tile);
						}
					}
					break;
				}
			case SELECT_ALL_FLOORS:
				{
					int start_x, start_y, start_z;
					int end_x, end_y, end_z;

					start_x = last_click_map_x;
					start_y = last_click_map_y;
					start_z = MAP_HEIGHT-1;
					end_x = mouse_map_x;
					end_y = mouse_map_y;
					end_z = floor;

					if(settings.getInteger(Config::COMPENSATED_SELECT))
					{
						start_x -= (floor < 7? 7-floor : 0);
						start_y -= (floor < 7? 7-floor : 0);

						end_x -= (floor < 7? 7-floor : 0);
						end_y -= (floor < 7? 7-floor : 0);
					}

					for(int z = start_z; z >= end_z; z--)
					{
						for(int x = start_x; x <= end_x; x++)
						{
							for(int y = start_y; y <= end_y; y++)
							{
								Tile* tile = editor.map.getTile(x, y, z);
								if(!tile) continue;
								editor.selection.add(tile);
							}
						}
						if(z <= 7 && settings.getInteger(Config::COMPENSATED_SELECT))
						{
							start_x++; start_y++;
							end_x++; end_y++;
						}
					}
					break;
				}
			case SELECT_VISIBLE_FLOORS:
				{
					int start_x, start_y, start_z;
					int end_x, end_y, end_z;

					start_x = last_click_map_x;
					start_y = last_click_map_y;
					if(floor < 8)
					{
						start_z = 7;
					} else {
						start_z = std::min(15, floor + 2);
					}
					end_x = mouse_map_x;
					end_y = mouse_map_y;
					end_z = floor;

					if(settings.getInteger(Config::COMPENSATED_SELECT))
					{
						start_x -= (floor < 7? 7-floor : 0);
						start_y -= (floor < 7? 7-floor : 0);

						end_x -= (floor < 7? 7-floor : 0);
						end_y -= (floor < 7? 7-floor : 0);
					}

					for(int z = start_z; z >= end_z; z--)
					{
						for(int x = start_x; x <= end_x; x++)
						{
							for(int y = start_y; y <= end_y; y++)
							{
								Tile* tile = editor.map.getTile(x, y, z);
								if(!tile) continue;
								editor.selection.add(tile);
							}
						}
						if(z <= 7 && settings.getInteger(Config::COMPENSATED_SELECT))
						{
							start_x++; start_y++;
							end_x++; end_y++;
						}
					}
					break;
				}
			}
			editor.selection.finish(); // Finish the selection session
			editor.selection.updateSelectionCount();
		}
	}
	else if(event.ControlDown())
	{
		// Nothing
	}


	popup_menu->Update();
	PopupMenu(popup_menu);

	editor.actionQueue->resetTimer();
	dragging = false;
	boundbox_selection = false;

	last_cursor_map_x = mouse_map_x;
	last_cursor_map_y = mouse_map_y;
	last_cursor_map_z = floor;

	gui.RefreshView();
}


void MapCanvas::OnWheel(wxMouseEvent& event)
{
	if(event.ControlDown())
	{
		static double diff = 0.0;
		diff += event.GetWheelRotation();
		if(diff <= 1.0 || diff >= 1.0)
		{
			if(diff < 0.0)
			{
				gui.ChangeFloor(floor - 1);
			} else {
				gui.ChangeFloor(floor + 1);
			}
			diff = 0.0;
		}
		UpdatePositionStatus();
	}
	else if(event.AltDown())
	{
		static double diff = 0.0;
		diff += event.GetWheelRotation();
		if(diff <= 1.0 || diff >= 1.0)
		{
			if(diff < 0.0)
			{
				gui.IncreaseBrushSize();
			} else {
				gui.DecreaseBrushSize();
			}
			diff = 0.0;
		}
	}
	else
	{
		double diff = -event.GetWheelRotation() * settings.getFloat(Config::ZOOM_SPEED) / 640.0;
		double oldzoom = zoom;
		zoom += diff;

		if(zoom < 0.125)
		{
			diff = 0.125 - oldzoom;
			zoom = 0.125;
		}
		if(zoom > 25.00)
		{
			diff = 25.00 - oldzoom;
			zoom = 25.0;
		}

		int screensize_x, screensize_y;
		static_cast<MapWindow*>(GetParent())->GetViewSize(&screensize_x, &screensize_y);

		// This took a day to figure out!
		int scroll_x = int(screensize_x * diff * (std::max(cursor_x, 1) / double(screensize_x)));
		int scroll_y = int(screensize_y * diff * (std::max(cursor_y, 1) / double(screensize_y)));

		static_cast<MapWindow*>(GetParent())->ScrollRelative(-scroll_x, -scroll_y);
	}


	Refresh();
}

void MapCanvas::OnLoseMouse(wxMouseEvent& event)
{
	Refresh();
}

void MapCanvas::OnGainMouse(wxMouseEvent& event)
{
	if(event.LeftIsDown() == false)
	{
		dragging = false;
		boundbox_selection = false;
		drawing = false;
	}
	if(event.MiddleIsDown() == false)
	{
		screendragging = false;
	}

	Refresh();
}

void MapCanvas::OnKeyDown(wxKeyEvent& event)
{
	//char keycode = event.GetKeyCode();
	// std::cout << "Keycode " << keycode << std::endl;
	switch(event.GetKeyCode())
	{
	case WXK_NUMPAD_ADD: case WXK_PAGEUP:
		{
			gui.ChangeFloor(floor - 1);
			break;
		}
	case WXK_NUMPAD_SUBTRACT: case WXK_PAGEDOWN:
		{
			gui.ChangeFloor(floor + 1);
			break;
		}
	case WXK_NUMPAD_MULTIPLY:
		{
			double diff = -0.3;

			double oldzoom = zoom;
			zoom += diff;

			if(zoom < 0.125)
			{diff = 0.125 - oldzoom; zoom = 0.125;}

			int screensize_x, screensize_y;
			static_cast<MapWindow*>(GetParent())->GetViewSize(&screensize_x, &screensize_y);

			// This took a day to figure out!
			int scroll_x = int(screensize_x * diff * (std::max(cursor_x, 1) / double(screensize_x)));
			int scroll_y = int(screensize_y * diff * (std::max(cursor_y, 1) / double(screensize_y)));

			static_cast<MapWindow*>(GetParent())->ScrollRelative(-scroll_x, -scroll_y);

			UpdatePositionStatus();
			Refresh();
			break;
		}
	case WXK_NUMPAD_DIVIDE:
		{
			double diff = 0.3;

			double oldzoom = zoom;
			zoom += diff;

			if(zoom > 25.00)
			{diff = 25.00 - oldzoom; zoom = 25.0;}

			int screensize_x, screensize_y;
			static_cast<MapWindow*>(GetParent())->GetViewSize(&screensize_x, &screensize_y);

			// This took a day to figure out!
			int scroll_x = int(screensize_x * diff * (std::max(cursor_x, 1) / double(screensize_x)));
			int scroll_y = int(screensize_y * diff * (std::max(cursor_y, 1) / double(screensize_y)));

			static_cast<MapWindow*>(GetParent())->ScrollRelative(-scroll_x, -scroll_y);

			UpdatePositionStatus();
			Refresh();
			break;
		}
		// This will work like crap with non-us layouts, well, sucks for them until there is another solution.
	case '[': case '+':
		{
			gui.IncreaseBrushSize();
			Refresh();
			break;
		}
	case ']': case '-':
		{
			gui.DecreaseBrushSize();
			Refresh();
			break;
		}
	case WXK_NUMPAD_UP: case WXK_UP:
		{
			int start_x, start_y;
			static_cast<MapWindow*>(GetParent())->GetViewStart(&start_x, &start_y);
			if(event.ControlDown())
			{
				static_cast<MapWindow*>(GetParent())->Scroll(start_x, int(start_y-32*10*zoom));
			} else {
				static_cast<MapWindow*>(GetParent())->Scroll(start_x, int(start_y-32*3*zoom));
			}
			UpdatePositionStatus();
			Refresh();
			break;
		}
	case WXK_NUMPAD_DOWN: case WXK_DOWN:
		{
			int start_x, start_y;
			static_cast<MapWindow*>(GetParent())->GetViewStart(&start_x, &start_y);
			if(event.ControlDown())
			{
				static_cast<MapWindow*>(GetParent())->Scroll(start_x, int(start_y+32*10*zoom));
			} else {
				static_cast<MapWindow*>(GetParent())->Scroll(start_x, int(start_y+32*3*zoom));
			}
			UpdatePositionStatus();
			Refresh();
			break;
		}
	case WXK_NUMPAD_LEFT: case WXK_LEFT:
		{
			int start_x, start_y;
			static_cast<MapWindow*>(GetParent())->GetViewStart(&start_x, &start_y);
			if(event.ControlDown())
			{
				static_cast<MapWindow*>(GetParent())->Scroll(int(start_x-32*10*zoom), start_y);
			} else {
				static_cast<MapWindow*>(GetParent())->Scroll(int(start_x-32*3*zoom), start_y);
			}
			UpdatePositionStatus();
			Refresh();
			break;
		}
	case WXK_NUMPAD_RIGHT: case WXK_RIGHT:
		{
			int start_x, start_y;
			static_cast<MapWindow*>(GetParent())->GetViewStart(&start_x, &start_y);
			if(event.ControlDown())
			{
				static_cast<MapWindow*>(GetParent())->Scroll(int(start_x+32*10*zoom), start_y);
			} else {
				static_cast<MapWindow*>(GetParent())->Scroll(int(start_x+32*3*zoom), start_y);
			}
			UpdatePositionStatus();
			Refresh();
			break;
		}

		// Utility keys
	case WXK_SPACE:
		{
			if(event.ControlDown())
			{
				gui.FillDoodadPreviewBuffer();
				gui.RefreshView();
			} else {
				gui.SwitchMode();
			}
			break;
		}

	case WXK_TAB:
		{ // Tab switch
			if(event.ShiftDown())
			{
				gui.CycleTab(false);
			} else {
				gui.CycleTab(true);
			}
			break;
		}

	case WXK_DELETE:
		{ // Delete
			editor.destroySelection();
			gui.RefreshView();
			break;
		}

	case 'z': case 'Z':
		{ // Rotate counterclockwise (actually shift variaton, but whatever... :P)
			int nv = gui.GetBrushVariation();
			--nv;
			if(nv < 0)
			{
				nv = max(0, (gui.GetCurrentBrush()? gui.GetCurrentBrush()->getMaxVariation() - 1 : 0));
			}
			gui.SetBrushVariation(nv);
			gui.RefreshView();
			break;
		}

	case 'x': case 'X':
		{ // Rotate clockwise (actually shift variaton, but whatever... :P)
			int nv = gui.GetBrushVariation();
			++nv;
			if(nv >= (gui.GetCurrentBrush()? gui.GetCurrentBrush()->getMaxVariation() : 0))
			{
				nv = 0;
			}
			gui.SetBrushVariation(nv);
			gui.RefreshView();
			break;
		}

	case 'q': case 'Q':
		{ // Select previous brush
			gui.SelectPreviousBrush();
			break;
		}
		// Hotkeys
	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
		{
			int index = event.GetKeyCode() - '0';
			if(event.ControlDown())
			{
				Hotkey hk;
				if(gui.IsSelectionMode())
				{
					int view_start_x, view_start_y;
					static_cast<MapWindow*>(GetParent())->GetViewStart(&view_start_x, &view_start_y);
					int view_start_map_x = view_start_x / 32, view_start_map_y = view_start_y / 32;

					int view_screensize_x, view_screensize_y;
					static_cast<MapWindow*>(GetParent())->GetViewSize(&view_screensize_x, &view_screensize_y);

					int map_x = int(view_start_map_x + (view_screensize_x*zoom) / 32 / 2);
					int map_y = int(view_start_map_y + (view_screensize_y*zoom) / 32 / 2);

					hk = Hotkey(Position(map_x, map_y, floor));
				} else if(gui.GetCurrentBrush())
				{
					// Drawing mode
					hk = Hotkey(gui.GetCurrentBrush());
				} else {
					break;
				}
				gui.SetHotkey(index, hk);
			} else {
				// Click hotkey
				Hotkey hk = gui.GetHotkey(index);
				if(hk.IsPosition())
				{
					gui.SetSelectionMode();

					int map_x = hk.GetPosition().x;
					int map_y = hk.GetPosition().y;
					int map_z = hk.GetPosition().z;

					static_cast<MapWindow*>(GetParent())->Scroll(32*map_x, 32*map_y, true);
					floor = map_z;

					gui.SetStatusText(wxT("Used hotkey ") + i2ws(index));
					gui.RefreshView();
				}
				else if(hk.IsBrush())
				{
					gui.SetDrawingMode();

					std::string name = hk.GetBrushname();
					Brush* brush = brushes.getBrush(name);
					if(brush == nullptr)
					{
						gui.SetStatusText(wxT("Brush \"") + wxstr(name) + wxT("\" not found"));
						return;
					}


					if(!gui.SelectBrush(brush))
					{
						gui.SetStatusText(wxT("Brush \"") + wxstr(name) + wxT("\" is not in any palette"));
						return;
					}

					gui.SetStatusText(wxT("Used hotkey ") + i2ws(index));
					gui.RefreshView();
				}
				else
				{
					gui.SetStatusText(wxT("Unassigned hotkey ") + i2ws(index));
				}
			}
			break;
		}
	default:
		{
			event.Skip();
			break;
		}
	}
}

void MapCanvas::OnCopy(wxCommandEvent& WXUNUSED(event))
{
	if(gui.IsSelectionMode())
	   editor.copybuffer.copy(editor, GetFloor());
}

void MapCanvas::OnCut(wxCommandEvent& WXUNUSED(event))
{
	if(gui.IsSelectionMode())
		editor.copybuffer.cut(editor, GetFloor());
	gui.RefreshView();
}

void MapCanvas::OnPaste(wxCommandEvent& WXUNUSED(event))
{
	gui.DoPaste();
	gui.RefreshView();
}

void MapCanvas::OnDelete(wxCommandEvent& WXUNUSED(event))
{
	editor.destroySelection();
	gui.RefreshView();
}

void MapCanvas::OnCopyPosition(wxCommandEvent& WXUNUSED(event))
{
	if (editor.selection.size() == 0)
		return;

	Position minPos = editor.selection.minPosition();
	Position maxPos = editor.selection.maxPosition();

	std::ostringstream clip;
	if (minPos != maxPos)
	{
		clip << "{";
		clip << "fromx = " << minPos.x << ", ";
		clip << "tox = " << maxPos.x << ", ";
		clip << "fromy = " << minPos.y << ", ";
		clip << "toy = " << maxPos.y << ", ";
		if (minPos.z != maxPos.z)
		{
			clip << "fromz = " << minPos.z << ", ";
			clip << "toz = " << maxPos.z;
		}
		else
			clip << "z = " << minPos.z;
		clip << "}";
	}
	else
	{
		clip << "{";
		clip << "x = " << minPos.x << ", ";
		clip << "y = " << minPos.y << ", ";
		clip << "z = " << minPos.z;
		clip << "}";
	}

	wxTextDataObject* obj = new wxTextDataObject();
	obj->SetText(wxstr(clip.str()));
	wxTheClipboard->SetData(obj);
}

void MapCanvas::OnRotateItem(wxCommandEvent& WXUNUSED(event))
{
	Tile* tile = editor.selection.getSelectedTile();

	Action* action = editor.actionQueue->createAction(ACTION_ROTATE_ITEM);

	Tile* new_tile = tile->deepCopy(editor.map);

	ItemVector selected_items = new_tile->getSelectedItems();
	ASSERT(selected_items.size() > 0);

	selected_items.front()->doRotate();

	action->addChange(newd Change(new_tile));

 	editor.actionQueue->addAction(action);
	gui.RefreshView();
}

void MapCanvas::OnGotoDestination(wxCommandEvent& WXUNUSED(event))
{
	Tile* tile = editor.selection.getSelectedTile();
	ItemVector selected_items = tile->getSelectedItems();
	ASSERT(selected_items.size() > 0);
	Teleport* teleport = dynamic_cast<Teleport*>(selected_items.front());
	if(teleport)
	{
		Position pos = teleport->getDestination();
		gui.CenterOnPosition(pos);
	}
}

void MapCanvas::OnSwitchDoor(wxCommandEvent& WXUNUSED(event))
{
	Tile* tile = editor.selection.getSelectedTile();

	Action* action = editor.actionQueue->createAction(ACTION_SWITCHDOOR);

	Tile* new_tile = tile->deepCopy(editor.map);

	ItemVector selected_items = new_tile->getSelectedItems();
	ASSERT(selected_items.size() > 0);

	DoorBrush::switchDoor(selected_items.front());

	action->addChange(newd Change(new_tile));

	editor.actionQueue->addAction(action);
	gui.RefreshView();
}

void MapCanvas::OnSelectRAWBrush(wxCommandEvent& WXUNUSED(event))
{
	if(editor.selection.size() != 1) return;
	Tile* tile = editor.selection.getSelectedTile();
	if(!tile) return;
	Item* item = tile->getTopSelectedItem();

	if(item && item->getRAWBrush())
		gui.SelectBrush(item->getRAWBrush(), TILESET_RAW);
}

void MapCanvas::OnSelectGroundBrush(wxCommandEvent& WXUNUSED(event))
{
	if(editor.selection.size() != 1) return;
	Tile* tile = editor.selection.getSelectedTile();
	if(!tile) return;
	GroundBrush* bb = tile->getGroundBrush();

	if(bb)
		gui.SelectBrush(bb, TILESET_TERRAIN);
}

void MapCanvas::OnSelectDoodadBrush(wxCommandEvent& WXUNUSED(event))
{
	if(editor.selection.size() != 1) return;
	Tile* tile = editor.selection.getSelectedTile();
	if(!tile) return;
	Item* item = tile->getTopSelectedItem();

	if(item)
		gui.SelectBrush(item->getDoodadBrush(), TILESET_DOODAD);
}

void MapCanvas::OnSelectDoorBrush(wxCommandEvent& WXUNUSED(event))
{
	if(editor.selection.size() != 1) return;
	Tile* tile = editor.selection.getSelectedTile();
	if(!tile) return;
	Item* item = tile->getTopSelectedItem();

	if(item)
		gui.SelectBrush(item->getDoorBrush(), TILESET_TERRAIN);
}

void MapCanvas::OnSelectWallBrush(wxCommandEvent& WXUNUSED(event))
{
	if(editor.selection.size() != 1) return;
	Tile* tile = editor.selection.getSelectedTile();
	if(!tile) return;
	Item* wall = tile->getWall();
	WallBrush* wb = wall->getWallBrush();

	if(wb)
		gui.SelectBrush(wb, TILESET_TERRAIN);
}

void MapCanvas::OnSelectCarpetBrush(wxCommandEvent& WXUNUSED(event))
{
	if(editor.selection.size() != 1) return;
	Tile* tile = editor.selection.getSelectedTile();
	if(!tile) return;
	Item* wall = tile->getCarpet();
	CarpetBrush* cb = wall->getCarpetBrush();

	if(cb)
		gui.SelectBrush(cb);
}

void MapCanvas::OnSelectTableBrush(wxCommandEvent& WXUNUSED(event))
{
	if(editor.selection.size() != 1) return;
	Tile* tile = editor.selection.getSelectedTile();
	if(!tile) return;
	Item* wall = tile->getTable();
	TableBrush* tb = wall->getTableBrush();

	if(tb)
		gui.SelectBrush(tb);
}

void MapCanvas::OnSelectHouseBrush(wxCommandEvent& WXUNUSED(event))
{
	Tile* tile = editor.selection.getSelectedTile();
	if(!tile)
		return;

	if(tile->isHouseTile())
	{
		House* house = editor.map.houses.getHouse(tile->getHouseID());
		if(house)
		{
			gui.house_brush->setHouse(house);
			gui.SelectBrush(gui.house_brush, TILESET_HOUSE);
		}
	}
}

void MapCanvas::OnSelectCreatureBrush(wxCommandEvent& WXUNUSED(event))
{
	Tile* tile = editor.selection.getSelectedTile();
	if(!tile)
		return;

	if(tile->creature)
		gui.SelectBrush(tile->creature->getBrush(), TILESET_CREATURE);
}

void MapCanvas::OnSelectSpawnBrush(wxCommandEvent& WXUNUSED(event))
{
	gui.SelectBrush(gui.spawn_brush, TILESET_CREATURE);
}

void MapCanvas::OnProperties(wxCommandEvent& WXUNUSED(event))
{
	if(editor.selection.size() != 1)
		return;

	Tile* tile = editor.selection.getSelectedTile();
	if(!tile) return;
	ASSERT(tile->isSelected());
	Tile* new_tile = tile->deepCopy(editor.map);

	wxDialog* w = nullptr;

	if(new_tile->spawn && settings.getInteger(Config::SHOW_SPAWNS))
		w = newd OldPropertiesWindow(gui.root, &editor.map, new_tile, new_tile->spawn);
	else if(new_tile->creature && settings.getInteger(Config::SHOW_CREATURES))
		w = newd OldPropertiesWindow(gui.root, &editor.map, new_tile, new_tile->creature);
	else
	{
		ItemVector selected_items = new_tile->getSelectedItems();

		Item* item = nullptr;
		int count = 0;
		for(ItemVector::iterator it = selected_items.begin(); it != selected_items.end(); ++it)
		{
			++count;
			if((*it)->isSelected())
			{
				item = *it;
			}
		}

		if (item)
		{
			if (editor.map.getVersion().otbm >= MAP_OTBM_4)
				w = newd PropertiesWindow(gui.root, &editor.map, new_tile, item);
			else
				w = newd OldPropertiesWindow(gui.root, &editor.map, new_tile, item);
		}
		else
			return;
	}

	int ret = w->ShowModal();
	if(ret != 0)
	{
		Action* action = editor.actionQueue->createAction(ACTION_CHANGE_PROPERTIES);
		action->addChange(newd Change(new_tile));
		editor.addAction(action);
	}
	else
	{
		// Cancel!
		delete new_tile;
	}
	w->Destroy();
}

void MapCanvas::ChangeFloor(int new_floor)
{
	ASSERT(new_floor >= 0 || new_floor <= 15);
	int old_floor = floor;
	floor = new_floor;
	if(old_floor != new_floor)
	{
		UpdatePositionStatus();
		gui.root->UpdateFloorMenu();
		gui.UpdateMinimap(true);
	}
	Refresh();
}

void MapCanvas::EnterDrawingMode()
{
	dragging = false;
	boundbox_selection = false;
	EndPasting();
	Refresh();
}

void MapCanvas::EnterSelectionMode()
{
	drawing = false;
	dragging_draw = false;
	replace_dragging = false;
	editor.replace_brush = nullptr;
	Refresh();
}

bool MapCanvas::isPasting() const
{
	return gui.isPasting();
}

void MapCanvas::StartPasting()
{
	gui.StartPasting();
}

void MapCanvas::EndPasting()
{
	gui.EndPasting();
}

void MapCanvas::Reset()
{
	cursor_x = 0;
	cursor_y = 0;

	zoom = 1.0;
	floor = 7;

	dragging = false;
	boundbox_selection = false;
	screendragging = false;
	drawing = false;
	dragging_draw = false;

	replace_dragging = false;
	editor.replace_brush = nullptr;

	drag_start_x = -1;
	drag_start_y = -1;
	drag_start_z = -1;

	last_click_map_x = -1;
	last_click_map_y = -1;
	last_click_map_z = -1;

	last_mmb_click_x = -1;
	last_mmb_click_y = -1;

	editor.selection.clear();
	editor.actionQueue->clear();
}

MapPopupMenu::MapPopupMenu(Editor& editor) : wxMenu(wxT("")), editor(editor)
{
	// ...
}

MapPopupMenu::~MapPopupMenu()
{
	// ...
}

void MapPopupMenu::Update()
{
	// Clear the menu of all items
	while(GetMenuItemCount() > 0)
	{
		wxMenuItem* m_item = FindItemByPosition(0);
		// If you add a submenu, this won't delete it.
		Delete(m_item);
	}

	bool anything_selected = editor.selection.size() != 0;

	wxMenuItem* cutItem = Append( MAP_POPUP_MENU_CUT, wxT("&Cut\tCTRL+X"), wxT("Cut out all selected items"));
	cutItem->Enable(anything_selected);

	wxMenuItem* copyItem = Append( MAP_POPUP_MENU_COPY, wxT("&Copy\tCTRL+C"), wxT("Copy all selected items"));
	copyItem->Enable(anything_selected);

	wxMenuItem* copyPositionItem = Append( MAP_POPUP_MENU_COPY_POSITION, wxT("&Copy Position"), wxT("Copy the position as a lua table"));
	copyPositionItem->Enable(anything_selected);

	wxMenuItem* pasteItem = Append( MAP_POPUP_MENU_PASTE, wxT("&Paste\tCTRL+V"), wxT("Paste items in the copybuffer here"));
	pasteItem->Enable(editor.copybuffer.canPaste());

	wxMenuItem* deleteItem = Append( MAP_POPUP_MENU_DELETE, wxT("&Delete\tDEL"), wxT("Removes all seleceted items"));
	deleteItem->Enable(anything_selected);

	if(anything_selected)
	{
		if(editor.selection.size() == 1)
		{
			Tile* tile = editor.selection.getSelectedTile();

			AppendSeparator();
			ItemVector selected_items = tile->getSelectedItems();

			bool hasWall = false;
			bool hasCarpet = false;
			bool hasTable = false;
			Item* topItem = nullptr;
			Item* topSelectedItem = (selected_items.size() == 1? selected_items.back() : nullptr);
			Creature* topCreature = tile->creature;
			Spawn* topSpawn = tile->spawn;

			for(ItemVector::iterator it = tile->items.begin(); it != tile->items.end(); ++it)
			{
				Item* iter_item = *it;
				if(iter_item->isWall())
				{
					Brush* wb = iter_item->getWallBrush();
					if(wb && wb->visibleInPalette()) hasWall = true;
				}
				if(iter_item->isTable())
				{
					Brush* tb = iter_item->getTableBrush();
					if(tb && tb->visibleInPalette()) hasTable = true;
				}
				if(iter_item->isCarpet())
				{
					Brush* cb = iter_item->getCarpetBrush();
					if(cb && cb->visibleInPalette()) hasCarpet = true;
				}
				if(iter_item->isSelected())
				{
					topItem = iter_item;
				}
			}
			if(!topItem)
			{
				topItem = tile->ground;
			}

			if(topSelectedItem || topCreature || topItem)
			{
				Teleport* teleport = dynamic_cast<Teleport*>(topSelectedItem);
				if(topSelectedItem && (topSelectedItem->isBrushDoor() || topSelectedItem->isRoteable() || teleport))
				{
					if(topSelectedItem->isRoteable())
					{
						Append( MAP_POPUP_MENU_ROTATE, wxT("&Rotate item"), wxT("Rotate this item"));
					}
					
					if(teleport && teleport->noDestination())
					{
						Append( MAP_POPUP_MENU_GOTO, wxT("&Goto Destination"), wxT("Goto the destination of this teleport"));
					}
					if(topSelectedItem->isOpen())
					{
						Append( MAP_POPUP_MENU_SWITCH_DOOR, wxT("&Close door"), wxT("Close this door"));
					}
					else
					{
						Append( MAP_POPUP_MENU_SWITCH_DOOR, wxT("&Open door"), wxT("Open this door"));
					}
					AppendSeparator();
				}

				if(topCreature)
					Append( MAP_POPUP_MENU_SELECT_CREATURE_BRUSH, wxT("Select Creature"), wxT("Uses the current creature as a creature brush"));

				if(topSpawn)
					Append( MAP_POPUP_MENU_SELECT_SPAWN_BRUSH, wxT("Select Spawn"), wxT("Select the spawn brush"));

				Append( MAP_POPUP_MENU_SELECT_RAW_BRUSH, wxT("Select RAW"), wxT("Uses the top item as a RAW brush"));

				if(hasWall)
					Append( MAP_POPUP_MENU_SELECT_WALL_BRUSH, wxT("Select Wallbrush"), wxT("Uses the current item as a wallbrush"));

				if(hasCarpet)
					Append( MAP_POPUP_MENU_SELECT_CARPET_BRUSH, wxT("Select Carpetbrush"), wxT("Uses the current item as a carpetbrush"));

				if(hasTable)
					Append( MAP_POPUP_MENU_SELECT_TABLE_BRUSH, wxT("Select Tablebrush"), wxT("Uses the current item as a tablebrush"));

				if(topSelectedItem && topSelectedItem->getDoodadBrush() && topSelectedItem->getDoodadBrush()->visibleInPalette())
					Append( MAP_POPUP_MENU_SELECT_DOODAD_BRUSH, wxT("Select Doodadbrush"), wxT("Use this doodad brush"));

				if(topSelectedItem && topSelectedItem->isBrushDoor() && topSelectedItem->getDoorBrush())
					Append( MAP_POPUP_MENU_SELECT_DOOR_BRUSH, wxT("Select Doorbrush"), wxT("Use this door brush"));

				if(tile->hasGround() && tile->getGroundBrush() && tile->getGroundBrush()->visibleInPalette())
					Append( MAP_POPUP_MENU_SELECT_GROUND_BRUSH, wxT("Select Groundbrush"), wxT("Uses the current item as a groundbrush"));

				if(tile->isHouseTile())
					Append(MAP_POPUP_MENU_SELECT_HOUSE_BRUSH, wxT("Select House"), wxT("Draw with the house on this tile."));

				AppendSeparator();
				Append( MAP_POPUP_MENU_PROPERTIES, wxT("&Properties"), wxT("Properties for the current object"));
			}
			else
			{

				if(topCreature)
					Append( MAP_POPUP_MENU_SELECT_CREATURE_BRUSH, wxT("Select Creature"), wxT("Uses the current creature as a creature brush"));

				if(topSpawn)
					Append( MAP_POPUP_MENU_SELECT_SPAWN_BRUSH, wxT("Select Spawn"), wxT("Select the spawn brush"));

				Append( MAP_POPUP_MENU_SELECT_RAW_BRUSH, wxT("Select RAW"), wxT("Uses the top item as a RAW brush"));
				if(hasWall)
				{
					Append( MAP_POPUP_MENU_SELECT_WALL_BRUSH, wxT("Select Wallbrush"), wxT("Uses the current item as a wallbrush"));
				}
				if(tile->hasGround() && tile->getGroundBrush() && tile->getGroundBrush()->visibleInPalette())
				{
					Append( MAP_POPUP_MENU_SELECT_GROUND_BRUSH, wxT("Select Groundbrush"), wxT("Uses the current tile as a groundbrush"));
				}

				if(tile->isHouseTile())
				{
					Append(MAP_POPUP_MENU_SELECT_HOUSE_BRUSH, wxT("Select House"), wxT("Draw with the house on this tile."));
				}

				if (tile->hasGround() || topCreature || topSpawn)
				{
					AppendSeparator();
					Append( MAP_POPUP_MENU_PROPERTIES, wxT("&Properties"), wxT("Properties for the current object"));
				}
			}
		}
	}
}

void MapCanvas::getTilesToDraw(int mouse_map_x, int mouse_map_y, int floor, 
							   PositionVector* tilestodraw, PositionVector* tilestoborder)
{
	for(int y = -gui.GetBrushSize()-1; y <= gui.GetBrushSize()+1; y++)
	{
		for(int x = -gui.GetBrushSize()-1; x <= gui.GetBrushSize()+1; x++)
		{
			if(gui.GetBrushShape() == BRUSHSHAPE_SQUARE)
			{
				if(x >= -gui.GetBrushSize() && x <= gui.GetBrushSize() && y >= -gui.GetBrushSize() && y <= gui.GetBrushSize())
				{
					if(tilestodraw)
						tilestodraw->push_back(Position(mouse_map_x+x,mouse_map_y+y, floor));
				}
				if(std::abs(x) - gui.GetBrushSize() < 2 && std::abs(y) - gui.GetBrushSize() < 2)
				{
					if(tilestoborder)
						tilestoborder->push_back(Position(mouse_map_x+x,mouse_map_y+y, floor));
				}
			}
			else if(gui.GetBrushShape() == BRUSHSHAPE_CIRCLE)
			{
				double distance = sqrt(double(x*x) + double(y*y));
				if(distance < gui.GetBrushSize()+0.005)
				{
					if(tilestodraw)
						tilestodraw->push_back(Position(mouse_map_x+x,mouse_map_y+y, floor));
				}
				if(std::abs(distance - gui.GetBrushSize()) < 1.5)
				{
					if(tilestoborder)
						tilestoborder->push_back(Position(mouse_map_x+x,mouse_map_y+y, floor));
				}
			}
		}
	}
}
