// ==============================================================
//	This file is part of Glest (www.glest.org)
//
//	Copyright (C) 2001-2008 Martiño Figueroa
//
//	You can redistribute this code and/or modify it under
//	the terms of the GNU General Public License as published
//	by the Free Software Foundation; either version 2 of the
//	License, or (at your option) any later version
// ==============================================================

#include "main.h"
#include <ctime>
#include "conversion.h"
#include "icons.h"
#include "platform_common.h"
#include "config.h"
#include <iostream>
#include"platform_util.h"
#include <wx/stdpaths.h>
#ifndef WIN32
#include <errno.h>
#endif
#include "common_scoped_ptr.h"
//#include <memory>

using namespace Shared::Util;
using namespace Shared::PlatformCommon;
using namespace Glest::Game;
using namespace std;

namespace Glest { namespace Game {
string getGameReadWritePath(string lookupKey) {
	string path = "";
    if(path == "" && getenv("GLESTHOME") != NULL) {
        path = getenv("GLESTHOME");
        if(path != "" && EndsWith(path, "/") == false && EndsWith(path, "\\") == false) {
            path += "/";
        }

        //SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] path to be used for read/write files [%s]\n",__FILE__,__FUNCTION__,__LINE__,path.c_str());
    }

    return path;
}
}}

namespace MapEditor {

const string mapeditorVersionString = "v3.13.0";
const string MainWindow::winHeader = "MegaGlest Map Editor " + mapeditorVersionString;

// ===============================================
//	class Global functions
// ===============================================

wxString ToUnicode(const char* str) {
	return wxString(str, wxConvUTF8);
}

wxString ToUnicode(const string& str) {
	return wxString(str.c_str(), wxConvUTF8);
}

// ===============================================
// class MainWindow
// ===============================================

MainWindow::MainWindow(string appPath)
		: wxFrame(NULL, -1,  ToUnicode(winHeader), wxPoint(0,0), wxSize(1024, 768))
		, glCanvas(NULL)
		, program(NULL)
		, lastX(0)
		, lastY(0)
		, panel(NULL)
		, menuBar(NULL)
		, fileDialog(NULL)

		, currentBrush(btHeight)
		, height(0)
		, surface(1)
		, radius(1)
		, object(0)
		, resource(0)
		, startLocation(1)
		, enabledGroup(ctHeight)
		, fileModified(false)
        , boxsizer(NULL)
        ,startupSettingsInited(false) {

	menuFile=NULL;
	menuEdit=NULL;
	menuEditMirror=NULL;
	menuView=NULL;
	menuBrush=NULL;
	menuBrushHeight=NULL;
	menuBrushGradient=NULL;
	menuBrushSurface=NULL;
	menuBrushObject=NULL;
	menuBrushResource=NULL;
	menuBrushStartLocation=NULL;
	menuRadius=NULL;
	fileDialog=NULL;
	resourceUnderMouse=0;
	objectUnderMouse=0;

	// default values for random height calculation that turned out to be quite useful
	randomWithReset=true;
	randomMinimumHeight=-300;
	randomMaximumHeight=400;
	randomChanceDivider=30;
	randomRecursions=3;

	this->appPath = appPath;
	Properties::setApplicationPath(executable_path(appPath));

	this->panel = new wxPanel(this, wxID_ANY);
}

void MainWindow::onToolPlayer(wxCommandEvent& event){
	PopupMenu(menuBrushStartLocation);
}

void MainToolBar::onMouseMove(wxMouseEvent &event) {
#ifdef WIN32
	if(this->GetParent() != NULL && this->GetParent()->GetParent() != NULL) {
		MainWindow *mainWindow = dynamic_cast<MainWindow *>(this->GetParent()->GetParent());
		if(mainWindow != NULL) {
			mainWindow->refreshMapRender();
		}
	}
#endif
	event.Skip();
}

BEGIN_EVENT_TABLE(MainToolBar, wxToolBar)

	EVT_MOTION(MainToolBar::onMouseMove)

END_EVENT_TABLE()

void MainWindow::init(string fname) {

	//menus
	menuBar = new wxMenuBar();

	//file
	menuFile = new wxMenu();
	menuFile->Append(wxID_NEW);
	menuFile->Append(wxID_OPEN);
	menuFile->AppendSeparator();
	menuFile->Append(wxID_SAVE);
	menuFile->Append(wxID_SAVEAS);
	menuFile->AppendSeparator();
	menuFile->Append(wxID_EXIT);
	menuBar->Append(menuFile, wxT("&File"));

	//edit
	menuEdit = new wxMenu();
	menuEdit->Append(miEditUndo, wxT("&Undo\tCTRL+Z"));
	menuEdit->Append(miEditRedo, wxT("&Redo\tCTRL+Y"));
	menuEdit->AppendSeparator();
//	menuEdit->Append(miEditReset, wxT("Rese&t..."));
	menuEdit->Append(miEditResetPlayers, wxT("Reset &Players..."));
	menuEdit->Append(miEditResize, wxT("Re&size..."));
	menuEdit->Append(miEditFlipDiagonal, wxT("Flip &Diagonal"));
	menuEdit->Append(miEditFlipX, wxT("Flip &X"));
	menuEdit->Append(miEditFlipY, wxT("Flip &Y"));

    // Mirror submenu-------------------------------------------
	menuEditMirror = new wxMenu();
	menuEditMirror->Append(miEditMirrorX, wxT("Copy &Left to Right"));
	menuEditMirror->Append(miEditMirrorY, wxT("Copy &Top to Bottom"));
	menuEditMirror->Append(miEditMirrorXY, wxT("Copy &BottomLeft to TopRight"));
	menuEditMirror->AppendSeparator();
	menuEditMirror->Append(miEditRotatecopyX, wxT("&Rotate Left to Right"));
	menuEditMirror->Append(miEditRotatecopyY, wxT("Rotate T&op to Bottom"));
	menuEditMirror->Append(miEditRotatecopyXY, wxT("Rotate Botto&mLeft to TopRight"));
	menuEditMirror->Append(miEditRotatecopyCorner, wxT("Rotate TopLeft &corner to TopRight"));
    menuEdit->Append(miEditMirror, wxT("&Mirror"), menuEditMirror);
    // ---------------------------------------------------------

	menuEdit->Append(miEditRandomizeHeights, wxT("Randomize &Heights"));
	menuEdit->Append(miEditRandomize, wxT("Randomi&ze Players"));
	menuEdit->Append(miEditSwitchSurfaces, wxT("Switch Sur&faces..."));
	menuEdit->Append(miEditInfo, wxT("&Info..."));
	menuEdit->Append(miEditAdvanced, wxT("&Advanced..."));
	menuBar->Append(menuEdit, wxT("&Edit"));

	//view
	menuView = new wxMenu();
	menuView->Append(miViewResetZoomAndPos, wxT("&Reset zoom and pos"));
    menuView->AppendCheckItem(miViewGrid, wxT("&Grid"));
	menuView->AppendCheckItem(miViewHeightMap, wxT("H&eightMap"));
	menuView->AppendCheckItem(miHideWater, wxT("Hide&Water"));
	menuView->AppendSeparator();
	menuView->Append(miViewHelp, wxT("&Help..."));
	menuView->Append(miViewAbout, wxT("&About..."));
	menuBar->Append(menuView, wxT("&View"));

	//brush
	// menuBrush = new wxMenu();

	//surface
	menuBrushSurface = new wxMenu();
	menuBrushSurface->AppendCheckItem(miBrushSurface + 1, wxT("&Grass"));
	menuBrushSurface->AppendCheckItem(miBrushSurface + 2, wxT("S&econdary grass"));
	menuBrushSurface->AppendCheckItem(miBrushSurface + 3, wxT("&Road"));
	menuBrushSurface->AppendCheckItem(miBrushSurface + 4, wxT("&Stone"));
	menuBrushSurface->AppendCheckItem(miBrushSurface + 5, wxT("Gr&ound"));
	menuBar->Append(menuBrushSurface, wxT("&Surface"));

	//resources
	menuBrushResource = new wxMenu();
	//menuBrushResource->AppendCheckItem(miBrushResource + 1, wxT("&0 - None"));
	menuBrushResource->AppendCheckItem(miBrushResource+2, wxT("&Gold  (unwalkable)"));
	menuBrushResource->AppendCheckItem(miBrushResource+3, wxT("&Stone (unwalkable)"));
	menuBrushResource->AppendCheckItem(miBrushResource+4, wxT("&3 - custom"));
	menuBrushResource->AppendCheckItem(miBrushResource+5, wxT("&4 - custom"));
	menuBrushResource->AppendCheckItem(miBrushResource+6, wxT("&5 - custom"));
	menuBar->Append(menuBrushResource, wxT("&Resource"));

	//objects
	menuBrushObject = new wxMenu();
	menuBrushObject->AppendCheckItem(miBrushObject+1, wxT("&None (erase)\tALT+0"));
	menuBrushObject->AppendSeparator();
	menuBrushObject->AppendCheckItem(miBrushObject+2, wxT("&Tree (harvestable)"));
	menuBrushObject->AppendCheckItem(miBrushObject+3, wxT("&Dead tree/Cactuses/Thornbush"));
	menuBrushObject->AppendCheckItem(miBrushObject+4, wxT("&Stone"));
	menuBrushObject->AppendCheckItem(miBrushObject+5, wxT("&Bush/Grass/Fern (walkable)"));
	menuBrushObject->AppendCheckItem(miBrushObject+6, wxT("&Water object/Reed/Papyrus (walkable)"));
	menuBrushObject->AppendCheckItem(miBrushObject+7, wxT("Big tree/&Old palm"));
	menuBrushObject->AppendCheckItem(miBrushObject+8, wxT("&Hanged/Impaled "));
	menuBrushObject->AppendCheckItem(miBrushObject+9, wxT("St&atues"));
	menuBrushObject->AppendCheckItem(miBrushObject+10, wxT("&Mountain"));
	menuBrushObject->AppendCheckItem(miBrushObject+11, wxT("&Invisible blocking object"));
	menuBar->Append(menuBrushObject, wxT("&Object"));

	// ZombiePirate height brush
	menuBrushGradient = new wxMenu();
	for (int i = 0; i < heightCount; ++i) {
		menuBrushGradient->AppendCheckItem(miBrushGradient + i + 1, ToUnicode((i>4?"&":"") +intToStr(i - heightCount / 2)));
	}
	menuBar->Append(menuBrushGradient, wxT("&Gradient"));

	// Glest height brush
	menuBrushHeight = new wxMenu();
	for (int i = 0; i < heightCount; ++i) {
		menuBrushHeight->AppendCheckItem(miBrushHeight + i + 1, ToUnicode((i>4?"&":"") +intToStr(i - heightCount / 2)));
	}
	menuBrushHeight->Check(miBrushHeight + (heightCount + 1) / 2, true);
	menuBar->Append(menuBrushHeight, wxT("&Height"));

	enabledGroup = ctHeight;

	//radius
	menuRadius = new wxMenu();
	for (int i = 1; i <= radiusCount; ++i) {
		menuRadius->AppendCheckItem(miRadius + i, ToUnicode("&" + intToStr(i) + " (diameter "+intToStr(i*2-1)+ ")"+ "\tALT+" + intToStr(i)));
	}
	menuRadius->Check(miRadius + 1, true);
	menuBar->Append(menuRadius, wxT("R&adius"));

	//players
	menuBrushStartLocation = new wxMenu();
	wxMenuItem *pmi1 = new wxMenuItem(menuBrushStartLocation, miBrushStartLocation + 1, wxT("Player &1 Red"));
	pmi1->SetBitmap(wxBitmap(brush_players_red));
	menuBrushStartLocation->Append(pmi1);
	wxMenuItem *pmi2 = new wxMenuItem(menuBrushStartLocation, miBrushStartLocation + 2, wxT("Player &2 Blue"));
	pmi2->SetBitmap(wxBitmap(brush_players_blue));
	menuBrushStartLocation->Append(pmi2);
	wxMenuItem *pmi3 = new wxMenuItem(menuBrushStartLocation, miBrushStartLocation + 3, wxT("Player &3 Green"));
	pmi3->SetBitmap(wxBitmap(brush_players_green));
	menuBrushStartLocation->Append(pmi3);
	wxMenuItem *pmi4 = new wxMenuItem(menuBrushStartLocation, miBrushStartLocation + 4, wxT("Player &4 Yellow"));
	pmi4->SetBitmap(wxBitmap(brush_players_yellow));
	menuBrushStartLocation->Append(pmi4);
	wxMenuItem *pmi5 = new wxMenuItem(menuBrushStartLocation, miBrushStartLocation + 5, wxT("Player &5 White"));
	pmi5->SetBitmap(wxBitmap(brush_players_white));
	menuBrushStartLocation->Append(pmi5);
	wxMenuItem *pmi6 = new wxMenuItem(menuBrushStartLocation, miBrushStartLocation + 6, wxT("Player &6 Cyan"));
	pmi6->SetBitmap(wxBitmap(brush_players_cyan));
	menuBrushStartLocation->Append(pmi6);
	wxMenuItem *pmi7 = new wxMenuItem(menuBrushStartLocation, miBrushStartLocation + 7, wxT("Player &7 Orange"));
	pmi7->SetBitmap(wxBitmap(brush_players_orange));
	menuBrushStartLocation->Append(pmi7);
	wxMenuItem *pmi8 = new wxMenuItem(menuBrushStartLocation, miBrushStartLocation + 8, wxT("Player &8 Pink")); // = Light Magenta :-)
	pmi8->SetBitmap(wxBitmap(brush_players_pink));
	menuBrushStartLocation->Append(pmi8);
	menuBar->Append(menuBrushStartLocation, wxT("&Player"));
	//menuBar->Append(menuBrush, wxT("&Brush"));


	SetMenuBar(menuBar);

	fileName = "New (unsaved) map";
	int status_widths[siCOUNT] = {
		10, // empty
		-25, // File name
		-6, // File type
		-20, // Current Object
		-14, // Brush Type
		-20, // Brush 'Value'
		-10, // Brush Radius
		-25, // Position
	};
	CreateStatusBar(siCOUNT);
	GetStatusBar()->SetStatusWidths(siCOUNT, status_widths);

	SetStatusText(wxT("File: ") + ToUnicode(fileName), siFILE_NAME);
	SetStatusText(wxT(".mgm"), siFILE_TYPE);
	SetStatusText(wxT("Object: None (Erase)"), siCURR_OBJECT);
	SetStatusText(wxT("Brush: Height"), siBRUSH_TYPE);
	SetStatusText(wxT("Value: 0"), siBRUSH_VALUE);
	SetStatusText(wxT("Radius: 1"), siBRUSH_RADIUS);
	SetStatusText(wxT("Pos (Ingame): 0"), siPOS_VALUE);

	wxToolBar *toolbar = new MainToolBar(this->panel, wxID_ANY);
	toolbar->AddTool(miEditUndo, _("undo"), wxBitmap(edit_undo), _("Undo"));
	toolbar->AddTool(miEditRedo, _("redo"), wxBitmap(edit_redo), _("Redo"));
	toolbar->AddTool(miEditRandomizeHeights, _("randomizeHeights"), wxBitmap(edit_randomize_heights), _("Randomize Heights"));
//	toolbar->AddTool(miEditRandomize, _("randomize"), wxBitmap(edit_randomize), _("Randomize"));
	toolbar->AddTool(miEditSwitchSurfaces, _("switch"), wxBitmap(edit_switch_surfaces), _("Switch Surfaces"));
	toolbar->AddSeparator();
	toolbar->AddTool(miBrushSurface + 1, _("brush_grass1"), wxBitmap(brush_surface_grass1), _("Grass"));
	toolbar->AddTool(miBrushSurface + 2, _("brush_grass2"), wxBitmap(brush_surface_grass2), _("Secondary grass"));
	toolbar->AddTool(miBrushSurface + 3, _("brush_road"), wxBitmap(brush_surface_road), _("Road"));
	toolbar->AddTool(miBrushSurface + 4, _("brush_stone"), wxBitmap(brush_surface_stone), _("Stone"));
	toolbar->AddTool(miBrushSurface + 5, _("brush_custom"), wxBitmap(brush_surface_custom), _("Ground"));
	toolbar->AddSeparator();
	toolbar->AddTool(miBrushResource + 2, _("resource1"), wxBitmap(brush_resource_1_gold), _("gold  (unwalkable)"));
	toolbar->AddTool(miBrushResource + 3, _("resource2"), wxBitmap(brush_resource_2_stone), _("stone (unwalkable)"));
	toolbar->AddTool(miBrushResource + 4, _("resource3"), wxBitmap(brush_resource_3), _("custom3"));
	toolbar->AddTool(miBrushResource + 5, _("resource4"), wxBitmap(brush_resource_4), _("custom4"));
	toolbar->AddTool(miBrushResource + 6, _("resource5"), wxBitmap(brush_resource_5), _("custom5"));
	toolbar->AddSeparator();
	toolbar->AddTool(miBrushObject + 1, _("brush_none"), wxBitmap(brush_none), _("None (erase)"));
	toolbar->AddTool(miBrushObject + 2, _("brush_tree"), wxBitmap(brush_object_tree), _("Tree (unwalkable/harvestable)"));
	toolbar->AddTool(miBrushObject + 3, _("brush_dead_tree"), wxBitmap(brush_object_dead_tree), _("Dead tree/Cactuses/Thornbush (unwalkable)"));
	toolbar->AddTool(miBrushObject + 4, _("brush_stone"), wxBitmap(brush_object_stone), _("Stone (unwalkable/not harvestable)"));
	toolbar->AddTool(miBrushObject + 5, _("brush_bush"), wxBitmap(brush_object_bush), _("Bush/Grass/Fern (walkable)"));
	toolbar->AddTool(miBrushObject + 6, _("brush_water"), wxBitmap(brush_object_water_object), _("Water object/Reed/Papyrus (walkable)"));
	toolbar->AddTool(miBrushObject + 7, _("brush_c1_bigtree"), wxBitmap(brush_object_c1_bigtree), _("Big tree/Old palm (unwalkable/not harvestable)"));
	toolbar->AddTool(miBrushObject + 8, _("brush_c2_hanged"), wxBitmap(brush_object_c2_hanged), _("Hanged/Impaled (unwalkable)"));
	toolbar->AddTool(miBrushObject + 9, _("brush_c3_statue"), wxBitmap(brush_object_c3_statue), _("Statues (unwalkable)"));
	toolbar->AddTool(miBrushObject +10, _("brush_c4_bigrock"), wxBitmap(brush_object_c4_bigrock), _("Mountain (unwalkable)"));
	toolbar->AddTool(miBrushObject +11, _("brush_c5_blocking"), wxBitmap(brush_object_c5_blocking), _("Invisible blocking object (unwalkable)"));
	toolbar->AddSeparator();
	toolbar->AddTool(toolPlayer, _("brush_player"), wxBitmap(brush_players_player),  _("Player start position"));
	toolbar->Realize();

	wxToolBar *toolbar2 = new MainToolBar(this->panel, wxID_ANY);
	toolbar2->AddTool(miBrushGradient + 1, _("brush_gradient_n5"), wxBitmap(brush_gradient_n5));
	toolbar2->AddTool(miBrushGradient + 2, _("brush_gradient_n4"), wxBitmap(brush_gradient_n4));
	toolbar2->AddTool(miBrushGradient + 3, _("brush_gradient_n3"), wxBitmap(brush_gradient_n3));
	toolbar2->AddTool(miBrushGradient + 4, _("brush_gradient_n2"), wxBitmap(brush_gradient_n2));
	toolbar2->AddTool(miBrushGradient + 5, _("brush_gradient_n1"), wxBitmap(brush_gradient_n1));
	toolbar2->AddTool(miBrushGradient + 6, _("brush_gradient_0"), wxBitmap(brush_gradient_0));
	toolbar2->AddTool(miBrushGradient + 7, _("brush_gradient_p1"), wxBitmap(brush_gradient_p1));
	toolbar2->AddTool(miBrushGradient + 8, _("brush_gradient_p2"), wxBitmap(brush_gradient_p2));
	toolbar2->AddTool(miBrushGradient + 9, _("brush_gradient_p3"), wxBitmap(brush_gradient_p3));
	toolbar2->AddTool(miBrushGradient +10, _("brush_gradient_p4"), wxBitmap(brush_gradient_p4));
	toolbar2->AddTool(miBrushGradient +11, _("brush_gradient_p5"), wxBitmap(brush_gradient_p5));
	toolbar2->AddSeparator();
	toolbar2->AddTool(miBrushHeight + 1, _("brush_height_n5"), wxBitmap(brush_height_n5));
	toolbar2->AddTool(miBrushHeight + 2, _("brush_height_n4"), wxBitmap(brush_height_n4));
	toolbar2->AddTool(miBrushHeight + 3, _("brush_height_n3"), wxBitmap(brush_height_n3));
	toolbar2->AddTool(miBrushHeight + 4, _("brush_height_n2"), wxBitmap(brush_height_n2));
	toolbar2->AddTool(miBrushHeight + 5, _("brush_height_n1"), wxBitmap(brush_height_n1));
	toolbar2->AddTool(miBrushHeight + 6, _("brush_height_0"), wxBitmap(brush_height_0));
	toolbar2->AddTool(miBrushHeight + 7, _("brush_height_p1"), wxBitmap(brush_height_p1));
	toolbar2->AddTool(miBrushHeight + 8, _("brush_height_p2"), wxBitmap(brush_height_p2));
	toolbar2->AddTool(miBrushHeight + 9, _("brush_height_p3"), wxBitmap(brush_height_p3));
	toolbar2->AddTool(miBrushHeight +10, _("brush_height_p4"), wxBitmap(brush_height_p4));
	toolbar2->AddTool(miBrushHeight +11, _("brush_height_p5"), wxBitmap(brush_height_p5));
	toolbar2->AddSeparator();
	toolbar2->AddTool(miRadius + 1, _("radius1"), wxBitmap(radius_1), _("1 (1x1)"));
	toolbar2->AddTool(miRadius + 2, _("radius2"), wxBitmap(radius_2), _("2 (3x3)"));
	toolbar2->AddTool(miRadius + 3, _("radius3"), wxBitmap(radius_3), _("3 (5x5)"));
	toolbar2->AddTool(miRadius + 4, _("radius4"), wxBitmap(radius_4), _("4 (7x7)"));
	toolbar2->AddTool(miRadius + 5, _("radius5"), wxBitmap(radius_5), _("5 (9x9)"));
	toolbar2->AddTool(miRadius + 6, _("radius6"), wxBitmap(radius_6), _("6 (11x11)"));
	toolbar2->AddTool(miRadius + 7, _("radius7"), wxBitmap(radius_7), _("7 (13x13)"));
	toolbar2->AddTool(miRadius + 8, _("radius8"), wxBitmap(radius_8), _("8 (15x15)"));
	toolbar2->AddTool(miRadius + 9, _("radius9"), wxBitmap(radius_9), _("9 (17x17)"));
	toolbar2->Realize();

	Config &config = Config::getInstance();

    string userData = config.getString("UserData_Root","");
    if(userData != "") {
    	endPathWithSlash(userData);
    }

	//std::cout << "A" << std::endl;
	wxInitAllImageHandlers();
#ifdef WIN32
	//std::cout << "B" << std::endl;
//	#if defined(__MINGW32__)
		wxIcon icon(ToUnicode("IDI_ICON1"));
//	#else
//		wxIcon icon("IDI_ICON1");
//	#endif

#else
	//std::cout << "B" << std::endl;
	wxIcon icon;
	string iniFilePath = extractDirectoryPathFromFile(config.getFileName(false));
	string icon_file = iniFilePath + "editor.ico";
	std::ifstream testFile(icon_file.c_str());
	if(testFile.good())	{
		testFile.close();
		icon.LoadFile(ToUnicode(icon_file.c_str()),wxBITMAP_TYPE_ICO);
	}
#endif
	//std::cout << "C" << std::endl;
	SetIcon(icon);
	fileDialog = new wxFileDialog(this);
    string defaultPath = userData + "maps/";
    fileDialog->SetDirectory(ToUnicode(defaultPath));

	//printf("Default Path [%s]\n",defaultPath.c_str());

	lastPaintEvent.start();

	boxsizer = new wxBoxSizer(wxVERTICAL);
	boxsizer->Add(toolbar, 0, wxEXPAND);
	boxsizer->Add(toolbar2, 0, wxEXPAND);
	//boxsizer->Add(glCanvas, 1, wxEXPAND);

	this->panel->SetSizer(boxsizer);
	//this->Layout();

	//program = new Program(glCanvas->GetClientSize().x, glCanvas->GetClientSize().y);

	fileName = "New (unsaved) Map";

	//printf("Does file exist, fname [%s]\n",fname.c_str());

	if (fname.empty() == false && fileExists(fname)) {
		//printf("YES file exist, fname [%s]\n",fname.c_str());

		//program->loadMap(fname);
		currentFile = fname;
		fileName = cutLastExt(extractFileFromDirectoryPath(fname.c_str()));
		fileDialog->SetPath(ToUnicode(fname));
	}
	SetTitle(ToUnicode(currentFile + " - " + winHeader));
	//setDirty(false);
	//setExtension();

	initGlCanvas();
	#if wxCHECK_VERSION(2, 9, 3)
		//glCanvas->setCurrentGLContext();
		//printf("setcurrent #1\n");
	#elif wxCHECK_VERSION(2, 9, 1)

	#else
		if(glCanvas) glCanvas->SetCurrent();
		//printf("setcurrent #2\n");
	#endif

	if(startupSettingsInited == false) {
		startupSettingsInited = true;
		setupStartupSettings();
	}
}

void MainWindow::onClose(wxCloseEvent &event) {
	if(program != NULL && program->getMap()->getHasChanged() == true) {
		if( wxMessageDialog(NULL, ToUnicode("Do you want to save the current map?"),
			ToUnicode("Question"), wxYES_NO | wxYES_DEFAULT).ShowModal() == wxID_YES) {
			wxCommandEvent ev;
			MainWindow::onMenuFileSave(ev);
		}
	}
	delete program;
	program = NULL;

	//delete glCanvas;
	if(glCanvas) glCanvas->Destroy();
	glCanvas = NULL;

	this->Destroy();
}

void MainWindow::initGlCanvas(){
	if(glCanvas == NULL) {
		int args[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_MIN_ALPHA,  8 };
		glCanvas = new GlCanvas(this, this->panel, args);

		boxsizer->Add(glCanvas, 1, wxEXPAND);
	}
}

void MainWindow::setupStartupSettings() {

	//gl canvas
	if(glCanvas == NULL) {
		initGlCanvas();

		boxsizer->Add(glCanvas, 1, wxEXPAND);

		this->panel->SetSizer(boxsizer);
		this->Layout();
	}
	glCanvas->setCurrentGLContext();
	glCanvas->SetFocus();

	string playerName = Config::getInstance().getString("NetPlayerName","");
	program = new Program(glCanvas->GetClientSize().x, glCanvas->GetClientSize().y, playerName);
	fileName = "New (unsaved) Map";

	//printf("#0 file load [%s]\n",currentFile.c_str());

	if (!currentFile.empty() && fileExists(currentFile)) {
		//printf("#0 exists file load [%s]\n",currentFile.c_str());

		program->loadMap(currentFile);
		//currentFile = fname;
		fileName = cutLastExt(extractFileFromDirectoryPath(currentFile.c_str()));
		fileDialog->SetPath(ToUnicode(currentFile));
	}
	SetTitle(ToUnicode(currentFile + " - " + winHeader));
	setDirty(false);
	setExtension();
}

MainWindow::~MainWindow() {
	delete fileDialog;
	fileDialog = NULL;

	delete program;
	program = NULL;

	delete glCanvas;
	glCanvas = NULL;
}

void MainWindow::setDirty(bool val) {
	wxPaintEvent ev;
	onPaint(ev);
	if (fileModified && val) {
		return;
	}
	fileModified = val;
	if (fileModified) {
		SetStatusText(wxT("File: ") + ToUnicode(fileName) + wxT("*"), siFILE_NAME);
	} else {
		SetStatusText(wxT("File: ") + ToUnicode(fileName), siFILE_NAME);
	}
}

void MainWindow::setExtension() {
	if (currentFile.empty() || program == NULL) {
		return;
	}

	string extnsn = ext(currentFile);

	//printf("#A currentFile [%s] extnsn [%s]\n",currentFile.c_str(),extnsn.c_str());

	if (extnsn == "gbm" || extnsn == "mgm") {
		currentFile = cutLastExt(currentFile);
	}
	SetStatusText(wxT(".mgm"), siFILE_TYPE);
	currentFile += ".mgm";
}

void MainWindow::onMouseDown(wxMouseEvent &event, int x, int y) {
	if (event.LeftIsDown() && program != NULL) {
		program->setUndoPoint(enabledGroup);
		program->setRefAlt(x, y);
		change(x, y);
		if (!isDirty()) {
			setDirty(true);
		}
		wxPaintEvent ev;
		onPaint(ev);
	}
	event.Skip();
}

// for the mousewheel
void MainWindow::onMouseWheelDown(wxMouseEvent &event) {
	if(program == NULL) {
		return;
	}
	wxPaintEvent ev;
	program->incCellSize(1);
	onPaint(ev);
}

void MainWindow::onMouseWheelUp(wxMouseEvent &event) {
	if(program == NULL) {
		return;
	}
	wxPaintEvent ev;
	program->incCellSize(-1);
	onPaint(ev);
}

void MainWindow::onMouseMove(wxMouseEvent &event, int x, int y) {
	if(program == NULL) {
		return;
	}
	bool repaint = false;
	if (event.LeftIsDown()) {
		change(x, y);
		repaint = true;
	} else if (event.MiddleIsDown()) {
		int dif = (y - lastY);
		if (dif != 0) {
			program->incCellSize(dif / abs(dif));
			repaint = true;
		}
	} else if (event.RightIsDown()) {
		program->setOfset(x - lastX, y - lastY);
		repaint = true;
	} else {
		int currResource = program->getResource(x, y);
		if (currResource > 0) {
			SetStatusText(wxT("Resource: ") + ToUnicode(resource_descs[currResource]), siCURR_OBJECT);
			resourceUnderMouse = currResource;
			objectUnderMouse = 0;
		} else {
			int currObject = program->getObject(x, y);
			SetStatusText(wxT("Object: ") + ToUnicode(object_descs[currObject]), siCURR_OBJECT);
			resourceUnderMouse = 0;
			objectUnderMouse = currObject;
		}

		SetStatusText(wxT("Pos (Ingame): ")
				+ ToUnicode(intToStr(program->getCellX(x))
				+ ","
				+ intToStr(program->getCellY(y))
				+ " ("
				+ intToStr(2*(program->getCellX(x)))
				+ ","
				+ intToStr(2*(program->getCellY(y)))
				+ ")"), siPOS_VALUE);
//#ifdef WIN32
		//repaint = true;
//#endif
	}
	lastX = x;
	lastY = y;

	if (repaint) {
		wxPaintEvent ev;
		onPaint(ev);
	}
	event.Skip();
}

void MainWindow::onPaint(wxPaintEvent &event) {
	//printf("onPaint map\n");

	if(!IsShown()) {
		//printf("onPaint skip map\n");

		event.Skip();
		return;
	}

//#if wxCHECK_VERSION(2, 9, 3)

//#elif wxCHECK_VERSION(2, 9, 1)
//	glCanvas->setCurrentGLContext();
//#endif

	//static bool contextSet = false;
	//if(contextSet == false) {
	//	contextSet = true;
	glCanvas->setCurrentGLContext();
	//}

	//printf("lastPaintEvent.getMillis() map\n");
	if(lastPaintEvent.getMillis() < 30) {
		sleep(1);
		event.Skip();
		return;
	}

	//printf("wxPaintDC dc map\n");
	wxPaintDC dc(this); // "In a paint event handler must always create a wxPaintDC object even if you do not use it.  (?)
	                    //  Otherwise, under MS Windows, refreshing for this and other windows will go wrong"
                        //  http://docs.wxwidgets.org/2.6/wx_wxpaintevent.html

	lastPaintEvent.start();

	if(panel) panel->Refresh(false);
	if(menuBar) menuBar->Refresh(false);

	refreshMapRender();
	event.Skip();
}

void MainWindow::refreshMapRender() {
	//printf("refreshMapRender map\n");

	if(program && glCanvas) {
		program->renderMap(glCanvas->GetClientSize().x, glCanvas->GetClientSize().y);
		glCanvas->SwapBuffers();
	}
}

void MainWindow::onMenuFileLoad(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	try {
		fileDialog->SetMessage(wxT("Select Glestmap to load"));
		fileDialog->SetWildcard(wxT("Glest&Mega Map (*.gbm *.mgm)|*.gbm;*.mgm|Glest Map (*.gbm)|*.gbm|Mega Map (*.mgm)|*.mgm"));
		if (fileDialog->ShowModal() == wxID_OK) {
#ifdef WIN32
			const wxWX2MBbuf tmp_buf = wxConvCurrent->cWX2MB(wxFNCONV(fileDialog->GetPath()));
			currentFile = tmp_buf;

			auto_ptr<wchar_t> wstr(Ansi2WideString(currentFile.c_str()));
			currentFile = utf8_encode(wstr.get());
#else
			//currentFile = fileDialog->GetPath().ToAscii();

			const wxWX2MBbuf tmp_buf = wxConvCurrent->cWX2MB(fileDialog->GetPath());
			currentFile = tmp_buf;
#endif

			//printf("#1 file load [%s]\n",currentFile.c_str());

			program->loadMap(currentFile);
			fileName = cutLastExt(extractFileFromDirectoryPath(currentFile.c_str()));
			setDirty(false);
			setExtension();
			SetTitle(ToUnicode(winHeader + "; " + currentFile));
		}
	}
	catch (const string &e) {
		MsgDialog(this, ToUnicode(e), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal();
	}
	catch (const exception &e) {
		MsgDialog(this, ToUnicode(e.what()), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal();
	}

}

void MainWindow::onMenuFileSave(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	if (currentFile.empty()) {
		wxCommandEvent ev;
		onMenuFileSaveAs(ev);
	}
	else {
		setExtension();

		//printf("#1 save load [%s]\n",currentFile.c_str());

		program->saveMap(currentFile);
		setDirty(false);
	}
}

void MainWindow::onMenuFileSaveAs(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

#if wxCHECK_VERSION(2, 9, 1)
	wxFileDialog fd(this, wxT("Select file"), wxT(""), wxT(""), wxT("*.mgm|*.gbm"), wxFD_SAVE);
#else
	wxFileDialog fd(this, wxT("Select file"), wxT(""), wxT(""), wxT("*.mgm|*.gbm"), wxSAVE);
#endif

	if(fileDialog->GetPath() != ToUnicode("")) {
		fd.SetPath(fileDialog->GetPath());
	}
	else {
		Config &config = Config::getInstance();
		string userData = config.getString("UserData_Root","");
		if(userData != "") {
			endPathWithSlash(userData);
		}
		string defaultPath = userData + "maps/";
		fd.SetDirectory(ToUnicode(defaultPath));
	}

	fd.SetWildcard(wxT("MegaGlest Map (*.mgm)|*.mgm|Glest Map (*.gbm)|*.gbm"));
	if (fd.ShowModal() == wxID_OK) {
#ifdef WIN32
		const wxWX2MBbuf tmp_buf = wxConvCurrent->cWX2MB(wxFNCONV(fd.GetPath()));
		currentFile = tmp_buf;

		auto_ptr<wchar_t> wstr(Ansi2WideString(currentFile.c_str()));
		currentFile = utf8_encode(wstr.get());
#else
		 //currentFile = fd.GetPath().ToAscii();
		const wxWX2MBbuf tmp_buf = wxConvCurrent->cWX2MB(fd.GetPath());
		currentFile = tmp_buf;
#endif

		fileDialog->SetPath(fd.GetPath());
		setExtension();

		//printf("#2 file save [%s]\n",currentFile.c_str());

		program->saveMap(currentFile);
		fileName = cutLastExt(extractFileFromDirectoryPath(currentFile.c_str()));
		setDirty(false);
	}
	SetTitle(ToUnicode(winHeader + "; " + currentFile));
}

void MainWindow::onMenuFileExit(wxCommandEvent &event) {
	Close();
}

void MainWindow::onMenuEditUndo(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	// std::cout << "Undo Pressed" << std::endl;
	if (program->undo()) {
		wxPaintEvent e;
		onPaint(e);
		setDirty();
	}
}

void MainWindow::onMenuEditRedo(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	if (program->redo()) {
		wxPaintEvent e;
		onPaint(e);
		setDirty();
	}
}

void MainWindow::onMenuEditReset(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	program->setUndoPoint(ctAll);
	SimpleDialog simpleDialog;
	simpleDialog.addValue("Width", "128","(must be 16,32,64,128,256,512...)"); // must be an exponent of two
	simpleDialog.addValue("Height", "128","(must be 16,32,64,128,256,512...)");
	simpleDialog.addValue("Surface", "1","(Default surface material)");
	simpleDialog.addValue("Altitude", "10","(Default surface height)");
	simpleDialog.addValue("Number of players", "8");
	if (!simpleDialog.show()) return;

	try {
		program->reset(
			strToInt(simpleDialog.getValue("Width")),
			strToInt(simpleDialog.getValue("Height")),
			strToInt(simpleDialog.getValue("Altitude")),
			strToInt(simpleDialog.getValue("Surface")));
			program->resetFactions(strToInt(simpleDialog.getValue("Number of players")));
	}
	catch (const exception &e) {
		MsgDialog(this, ToUnicode(e.what()), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal();
	}
	currentFile = "";
	fileName = "New (unsaved) map";

	wxPaintEvent ev;
	onPaint(ev);
}

void MainWindow::onMenuEditResetPlayers(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	SimpleDialog simpleDialog;
	simpleDialog.addValue("Number of players", intToStr(program->getMap()->getMaxFactions()));
	if (!simpleDialog.show("Reset players")) return;

	try {
		program->resetFactions(strToInt(simpleDialog.getValue("Number of players")));
	}
	catch (const exception &e) {
		MsgDialog(this, ToUnicode(e.what()), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal();
	}
	setDirty();
	setExtension();
}


void MainWindow::onMenuEditFlipDiagonal(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}
	program->setUndoPoint(ctAll);
		program->flipDiagonal();
		setDirty();
}

void MainWindow::onMenuEditResize(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	SimpleDialog simpleDialog;
	simpleDialog.addValue("Width", intToStr(program->getMap()->getW()),"(must be 16,32,64,128,256,512...)");
	simpleDialog.addValue("Height", intToStr(program->getMap()->getH()),"(must be 16,32,64,128,256,512...)");
	simpleDialog.addValue("Surface", "1","(surface material for new area around map)");
	simpleDialog.addValue("Altitude", "10","(surface height for new area around map)");
	if (!simpleDialog.show("Resize - expand around, shrink to topleft")) return;

	try {
		program->resize(
			strToInt(simpleDialog.getValue("Width")),
			strToInt(simpleDialog.getValue("Height")),
			strToInt(simpleDialog.getValue("Altitude")),
			strToInt(simpleDialog.getValue("Surface")));
	}
	catch (const exception &e) {
		MsgDialog(this, ToUnicode(e.what()), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal();
	}
	setDirty();
}

void MainWindow::onMenuEditFlipX(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	program->setUndoPoint(ctAll);
	program->flipX();
	setDirty();
}

void MainWindow::onMenuEditFlipY(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	program->setUndoPoint(ctAll);
	program->flipY();
	setDirty();
}

void MainWindow::onMenuEditMirrorX(wxCommandEvent &event) { // copy left to right
	if(program == NULL) {
		return;
	}

	program->setUndoPoint(ctAll);
	program->mirrorX();
	setDirty();
}

void MainWindow::onMenuEditMirrorY(wxCommandEvent &event) { // copy top to bottom
	if(program == NULL) {
		return;
	}

	program->setUndoPoint(ctAll);
	program->mirrorY();
	setDirty();
}

void MainWindow::onMenuEditMirrorXY(wxCommandEvent &event) { // copy bottomleft tp topright
	if(program == NULL) {
		return;
	}

	program->setUndoPoint(ctAll);
	program->mirrorXY();
	setDirty();
}

void MainWindow::onMenuEditRotatecopyX(wxCommandEvent &event) { // copy left to right, rotated
	if(program == NULL) {
		return;
	}

	program->setUndoPoint(ctAll);
	program->rotatecopyX();
	setDirty();
}

void MainWindow::onMenuEditRotatecopyY(wxCommandEvent &event) { // copy top to bottom, rotated
	if(program == NULL) {
		return;
	}

	program->setUndoPoint(ctAll);
	program->rotatecopyY();
	setDirty();
}

void MainWindow::onMenuEditRotatecopyXY(wxCommandEvent &event) { // copy bottomleft to topright, rotated
	if(program == NULL) {
		return;
	}

	program->setUndoPoint(ctAll);
	program->rotatecopyXY();
	setDirty();
}

void MainWindow::onMenuEditRotatecopyCorner(wxCommandEvent &event) { // copy top left 1/4 to top right 1/4, rotated
	if(program == NULL) {
		return;
	}

	program->setUndoPoint(ctAll);
	program->rotatecopyCorner();
	setDirty();
}


void MainWindow::onMenuEditRandomizeHeights(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}
	while(true){

		SimpleDialog simpleDialog;
		simpleDialog.addValue("Initial Reset", boolToStr(randomWithReset),"(1 = true, 0 = false) If set to '0' no height reset is done before calculating");
		simpleDialog.addValue("Min Height", intToStr(randomMinimumHeight),"Lowest random height. example: -300 or below if you want water , 0 if you don't want water.");
		simpleDialog.addValue("Max Height", intToStr(randomMaximumHeight),"Max random height. A good value is 400");
		simpleDialog.addValue("Chance Divider", intToStr(randomChanceDivider),"Defines how often you get a hill or hole default is 30. Bigger number, less hills/holes.");
		simpleDialog.addValue("Smooth Recursions", intToStr(randomRecursions),"Number of recursions cycles to smooth the hills and holes. 0<x<50 default is 3.");
		if (!simpleDialog.show("Randomize Height")) return;

		try {
			string checkValue = simpleDialog.getValue("Initial Reset");
			if(checkValue != "" && strToInt(checkValue) > 1) {
				randomWithReset = true;
			}
			else {
				randomWithReset = strToBool(simpleDialog.getValue("Initial Reset"));
			}
			randomMinimumHeight=strToInt(simpleDialog.getValue("Min Height"));
			randomMaximumHeight=strToInt(simpleDialog.getValue("Max Height"));
			randomChanceDivider=strToInt(simpleDialog.getValue("Chance Divider"));
			randomRecursions=strToInt(simpleDialog.getValue("Smooth Recursions"));

			// set insane inputs to something that does not crash
			if(randomMinimumHeight>=randomMaximumHeight) randomMinimumHeight=randomMaximumHeight-1;
			if(randomChanceDivider<1) randomChanceDivider=1;

			// set randomRecursions to something useful
			if(randomRecursions<0) randomRecursions=0;
			if(randomRecursions>50) randomRecursions=50;

			program->setUndoPoint(ctAll);
			program->randomizeMapHeights(randomWithReset, randomMinimumHeight, randomMaximumHeight,
					randomChanceDivider, randomRecursions);
		}
		catch (const exception &e) {
			MsgDialog(this, ToUnicode(e.what()), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal();
		}
		setDirty();
	}
}

void MainWindow::onMenuEditRandomize(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	program->setUndoPoint(ctAll);
	program->randomizeFactions();
	setDirty();
}

void MainWindow::onMenuEditSwitchSurfaces(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	SimpleDialog simpleDialog;
	simpleDialog.addValue("Surface1", "1","replace this surface with...");
	simpleDialog.addValue("Surface2", "2","...this and vice versa");
	if (!simpleDialog.show("Switch surfaces")) return;

	try {
        program->setUndoPoint(ctSurface);
		program->switchMapSurfaces(
			strToInt(simpleDialog.getValue("Surface1")),
			strToInt(simpleDialog.getValue("Surface2")));
	}
	catch (const exception &e) {
		MsgDialog(this, ToUnicode(e.what()), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal();
	}
	setDirty();
}

void MainWindow::onMenuEditInfo(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	SimpleDialog simpleDialog;
	simpleDialog.addValue("Title", program->getMap()->getTitle());
	simpleDialog.addValue("Description", program->getMap()->getDesc());
	simpleDialog.addValue("Author", program->getMap()->getAuthor());
	if (!simpleDialog.show("Info",true)) return;

    bool ischanged = program->setMapTitle(simpleDialog.getValue("Title"));
	ischanged = (program->setMapDesc(simpleDialog.getValue("Description")) || ischanged);
	ischanged = (program->setMapAuthor(simpleDialog.getValue("Author")) || ischanged);
	if (ischanged)
		if (!isDirty()) {
			setDirty(true);
		}

}

void MainWindow::onMenuEditAdvanced(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	SimpleDialog simpleDialog;
	simpleDialog.addValue("Height Factor", intToStr(program->getMap()->getHeightFactor()),"lower means more hill effect. Numbers above 100 are handled like this:\nx=x/100 ,so a 150 will mean 1.5 in the game.");
	simpleDialog.addValue("Water Level", intToStr(program->getMap()->getWaterLevel()),"water is visible below this, and walkable until 1.5 less");
	simpleDialog.addValue("Cliff Level", intToStr(program->getMap()->getCliffLevel()),"neighboring fields with at least this heights difference are cliffs");
	simpleDialog.addValue("Camera Height", intToStr(program->getMap()->getCameraHeight()),"you can give a camera height here default is 0 ;ignored if <20");
	if (!simpleDialog.show("Advanced")) return;

	try {
		program->setMapAdvanced(
			strToInt(simpleDialog.getValue("Height Factor")),
			strToInt(simpleDialog.getValue("Water Level")),
			strToInt(simpleDialog.getValue("Cliff Level")),
			strToInt(simpleDialog.getValue("Camera Height")));
	}
	catch (const exception &e) {
		MsgDialog(this, ToUnicode(e.what()), wxT("Exception"), wxOK | wxICON_ERROR).ShowModal();
	}
	setDirty();
}

void MainWindow::onMenuViewResetZoomAndPos(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	program->resetOfset();
	wxPaintEvent e;
	onPaint(e);
}

void MainWindow::onMenuViewGrid(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	menuView->Check(miViewGrid, program->setGridOnOff());    // miViewGrid event.GetId()
	wxPaintEvent e;
	onPaint(e);
}


void MainWindow::onMenuViewHeightMap(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	menuView->Check(miViewHeightMap, program->setHeightMapOnOff());    // miViewGrid event.GetId()
	wxPaintEvent e;
	onPaint(e);
}
void MainWindow::onMenuHideWater(wxCommandEvent &event) {
	if(program == NULL) {
		return;
	}

	menuView->Check(miHideWater, program->setHideWaterOnOff());    // miViewGrid event.GetId()
	wxPaintEvent e;
	onPaint(e);
}
void MainWindow::onMenuViewAbout(wxCommandEvent &event) {
	MsgDialog(
		this,
		wxT("\n    Glest Map Editor\n    Copyright 2004-2010 The Glest Team\n    Copyright 2010-2017 The MegaGlest Team    \n"),
		wxT("About")).ShowModal();
}

void MainWindow::onMenuViewHelp(wxCommandEvent &event) {
	MsgDialog(this,
		wxT("Draw with left mouse\nMove viewport with right mouse drag\nZoom with center mouse drag, or mousewheel\n\n\
You can change brush in the same category with key 1-9\n\
and change category with their first letter (keys S, R, O, G, H)\n\
Press Space to set brush to the resource or object under the mouse cursor\n\
To center things in the map shift it with Shift-Up/Down/Left/Right keys\n\
Height tool (blue) builds with integer height steps 0-20, \nwhile Gradient tool (red) uses any real number \n\
Units can go over water as long as it is less than 1.5 deep\n\n\
A good idea is to put some stone, gold and tree near starting position\n\
Starting position needs an open area for the tower and the starting units\n"),
		wxT("Help")).ShowModal();
		/* 5 away and 10x10 empty area? */
}

void MainWindow::onMenuBrushHeight(wxCommandEvent &e) {
	uncheckBrush();
	menuBrushHeight->Check(e.GetId(), true);
	height = e.GetId() - miBrushHeight - heightCount / 2 - 1;
	enabledGroup = ctHeight;
	currentBrush = btHeight;
	SetStatusText(wxT("Brush: Height"), siBRUSH_TYPE);
	SetStatusText(wxT("Value: ") + ToUnicode(intToStr(height)), siBRUSH_VALUE);
}

void MainWindow::onMenuBrushGradient(wxCommandEvent &e) {
	uncheckBrush();
	menuBrushGradient->Check(e.GetId(), true);
	height = e.GetId() - miBrushGradient - heightCount / 2 - 1;
	enabledGroup = ctGradient;
	currentBrush = btGradient;
	SetStatusText(wxT("Brush: Gradient"), siBRUSH_TYPE);
	SetStatusText(wxT("Value: ") + ToUnicode(intToStr(height)), siBRUSH_VALUE);
}


void MainWindow::onMenuBrushSurface(wxCommandEvent &e) {
	uncheckBrush();
	menuBrushSurface->Check(e.GetId(), true);
	surface = e.GetId() - miBrushSurface;
	enabledGroup = ctSurface;
	currentBrush = btSurface;
	SetStatusText(wxT("Brush: Surface"), siBRUSH_TYPE);
	SetStatusText(
		wxT("Value: ") + ToUnicode(intToStr(surface)) + wxT(" ")
		+ ToUnicode(surface_descs[surface - 1]), siBRUSH_VALUE);
}

void MainWindow::onMenuBrushObject(wxCommandEvent &e) {
	uncheckBrush();
	menuBrushObject->Check(e.GetId(), true);
	object = e.GetId() - miBrushObject - 1;
	enabledGroup = ctObject;
	currentBrush = btObject;
	SetStatusText(wxT("Brush: Object"), siBRUSH_TYPE);
	SetStatusText(
		wxT("Value: ") + ToUnicode(intToStr(object)) + wxT(" ")
		+ ToUnicode(object_descs[object]), siBRUSH_VALUE);
}

void MainWindow::onMenuBrushResource(wxCommandEvent &e) {
	uncheckBrush();
	menuBrushResource->Check(e.GetId(), true);
	resource = e.GetId() - miBrushResource - 1;
	enabledGroup = ctResource;
	currentBrush = btResource;
	SetStatusText(wxT("Brush: Resource"), siBRUSH_TYPE);
	SetStatusText(
		wxT("Value: ") + ToUnicode(intToStr(resource)) + wxT(" ")
		+ ToUnicode(resource_descs[resource]), siBRUSH_VALUE);
}

void MainWindow::onMenuBrushStartLocation(wxCommandEvent &e) {
	uncheckBrush();
	menuBrushStartLocation->Check(e.GetId(), true);
	startLocation = e.GetId() - miBrushStartLocation;
	enabledGroup = ctLocation;
	currentBrush = btStartLocation;
	SetStatusText(wxT("Brush: Start Locations"), siBRUSH_TYPE);
	SetStatusText(wxT("Value: ") + ToUnicode(intToStr(startLocation)), siBRUSH_VALUE);
}

void MainWindow::onMenuRadius(wxCommandEvent &e) {
	uncheckRadius();
	menuRadius->Check(e.GetId(), true);
	radius = e.GetId() - miRadius;
	SetStatusText(wxT("Radius: ") + ToUnicode(intToStr(radius)), siBRUSH_RADIUS);
}

void MainWindow::change(int x, int y) {
	if(program == NULL) {
		return;
	}

	switch (enabledGroup) {
	case ctHeight:
		program->glestChangeMapHeight(x, y, height, radius);
		break;
	case ctSurface:
		program->changeMapSurface(x, y, surface, radius);
		break;
	case ctObject:
		program->changeMapObject(x, y, object, radius);
		break;
	case ctResource:
		program->changeMapResource(x, y, resource, radius);
		break;
	case ctLocation:
		program->changeStartLocation(x, y, startLocation - 1);
		break;
	case ctGradient:
		program->pirateChangeMapHeight(x, y, height, radius);
		break;
	}
}

void MainWindow::uncheckBrush() {
	for (int i = 0; i < heightCount; ++i) {
		menuBrushHeight->Check(miBrushHeight + i + 1, false);
	}
	for (int i = 0; i < heightCount; ++i) {
		menuBrushGradient->Check(miBrushGradient + i + 1, false);
	}
	for (int i = 0; i < surfaceCount; ++i) {
		menuBrushSurface->Check(miBrushSurface + i + 1, false);
	}
	for (int i = 0; i < objectCount; ++i) {
		menuBrushObject->Check(miBrushObject + i + 1, false);
	}
	for (int i = 0; i < resourceCount; ++i) {
		menuBrushResource->Check(miBrushResource + i + 1, false);
	}
	for (int i = 0; i < startLocationCount; ++i) {
		menuBrushStartLocation->Check(miBrushStartLocation + i + 1, false);
	}
}

void MainWindow::uncheckRadius() {
	for (int i = 1; i <= radiusCount; ++i) {
		menuRadius->Check(miRadius + i, false);
	}
}

 void MainWindow::onKeyDown(wxKeyEvent &e) {
	if(program == NULL) {
		return;
	}

	if(e.GetModifiers() == wxMOD_CONTROL || e.GetModifiers() == wxMOD_ALT){
		e.Skip();
	}
	// WARNING: don't add any Ctrl or ALt key shortcuts below those are reserved for internal menu use.

 	if (currentBrush == btSurface) { // surface texture
 		if (e.GetKeyCode() >= '1' && e.GetKeyCode() <= '5') {
 			surface = e.GetKeyCode() - 48; // '1'-'5' == 1-5
 			wxCommandEvent evt(wxEVT_NULL, miBrushSurface + surface);
 			onMenuBrushSurface(evt);
 			return;
 		}
 	}
 	if (currentBrush == btObject) {
 		bool valid = true;
 		if (e.GetKeyCode() >= '1' && e.GetKeyCode() <= '9') {
 			object = e.GetKeyCode() - 48; // '1'-'9' == 1-9
 		} else if (e.GetKeyCode() == '0') { // '0' == 10
 			object = 10;
 		} else if (e.GetKeyCode() == '-') {	// '-' == 0
 			object = 0;
 		} else {
 			valid = false;
 		}
 		if (valid) {
 			wxCommandEvent evt(wxEVT_NULL, miBrushObject + object + 1);
 			onMenuBrushObject(evt);
 			return;
 		}
 	}
 	if (currentBrush == btResource) {
 		if (e.GetKeyCode() >= '0' && e.GetKeyCode() <= '5') {
 			resource = e.GetKeyCode() - 48;	// '0'-'5' == 0-5
 			wxCommandEvent evt(wxEVT_NULL, miBrushResource + resource + 1);
 			onMenuBrushResource(evt);
 			return;
 		}
 	}
 	if (currentBrush == btStartLocation) {
 		if (e.GetKeyCode() >= '1' && e.GetKeyCode() <= '8') {
 			startLocation = e.GetKeyCode() - 48; // '1'-'8' == 0-7
 			wxCommandEvent evt(wxEVT_NULL, miBrushStartLocation + startLocation);
 			onMenuBrushStartLocation(evt);
 			return;
 		}
 	}
 	if (e.GetKeyCode() == 'H') {
 		wxCommandEvent evt(wxEVT_NULL, miBrushHeight + height + heightCount / 2 + 1);
 		onMenuBrushHeight(evt);
 	} else if (e.GetKeyCode() == ' ') {
		if (resourceUnderMouse != 0) {
			wxCommandEvent evt(wxEVT_NULL, miBrushResource + resourceUnderMouse + 1);
 			onMenuBrushResource(evt);
		} else {
			wxCommandEvent evt(wxEVT_NULL, miBrushObject + objectUnderMouse + 1);
 			onMenuBrushObject(evt);
		}
 	} else if (e.GetKeyCode() == 'G') {
 		wxCommandEvent evt(wxEVT_NULL, miBrushGradient + height + heightCount / 2 + 1);
 		onMenuBrushGradient(evt);
 	} else if (e.GetKeyCode() == 'S') {
		wxCommandEvent evt(wxEVT_NULL, miBrushSurface + surface);
		onMenuBrushSurface(evt);
 	} else if (e.GetKeyCode() == 'O') {
 		wxCommandEvent evt(wxEVT_NULL, miBrushObject + object + 1);
 		onMenuBrushObject(evt);
 	} else if (e.GetKeyCode() == 'R') {
 		wxCommandEvent evt(wxEVT_NULL, miBrushResource + resource + 1);
 		onMenuBrushResource(evt);
 	} else if (e.GetKeyCode() == 'L') {
 		wxCommandEvent evt(wxEVT_NULL, miBrushStartLocation + startLocation + 1);
 		onMenuBrushStartLocation(evt);
 	} else if (e.GetKeyCode() == WXK_LEFT && e.GetModifiers() == wxMOD_SHIFT) { // shift-left/right/up/down to shift the map one square
 	    program->setUndoPoint(ctAll);
 	    program->shiftLeft();
 		setDirty();
 	} else if (e.GetKeyCode() == WXK_RIGHT && e.GetModifiers() == wxMOD_SHIFT) {
 	    program->setUndoPoint(ctAll);
 	    program->shiftRight();
 		setDirty();
 	} else if (e.GetKeyCode() == WXK_UP && e.GetModifiers() == wxMOD_SHIFT) {
 	    program->setUndoPoint(ctAll);
 	    program->shiftUp();
 		setDirty();
 	} else if (e.GetKeyCode() == WXK_DOWN && e.GetModifiers() == wxMOD_SHIFT) {
 	    program->setUndoPoint(ctAll);
 	    program->shiftDown();
 		setDirty();
 	} else {
 		e.Skip();
	}
}

BEGIN_EVENT_TABLE(MainWindow, wxFrame)

	EVT_CLOSE(MainWindow::onClose)

	// these are 'handled' by GlCanvas and funneled to these handlers. See BEGIN_EVENT_TABLE(GlCanvas, wxGLCanvas) below.
	//EVT_LEFT_DOWN(MainWindow::onMouseDown)
	//EVT_MOTION(MainWindow::onMouseMove)
	//EVT_KEY_DOWN(MainWindow::onKeyDown)

	EVT_MENU(wxID_NEW, MainWindow::onMenuEditReset)
	EVT_MENU(wxID_OPEN, MainWindow::onMenuFileLoad)
	EVT_MENU(wxID_SAVE, MainWindow::onMenuFileSave)
	EVT_MENU(wxID_SAVEAS, MainWindow::onMenuFileSaveAs)
	EVT_MENU(wxID_EXIT, MainWindow::onMenuFileExit)

	EVT_MENU(miEditUndo, MainWindow::onMenuEditUndo)
	EVT_MENU(miEditRedo, MainWindow::onMenuEditRedo)
	EVT_MENU(miEditResetPlayers, MainWindow::onMenuEditResetPlayers)
	EVT_MENU(miEditResize, MainWindow::onMenuEditResize)
	EVT_MENU(miEditFlipDiagonal, MainWindow::onMenuEditFlipDiagonal)
	EVT_MENU(miEditFlipX, MainWindow::onMenuEditFlipX)
	EVT_MENU(miEditFlipY, MainWindow::onMenuEditFlipY)

    EVT_MENU(miEditMirrorX, MainWindow::onMenuEditMirrorX)
	EVT_MENU(miEditMirrorY, MainWindow::onMenuEditMirrorY)
	EVT_MENU(miEditMirrorXY, MainWindow::onMenuEditMirrorXY)
	EVT_MENU(miEditRotatecopyX, MainWindow::onMenuEditRotatecopyX)
	EVT_MENU(miEditRotatecopyY, MainWindow::onMenuEditRotatecopyY)
	EVT_MENU(miEditRotatecopyXY, MainWindow::onMenuEditRotatecopyXY)
	EVT_MENU(miEditRotatecopyCorner, MainWindow::onMenuEditRotatecopyCorner)

	EVT_MENU(miEditRandomizeHeights, MainWindow::onMenuEditRandomizeHeights)
	EVT_MENU(miEditRandomize, MainWindow::onMenuEditRandomize)
	EVT_MENU(miEditSwitchSurfaces, MainWindow::onMenuEditSwitchSurfaces)
	EVT_MENU(miEditInfo, MainWindow::onMenuEditInfo)
	EVT_MENU(miEditAdvanced, MainWindow::onMenuEditAdvanced)

	EVT_MENU(miViewResetZoomAndPos, MainWindow::onMenuViewResetZoomAndPos)
	EVT_MENU(miViewGrid, MainWindow::onMenuViewGrid)
	EVT_MENU(miViewHeightMap, MainWindow::onMenuViewHeightMap)
	EVT_MENU(miHideWater, MainWindow::onMenuHideWater)
	EVT_MENU(miViewAbout, MainWindow::onMenuViewAbout)
	EVT_MENU(miViewHelp, MainWindow::onMenuViewHelp)

	EVT_MENU_RANGE(miBrushHeight + 1, miBrushHeight + heightCount, MainWindow::onMenuBrushHeight)
	EVT_MENU_RANGE(miBrushGradient + 1, miBrushGradient + heightCount, MainWindow::onMenuBrushGradient)
	EVT_MENU_RANGE(miBrushSurface + 1, miBrushSurface + surfaceCount, MainWindow::onMenuBrushSurface)
	EVT_MENU_RANGE(miBrushObject + 1, miBrushObject + objectCount, MainWindow::onMenuBrushObject)
	EVT_MENU_RANGE(miBrushResource + 1, miBrushResource + resourceCount, MainWindow::onMenuBrushResource)
	EVT_MENU_RANGE(miBrushStartLocation + 1, miBrushStartLocation + startLocationCount, MainWindow::onMenuBrushStartLocation)
	EVT_MENU_RANGE(miRadius, miRadius + radiusCount, MainWindow::onMenuRadius)

	EVT_PAINT(MainWindow::onPaint)

	EVT_TOOL(toolPlayer, MainWindow::onToolPlayer)
END_EVENT_TABLE()

// =====================================================
// class GlCanvas
// =====================================================

GlCanvas::GlCanvas(MainWindow *mainWindow, wxWindow *parent, int *args)
#if wxCHECK_VERSION(2, 9, 1)
		: wxGLCanvas(parent, -1, args, wxDefaultPosition, wxDefaultSize, 0, wxT("GLCanvas")) {
	this->context = new wxGLContext(this);
#else
		: wxGLCanvas(parent, -1, wxDefaultPosition, wxDefaultSize, 0, wxT("GLCanvas"), args) {
	this->context = NULL;
#endif

	this->mainWindow = mainWindow;
}

GlCanvas::~GlCanvas() {
	delete this->context;
	this->context = NULL;
}

void GlCanvas::setCurrentGLContext() {
#ifndef __APPLE__

#if wxCHECK_VERSION(2, 9, 1)
	if(this->context == NULL) {
		this->context = new wxGLContext(this);
	}
#endif

	if(this->context) {
		this->SetCurrent(*this->context);
	}
#else
        this->SetCurrent();
#endif
}

void translateCoords(wxWindow *wnd, int &x, int &y) {
/*
#ifdef WIN32
	int cx, cy;
	wnd->GetPosition(&cx, &cy);
	x += cx;
	y += cy;
#endif
	*/
}

// for the mousewheel
void GlCanvas::onMouseWheel(wxMouseEvent &event) {
	if(event.GetWheelRotation()>0) mainWindow->onMouseWheelDown(event);
	else mainWindow->onMouseWheelUp(event);
}

void GlCanvas::onMouseDown(wxMouseEvent &event) {
	int x, y;
	event.GetPosition(&x, &y);
	translateCoords(this, x, y);
	mainWindow->onMouseDown(event, x, y);
}

void GlCanvas::onMouseMove(wxMouseEvent &event) {
	int x, y;
	event.GetPosition(&x, &y);
	translateCoords(this, x, y);
	mainWindow->onMouseMove(event, x, y);
}

void GlCanvas::onKeyDown(wxKeyEvent &event) {
	int x, y;
	event.GetPosition(&x, &y);
	translateCoords(this, x, y);
	mainWindow->onKeyDown(event);
}

void GlCanvas::onPaint(wxPaintEvent &event) {
//	wxPaintDC dc(this); //N "In a paint event handler must always create a wxPaintDC object even if you do not use it.  (?)
//    mainWindow->program->renderMap(GetClientSize().x, GetClientSize().y);
//	SwapBuffers();
//	event.Skip();
	//printf("gl onPaint skip map\n");
  mainWindow->onPaint(event);
}

BEGIN_EVENT_TABLE(GlCanvas, wxGLCanvas)
	EVT_KEY_DOWN(GlCanvas::onKeyDown)
    EVT_MOUSEWHEEL(GlCanvas::onMouseWheel)
	EVT_LEFT_DOWN(GlCanvas::onMouseDown)
	EVT_MOTION(GlCanvas::onMouseMove)
	EVT_PAINT(GlCanvas::onPaint)  // Because the drawing area needs to be repainted too.
END_EVENT_TABLE()

// ===============================================
//  class SimpleDialog
// ===============================================

void SimpleDialog::addValue(const string &key, const string &value, const string &help) {
	values.push_back(pair<string, string>(key, value+"|"+help)); // I guess I need map<,> instead but I don't know how to do it
}

string SimpleDialog::getValue(const string &key) {
	for (unsigned int i = 0; i < values.size(); ++i) {
		if (values[i].first == key) {
			return values[i].second;
		}
	}
	return "";
}

bool SimpleDialog::show(const string &title, bool wide) {
	Create(NULL, -1, ToUnicode(title));
	wxSizer *sizer2 = new wxBoxSizer(wxVERTICAL);
	wxSizer *sizer = new wxFlexGridSizer(3);

	vector<wxTextCtrl*> texts;
	for (Values::iterator it = values.begin(); it != values.end(); ++it) {
	    size_t helptextpos = it->second.find_first_of('|');
		sizer->Add(new wxStaticText(this, -1, ToUnicode(it->first)), 0, wxALL, 5);
		wxTextCtrl *text = new wxTextCtrl(this, -1, ToUnicode(it->second.substr(0,helptextpos)));
		if(wide) text->SetMinSize( wxSize((text->GetSize().GetWidth())*4, text->GetSize().GetHeight()));  // 4 time as wide as default
		sizer->Add(text, 0, wxALL, 5);
		texts.push_back(text);
	    sizer->Add(new wxStaticText(this, -1, ToUnicode(it->second.substr(helptextpos+1))), 0, wxALL, 5);
	}
	sizer2->Add(sizer);
    sizer2->Add(CreateButtonSizer(wxOK|wxCANCEL),0,wxALIGN_RIGHT); // enable Cancel button
	SetSizerAndFit(sizer2);

	ShowModal();
	if(m_returnCode==wxID_CANCEL) return false; // don't change values if canceled

	for (unsigned int i = 0; i < texts.size(); ++i) {
#ifdef WIN32
		const wxWX2MBbuf tmp_buf = wxConvCurrent->cWX2MB(wxFNCONV(texts[i]->GetValue()));
		values[i].second = tmp_buf;
#else
		values[i].second = texts[i]->GetValue().ToAscii();
#endif
	}
	return true;
}


// ===============================================
//  class App
// ===============================================

bool App::OnInit() {
	SystemFlags::VERBOSE_MODE_ENABLED  = false;
	SystemFlags::ENABLE_THREADED_LOGGING = false;

	string fileparam;
	if(argc==2){
		if(argv[1][0]=='-') {   // any flag gives help and exits program.
			std::cout << "MegaGlest map editor " << mapeditorVersionString << " [Using " << (const char *)wxConvCurrent->cWX2MB(wxVERSION_STRING) << "]" << std::endl << std::endl;
			std::cout << "glest_map_editor [MGM FILE]" << std::endl << std::endl;
			std::cout << "Creates or edits glest/megaglest maps."  << std::endl;
			std::cout << "Draw with left mouse button"  << std::endl;
			std::cout << "Move map with right mouse button"  << std::endl;
			std::cout << "Zoom with middle mouse button or mousewheel"  << std::endl;

//			std::cout << " ~ more helps should be written here ~"  << std::endl;
			std::cout << std::endl;
			exit (0);
		}
//#if defined(__MINGW32__)
		const wxWX2MBbuf tmp_buf = wxConvCurrent->cWX2MB(argv[1]);
		fileparam = tmp_buf;

#ifdef WIN32
		auto_ptr<wchar_t> wstr(Ansi2WideString(fileparam.c_str()));
		fileparam = utf8_encode(wstr.get());
#endif

//#else
//        fileparam = wxFNCONV(argv[1]);
//#endif
	}

#if defined(wxMAJOR_VERSION) && defined(wxMINOR_VERSION) && defined(wxRELEASE_NUMBER) && defined(wxSUBRELEASE_NUMBER)
	printf("Using wxWidgets version [%d.%d.%d.%d]\n",wxMAJOR_VERSION,wxMINOR_VERSION,wxRELEASE_NUMBER,wxSUBRELEASE_NUMBER);
#endif

	wxString exe_path = wxStandardPaths::Get().GetExecutablePath();
    //wxString path_separator = wxFileName::GetPathSeparator();
    //exe_path = exe_path.BeforeLast(path_separator[0]);
    //exe_path += path_separator;

	string appPath;
//#if defined(__MINGW32__)

#ifdef WIN32
	const wxWX2MBbuf tmp_buf = wxConvCurrent->cWX2MB(wxFNCONV(exe_path));
	appPath = tmp_buf;

	auto_ptr<wchar_t> wstr(Ansi2WideString(appPath.c_str()));
	appPath = utf8_encode(wstr.get());
#else
	appPath = wxFNCONV(exe_path);
#endif

//#else
//		const wxWX2MBbuf tmp_buf = wxConvCurrent->cWX2MB(wxFNCONV(exe_path));
//		appPath = tmp_buf;
//#endif

	mainWindow = new MainWindow(appPath);
	mainWindow->Show();
	mainWindow->init(fileparam);
	mainWindow->Update();

#ifdef WIN32
	wxPoint pos = mainWindow->GetScreenPosition();
	wxSize size = mainWindow->GetSize();

	mainWindow->SetSize(pos.x, pos.y, 1, 1, wxSIZE_FORCE);
	//mainWindow->Update();

	mainWindow->SetSize(pos.x, pos.y, size.x-1, size.y, wxSIZE_FORCE);
	mainWindow->Update();
#endif

	return true;
}

int App::MainLoop() {
	try {
		//throw megaglest_runtime_error("test");
		return wxApp::MainLoop();
	}
	catch (const exception &e) {
		MsgDialog(NULL, ToUnicode(e.what()), wxT("Exception")).ShowModal();
	}
	return 0;
}

int App::OnExit() {
	SystemFlags::Close();
	SystemFlags::SHUTDOWN_PROGRAM_MODE=true;

	return 0;
}

MsgDialog::MsgDialog(wxWindow *parent,
                     const wxString& message,
                     const wxString& caption,
                     long style,
					 const wxPoint& pos) {

	m_sizerText = NULL;
    if ( !wxDialog::Create(parent, wxID_ANY, caption,
                           pos, wxDefaultSize,
						   style) ) {
        return;
	}
    m_sizerText = new wxBoxSizer(wxVERTICAL);
    wxStaticText *label = new wxStaticText(this, wxID_ANY, message);
    wxFont font(*wxNORMAL_FONT);
    font.SetPointSize(font.GetPointSize());
    font.SetWeight(wxFONTWEIGHT_NORMAL);
    label->SetFont(font);

    m_sizerText->Add(label, wxSizerFlags().Centre().Border());

    wxSizer *sizerIconAndText = new wxBoxSizer(wxHORIZONTAL);
    sizerIconAndText->Add(m_sizerText, wxSizerFlags(1).Expand());

    wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
    sizerTop->Add(sizerIconAndText, wxSizerFlags(1).Expand().Border());

    wxSizer *sizerBtns = CreateButtonSizer(wxOK);
    if ( sizerBtns )
    {
        sizerTop->Add(sizerBtns, wxSizerFlags().Expand().Border());
    }

    SetSizerAndFit(sizerTop);
    CentreOnScreen();
}
MsgDialog::~MsgDialog() {

}

}// end namespace

IMPLEMENT_APP(MapEditor::App)
