#include "general.h"
#include "widgets.h"

#include <sstream>
#include <cstdio>

ALLEGRO_DISPLAY *display;
static bool resize;
static std::vector<ALLEGRO_BITMAP *> tileSheets;
static A_Combobox *layerCombo, *sheetCombo;
static A_Tileselector *tileSelector;
static A_Leveleditor *levelEditor;

/* Quick reference window stuff
 */
A_Frame *quickRefFrame;
A_Splitter *quickRefSplitter;
A_Splitter *quickRefBottomSplitter;
A_Label *quickRefContent1, *quickRefContent2;
A_Titlebar *quickRefTitlebar;

static void setTitle(void) {
	std::string filename = levelEditor->getOperatingFilename();
	if (filename == "") {
		al_set_window_title(display, "AshEdit2");
	}
	else {
		std::string name = std::string("AshEdit2") + " - " + filename;
		al_set_window_title(display, name.c_str());
	}
}

static void draw(void)
{
	if (resize) {
		tgui::resize(NULL);
		resize = false;
	}
	al_clear_to_color(al_color_name("white"));
	tgui::draw();
	al_flip_display();
}

static void tileDrawCallback(int ox, int oy, int dx, int dy, int w, int h)
{
	ALLEGRO_BITMAP *sheet = tileSheets[sheetCombo->getSelected()];
	al_draw_bitmap_region(
		sheet,
		ox,
		oy,
		w,
		h,
		dx,
		dy,
		0
	);

	int sel_x, sel_y;
	tileSelector->getSelected(&sel_x, &sel_y);

	int rx = dx + ((sel_x*General::tileSize) - ox);
	int ry = dy + ((sel_y*General::tileSize) - oy);

	al_draw_rectangle(
		rx+0.5,
		ry+0.5,
		rx+General::tileSize-0.5,
		ry+General::tileSize-0.5,
		al_color_name("black"),
		1
	);
	al_draw_rectangle(
		rx+1.5,
		ry+1.5,
		rx+General::tileSize-1.5,
		ry+General::tileSize-1.5,
		al_color_name("yellow"),
		1
	);
	al_draw_rectangle(
		rx+2.5,
		ry+2.5,
		rx+General::tileSize-2.5,
		ry+General::tileSize-2.5,
		al_color_name("black"),
		1
	);
}

static void levelDrawCallback(int ox, int oy, int dx, int dy, int w, int h)
{
	int xx = dx - (ox % General::tileSize);
	int yy = dy - (oy % General::tileSize);
	int tx = ox / General::tileSize;
	int ty = oy / General::tileSize;
	int wt = w / General::tileSize+2;
	int ht = h / General::tileSize+2;
	int layers = levelEditor->getLayers();
	bool solids[layers];

	dy = yy;
	for (int y = 0; y < ht; y++) {
		if (ty+y >= levelEditor->getHeight())
			break;
		dx = xx;
		for (int x = 0; x < wt; x++) {
			if (tx+x >= levelEditor->getWidth())
				break;
			for (int l = 0; l < layers; l++) {
				int tile, sheet;
				levelEditor->getTile(tx+x, ty+y, l, &tile, &sheet, &solids[l]);
				if (tile < 0 || sheet < 0) {
					continue;
				}
				ALLEGRO_BITMAP *bmp = tileSheets[sheet];
				int widthInTiles = al_get_bitmap_width(bmp) / General::tileSize;
				int tileX = tile % widthInTiles;
				int tileY = tile / widthInTiles;
				al_draw_bitmap_region(
					bmp,
					tileX*General::tileSize,
					tileY*General::tileSize,
					General::tileSize,
					General::tileSize,
					dx, dy,
					0
				);
			}
			if (solids[layerCombo->getSelected()]) {
				al_draw_line(dx, dy, dx+General::tileSize, dy+General::tileSize, al_color_name("yellow"), 2);
				al_draw_line(dx, dy+General::tileSize, dx+General::tileSize, dy, al_color_name("yellow"), 2);
			}
			dx += General::tileSize;
		}
		dy += General::tileSize;
	}
}

int main(int argc, char **argv)
{
	// Initialize Allegro
	al_init();
	al_install_mouse();
	al_install_keyboard();
	al_init_image_addon();
	al_init_font_addon();
	al_init_ttf_addon();
	al_init_primitives_addon();

	al_set_new_display_flags(ALLEGRO_RESIZABLE|ALLEGRO_GENERATE_EXPOSE_EVENTS);
	display = al_create_display(640, 480);
	ALLEGRO_FONT *font = al_load_ttf_font("DejaVuSans.ttf", 12, 0);
	ALLEGRO_EVENT_QUEUE *queue = al_create_event_queue();
	ALLEGRO_TIMER *draw_timer = al_install_timer(1.0/20.0);
	al_start_timer(draw_timer);

	al_register_event_source(queue, al_get_mouse_event_source());
	al_register_event_source(queue, al_get_keyboard_event_source());
	al_register_event_source(queue, al_get_display_event_source(display));
	al_register_event_source(queue, al_get_timer_event_source(draw_timer));

	// some outrageously high number
	for (int i = 0; i < 256; i++) {
		char filename[50];
		sprintf(filename, "tiles%d.png", i);
		ALLEGRO_BITMAP *bmp = al_load_bitmap(filename);
		if (!bmp)
			break;
		tileSheets.push_back(bmp);
	}

	tgui::init();
	tgui::setFont(font);
	
	al_register_event_source(queue, tgui::getEventSource());

	A_Splitter *mainSplitTop = new A_Splitter(A_Splitter::SPLIT_HORIZONTAL);
	A_Splitter *mainSplitBottom= new A_Splitter(A_Splitter::SPLIT_HORIZONTAL);
	A_Splitter *canvasSplit = new A_Splitter(A_Splitter::SPLIT_VERTICAL);
	A_Splitter *canvasSplitLeft = new A_Splitter(A_Splitter::SPLIT_HORIZONTAL);
	A_Splitter *canvasSplitRight = new A_Splitter(A_Splitter::SPLIT_HORIZONTAL);
	canvasSplit->setResizable(true);

	A_Menubar *menubar = new A_Menubar();
	A_Menu *fileMenu = new A_Menu("File");
	A_Menu *editMenu = new A_Menu("Edit");
	A_Menu *helpMenu = new A_Menu("Help");
	A_Menuitem *fileOpen = new A_Menuitem("Open...");
	A_Menuitem *fileSave = new A_Menuitem("Save");
	A_Menuitem *fileSaveAs = new A_Menuitem("Save As...");
	A_Menuitem *editUndo = new A_Menuitem("Undo");
	A_Menuitem *helpQuickRef = new A_Menuitem("Quick Reference");
	menubar->addMenu(fileMenu);
	menubar->addMenu(editMenu);
	menubar->addMenu(helpMenu);
	fileMenu->addMenuitem(fileOpen);
	fileMenu->addMenuitem(fileSave);
	fileMenu->addMenuitem(fileSaveAs);
	editMenu->addMenuitem(editUndo);
	helpMenu->addMenuitem(helpQuickRef);

	layerCombo = new A_Combobox(
		7,
		al_color_name("cyan"),
		al_color_name("black"),
		al_color_name("darkcyan")
	);
	sheetCombo = new A_Combobox(
		7,
		al_color_name("cyan"),
		al_color_name("black"),
		al_color_name("darkcyan")
	);
	for (int i = 0; i < General::startLayers; i++) {
		char buf[20];
		sprintf(buf, "Layer %d", i);
		layerCombo->addValue(std::string(buf));
	}
	for (unsigned int i = 0; i < tileSheets.size(); i++) {
		char buf[20];
		sprintf(buf, "Sheet %d", i);
		sheetCombo->addValue(std::string(buf));
	}

	A_Scrollpane *levelScrollpane = new A_Scrollpane(
		al_color_name("gray"),
		al_color_name("lightgrey")
	);
	A_Scrollpane *tileScrollpane = new A_Scrollpane(
		al_color_name("gray"),
		al_color_name("lightgrey")
	);
	tileSelector = new A_Tileselector(tileDrawCallback);
	tileScrollpane->addScrollable(tileSelector);
	tileScrollpane->setScrollSize(
		al_get_bitmap_width(tileSheets[0]),
		al_get_bitmap_height(tileSheets[0])
	);
	levelEditor = new A_Leveleditor(levelDrawCallback, General::startLayers);
	levelScrollpane->addScrollable(levelEditor);
	levelScrollpane->setScrollSize(levelEditor->getWidth()*General::tileSize, levelEditor->getHeight()*General::tileSize);

	A_Label *statusLabel = new A_Label("Status", al_color_name("black"));

	setTitle();

	tgui::setNewWidgetParent(0);
	tgui::addWidget(mainSplitTop);
	mainSplitTop->addToFirstPane(menubar);
	mainSplitTop->addToSecondPane(mainSplitBottom);
	mainSplitBottom->addToSecondPane(statusLabel);
	mainSplitBottom->addToFirstPane(canvasSplit);
	canvasSplit->addToFirstPane(canvasSplitLeft);
	canvasSplit->addToSecondPane(canvasSplitRight);
	canvasSplitLeft->addToFirstPane(layerCombo);
	canvasSplitLeft->addToSecondPane(levelScrollpane);
	canvasSplitRight->addToFirstPane(sheetCombo);
	canvasSplitRight->addToSecondPane(tileScrollpane);

	tgui::setFocus(mainSplitTop);

	mainSplitTop->setSplitSize(20, -1);
	mainSplitBottom->setSplitSize(-1, 20);
	canvasSplitLeft->setSplitSize(20, -1);
	canvasSplitRight->setSplitSize(20, -1);

	tgui::resize(NULL); // similar to java pack(), on all widgets

	draw();
	int draw_ticks = 0;
	
	while (true) {
		ALLEGRO_EVENT event;
		do {
			al_wait_for_event(queue, &event);
			if (event.type == ALLEGRO_EVENT_TIMER) {
				draw_ticks++;
			}
			else if (event.type == ALLEGRO_EVENT_DISPLAY_RESIZE) {
				al_acknowledge_resize(event.display.source);
				resize = true;
				draw();
			}
			else if ((event.type == ALLEGRO_EVENT_KEY_DOWN &&
				event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) ||
				event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
				goto done;
			}
			else if (event.type == ALLEGRO_GET_EVENT_TYPE('T', 'G', 'U', 'I')) {
				ALLEGRO_USER_EVENT *u = (ALLEGRO_USER_EVENT *)&event;
				int type = (int)u->data1;
				if (type == tgui::TGUI_EVENT_OBJECT) {
					tgui::TGUIWidget *widget = (tgui::TGUIWidget *)u->data2;
					if (widget == fileOpen) {
						levelEditor->load();
						setTitle();
					}
					else if (widget == fileSave) {
						levelEditor->save(false);
						setTitle();
					}
					else if (widget == fileSaveAs) {
						levelEditor->save(true);
						setTitle();
					}
					else if (widget == editUndo) {
						levelEditor->doUndo();
					}
					else if (widget && widget == quickRefTitlebar) {
						quickRefFrame->remove();
						delete quickRefTitlebar;
						delete quickRefContent1;
						delete quickRefContent2;
						delete quickRefSplitter;
						delete quickRefBottomSplitter;
						delete quickRefFrame;
						quickRefTitlebar = NULL;
						quickRefContent1 = NULL;
						quickRefContent2 = NULL;
						quickRefSplitter = NULL;
						quickRefBottomSplitter = NULL;
						quickRefFrame = NULL;
					}
					else if (widget == helpQuickRef) {
						quickRefFrame = new A_Frame(al_color_name("blue"), 450, 450);
						quickRefFrame->setPosition(50, 50);
						quickRefSplitter = new A_Splitter(A_Splitter::SPLIT_HORIZONTAL);
						quickRefBottomSplitter = new A_Splitter(A_Splitter::SPLIT_VERTICAL);
						quickRefContent1 = new A_Label(
							"NO UNDO for these!\n"
							"\n"
							"Ctrl-R\n"
							"Shift-Ctrl-R\n"
							"Ctrl-C\n"
							"Shift-Ctrl-C\n"
							"Ctrl-Delete\n"
							"Shift-Ctrl-Delete\n"
							"Ctrl-L\n"
							"Shift-Ctrl-L\n"
							"Ctrl-Alt-L\n"
							"B\n"
							"C\n"
							"S\n"
							"M\n"
							"R\n",
							al_color_name("white")
						);
						quickRefContent2 = new A_Label(
							"\n"
							"\n"
							"Insert row before cursor\n"
							"Insert row after cursor\n"
							"Insert column before cursor\n"
							"Insert column after cursor\n"
							"Delete row\n"
							"Delete column\n"
							"Insert layer before current layer\n"
							"Insert layer after current layer\n"
							"Delete current layer\n"
							"Switch to the brush tool\n"
							"Switch to the clear tool\n"
							"Switch to the solids tool\n"
							"Switch to the macro tool\n"
							"Start/stop recording macro\n",
							al_color_name("white")
						);
						quickRefContent1->setX(5);
						quickRefContent1->setY(5);
						quickRefContent2->setY(5);
						quickRefTitlebar = new A_Titlebar(quickRefFrame, "Quick Reference", al_color_name("cyan"), A_Titlebar::CLOSE_BUTTON);
						quickRefSplitter->addToFirstPane(quickRefTitlebar);
						quickRefSplitter->addToSecondPane(quickRefBottomSplitter);
						quickRefBottomSplitter->addToFirstPane(quickRefContent1);
						quickRefBottomSplitter->addToSecondPane(quickRefContent2);
						tgui::setNewWidgetParent(NULL);
						tgui::addWidget(quickRefFrame);
						tgui::setNewWidgetParent(quickRefFrame);
						tgui::addWidget(quickRefSplitter);
						quickRefSplitter->setSplitSize(20, -1);
						quickRefBottomSplitter->setSplitRatio(0.3, 0.7);
						tgui::resize(NULL);
					}
				}
			}
			else {
				if (quickRefFrame)
					quickRefFrame->raise();
				int numLayers = levelEditor->getNumLayers();
				levelEditor->setLayer(layerCombo->getSelected());
				int tileX, tileY;
				tileSelector->getSelected(&tileX, &tileY);
				levelEditor->setTile(tileY*(al_get_bitmap_width(tileSheets[0])/General::tileSize)+tileX);
				levelEditor->setSheet(sheetCombo->getSelected());
				tgui::handleEvent(&event);
				// build status string
				int levelX, levelY;
				int tileNumber = tileY*(al_get_bitmap_width(tileSheets[0])/General::tileSize)+tileX;
				levelEditor->getHoverPosition(&levelX, &levelY);
				std::stringstream ss;
				if (levelEditor->getRecording())
					ss << "(Recording) ";
				ss << levelEditor->getTool() << " -- Level (" << levelX << "," << levelY << ") Tile (" << tileNumber << "," << tileX << "," << tileY << ")";
				statusLabel->setText(ss.str());
				if (levelEditor->getNumLayers() != numLayers) {
					layerCombo->setSelected(levelEditor->getLayer());
					layerCombo->clearValues();
					for (int i = 0; i < levelEditor->getNumLayers(); i++) {
						char buf[20];
						sprintf(buf, "Layer %d", i);
						layerCombo->addValue(std::string(buf));
					}
				}
			}
		} while (!al_event_queue_is_empty(queue));

		if (draw_ticks > 0)
			draw();

		draw_ticks = 0;

		al_rest(0.001);
	}
done:
	return 0;
}

