// -----------------------------------------------
// mapedit.cpp
// -----------------------------------------------
// Built-in map editor (totally l33t!)
// -----------------------------------------------
// By Kronoman - Copyright (c) 2003
// In loving memory of my father
// -----------------------------------------------

#include "mapedit.h"
#include "filehelp.h"
#include "sound.h"
#include "musiclvl.h"

CMapEditor::CMapEditor()
{
	mtracer.add("CMapEditor::CMapEditor()");

	tx = ty = 0;
	ts = 1;
	tl = 0;

	dbuffer = NULL;
	mcursor = NULL;

	ykprint = ckprint = 0;

	draw_grid = true;

}

CMapEditor::~CMapEditor()
{
	// debug, source need here? maybe... somebody call 911
	mtracer.add("CMapEditor::~CMapEditor()");
}

// -----------------------------------------------
// Helper functions
// -----------------------------------------------
void CMapEditor::kprint(char *msg)
{
	textout_ex(screen, font, msg, 0, ykprint, ckprint, -1);
	ykprint += text_height(font);
}

void CMapEditor::map_editor_help_message()
{
	ykprint = 0;
	ckprint = makecol(255,255,0);
	scare_mouse();
	clear_bitmap(screen);
	kprint("Map Editor - Help");
	kprint("-----------------");
	kprint("");
	kprint("");

	ckprint = makecol(128,128,255);
	kprint("Copyright (c) 2003, 2004, Kronoman");
	kprint("In loving memory of my father");

	kprint("");
	kprint("");

	ckprint = makecol(255,255,255);
	kprint("Reference:");
	kprint("----------");
	kprint("");
	kprint("");
	kprint("Arrow keys: scroll the map; press SHIFT+Arrow keys for faster scroll.");
	kprint("");
	kprint("Left mouse button or ENTER: set pointed tile to current selected tile type.");
	kprint("");
	kprint("Right mouse button or DELETE: delete tile on that area.");
	kprint("");
	kprint("Middle mouse button or SPACE: set start position for player.");
	kprint("");
	kprint("Z,X or mouse wheel: select tile type to use. (Mouse cursor will reflect changes)");
	kprint("");
	kprint("F: find player start position.");
	kprint("");
	kprint("G: toggle drawing of grid.");
	kprint("");
	kprint("C: clear current map.");
	kprint("");
	kprint("P: set map properties (TIME, etc)");
	kprint("");
	kprint("S: save current map to a file.");
	kprint("");
	kprint("L: load map from a file.");
	kprint("");
	kprint("V: toggle 'show only current layer'");
	kprint("");
	kprint("D: dump tile set configuration to text file \"TILE_SET_CFG.TXT\"");
	kprint("");
	kprint("I: show information about current selected tile");
	kprint("");
	kprint("B: select background image for this level.");
	kprint("");
	kprint("M: select background music for this level.");
	kprint("");
	kprint("PLUS (+) and MINUS (-): change current layer in edition");
	kprint("");
	kprint("ESC: exit map editor.");
	kprint("");
	kprint("F1: show this help");
	kprint("");
	kprint("");
	ckprint = makecol(128,128,128);
	kprint("-- Press any key to continue --");
	unscare_mouse();
	clear_keybuf();
	readkey();
}

// layer = layer to redraw, or -1 for full layers
// grid = true/false (draw or not the grid)
void CMapEditor::redraw_the_tile_map(int layer, bool grid)
{
	clear_bitmap(dbuffer);

	clear_scene(dbuffer); // clear 3D scene zbuffer
	if (layer > -1)
	{
		game_map.draw_map(dbuffer, tx, ty, SCREEN_W, SCREEN_H, layer, grid); // draw it
	}
	else
	{
		for (int l=0; l < MAP_LAYERS; l++)
			game_map.draw_map(dbuffer, tx, ty, SCREEN_W, SCREEN_H, l, grid); // draw it
	}
	render_scene(); // render the 3D scene to the bitmap

	scare_mouse();
	blit(dbuffer, screen,0,0,0,0,dbuffer->w,dbuffer->h);
	unscare_mouse();
}

void CMapEditor::do_change_mouse_cursor()
{
	// set mouse cursor on the tile to put, and also, show it
	if (game_map.tile_set[ts].spr != NULL)
	{
		stretch_blit(game_map.tile_set[ts].spr, mcursor, 0,0,game_map.tile_set[ts].spr->w, game_map.tile_set[ts].spr->h,0,0, mcursor->w,mcursor->h);
	}
	else
	{
		clear_to_color(mcursor, makecol(255,0,255));

		if (ts == 0)
			textout_ex(mcursor, font, "CLS", 0, 0, makecol(128,128,255), -1);
		else
			textprintf_ex(mcursor, font, 0,0,makecol(255,0,0),-1, "%d", ts);
	}
	set_mouse_sprite(mcursor);
	show_mouse(screen);
}

// -----------------------------------------------
// Map edit entry point
// -----------------------------------------------
void CMapEditor::start_map_editor()
{
	bool dirty = false;
	bool finish = false;
	bool socl = false;
	int omx=0,omy=0,old_mouse_z;
	char str[2048];
	str[0] = '\0';
	usprintf(str, "levels");

	mtracer.add("CMapEditor::start_map_editor()");

	dbuffer = create_bitmap(SCREEN_W, SCREEN_H);
	if (dbuffer == NULL)
		raise_error("ERROR: unable to create doble buffer bitmap!\n");

	mcursor = create_bitmap(32,32);
	if (mcursor == NULL)
		raise_error("ERROR: unable to create mouse cursor!\n");

	clear_bitmap(screen);
	clear_bitmap(mcursor);
	clear_bitmap(dbuffer);



	textout_centre_ex(screen, font, "[Map Editor]-[   Please wait... loading...   ]", SCREEN_W/2, SCREEN_H/2, makecol(255,255,255), makecol(0,64,0));

	char tmp_file_buf[2048];
	if (game_map.load_tile_set_from_file(where_is_the_filename(tmp_file_buf,TILESET_FILE)) )
		raise_error("ERROR! Can't load  \"%s\" file!\n", TILESET_FILE); // load tileset

	game_map.empty_the_map(); // reset the map
	tx = 0, ty = 0; // current scroll in the map
	ts = 1; // current tile selected from the tile set (1..255)
	tl = 0; // current layer of map selected 0..MAP_LAYERS-1 (MAP_LAYERS in tmap.h)

	redraw_the_tile_map(-1, draw_grid);
	textout_centre_ex(screen, font, "[ Map editor - Press F1 for help ]", SCREEN_W/2, SCREEN_H/2, makecol(255,255,0),makecol(128,0,0));
	do_change_mouse_cursor();

	// go to map start point
	tx = (game_map.sxp-(SCREEN_W/2/TMAPS_W)) * TMAPS_W;
	ty = (game_map.syp-(SCREEN_H/2/TMAPS_H)) * TMAPS_H;

	// main editor loop
	while (!finish)
	{

		if (dirty)
		{
			if (socl)
				redraw_the_tile_map(tl,draw_grid);
			else
				redraw_the_tile_map(-1,draw_grid);

			dirty = false;
		}

		// little 'HUD'
		textprintf_ex(screen, font, 0,0,makecol(255,255,255), makecol(0,0,0), "[%03d,%03d]-[%3d]-[L:%2d(%c)]", tx/TMAPS_W,ty/TMAPS_H,ts,tl, socl ? 'X' : ' ');

		old_mouse_z = mouse_z;
		while (!keypressed() && !mouse_b &&mouse_z == old_mouse_z)  // wait until user action
		{
			if (keyboard_needs_poll())
				poll_keyboard();
			if (mouse_needs_poll())
				poll_mouse();
			//yield_timeslice(); // play nice with multitasking

			// sub hud
			if (omx != mouse_x/10 || omy != mouse_y/10)
			{
				scare_mouse();
				textprintf_ex(screen, font, 0,text_height(font),makecol(128,255,128), makecol(0,0,0), "[%03d,%03d]", (tx+mouse_x)/TMAPS_W, (ty+mouse_y)/TMAPS_H);
				unscare_mouse();
				// div by 10 to get few precission
				omx = mouse_x/10;
				omy = mouse_y/10;
			}
		}

		if (mouse_b & 1 || key[KEY_ENTER]) // click of left mouse == set the tile
		{
			// DEBUG : NOTICE : if the tile is a prize, it always go on tile 1, so the player can pickup it
			game_map.set_tile_type((tx+mouse_x)/TMAPS_W, (ty+mouse_y)/TMAPS_H, (game_map.tile_set[ts].is_a_prize) ? 1 : tl, ts);
			dirty = true;
			while (mouse_b)
			{
				if (mouse_needs_poll())
					poll_mouse();
			}
			; // wait until mouse release
		}

		if (mouse_b & 2 || key[KEY_DEL]) // click of right mouse == clear the tile
		{
			game_map.set_tile_type((tx+mouse_x)/TMAPS_W, (ty+mouse_y)/TMAPS_H,tl, 0);
			dirty = true;
			while (mouse_b)
			{
				if (mouse_needs_poll())
					poll_mouse();
			}
			; // wait until mouse release
		}

		if (mouse_b & 4 || key[KEY_SPACE]) // change the start pos of the player
		{
			game_map.sxp = (tx+mouse_x)/TMAPS_W;
			game_map.syp = (ty+mouse_y)/TMAPS_H;
			if (game_map.sxp < 0)
				game_map.sxp = 0;
			if (game_map.sxp > 255)
				game_map.sxp = 255;
			if (game_map.syp < 0)
				game_map.syp = 0;
			if (game_map.syp > 255)
				game_map.syp = 255;
			dirty = true;
			while (mouse_b)
			{
				if (mouse_needs_poll())
					poll_mouse();
			}
			; // wait until mouse release
		}

		// with shift, you move faster

		if (key_shifts & KB_SHIFT_FLAG)
		{
			if (key[KEY_LEFT] )
			{
				tx -= 64;
				dirty = true;
			}
			if (key[KEY_RIGHT] )
			{
				tx += 64;
				dirty = true;
			}
			if (key[KEY_UP  ] )
			{
				ty -= 64;
				dirty = true;
			}
			if (key[KEY_DOWN] )
			{
				ty += 64;
				dirty = true;
			}
		}
		else
		{
			// without it, slower
			if (key[KEY_LEFT] )
			{
				tx -= 24;
				dirty = true;
			}
			if (key[KEY_RIGHT] )
			{
				tx += 24;
				dirty = true;
			}
			if (key[KEY_UP  ] )
			{
				ty -= 24;
				dirty = true;
			}
			if (key[KEY_DOWN] )
			{
				ty += 24;
				dirty = true;
			}
		}

		if (key[KEY_X] || mouse_z > old_mouse_z) // change tile selected
		{
			ts++;
			if (ts > 255)
				ts = 0;
			do_change_mouse_cursor();
		}

		if (key[KEY_Z] || mouse_z < old_mouse_z) // change tile selected
		{
			ts--;
			if (ts < 0)
				ts = 255;
			do_change_mouse_cursor();
		}

		if (key[KEY_ESC] ) // exit editor
		{
			set_mouse_sprite(NULL);
			if (alert("Exit map editor", "Are you sure?", NULL, "Yes", "No", 'y', 'n') == 1)
				finish = true;
			do_change_mouse_cursor();
		}

		if (key[KEY_I]) // show info about current tile
		{
			set_mouse_sprite(NULL);
			ykprint = 0;
			ckprint = makecol(255,255,255);
			scare_mouse();
			clear_bitmap(screen);
			kprint("Info about tile");
			kprint("");
			kprint("");

			if (game_map.tile_set[ts].spr != NULL)
			{
				draw_sprite(screen, game_map.tile_set[ts].spr, 0, ykprint );
				ykprint += (int)(game_map.tile_set[ts].spr->w*1.1);
				textprintf_ex(screen, font, 0, ykprint+=text_height(font), ckprint,-1, "Sprite: %s", game_map.tile_set[ts].spr_name );
			}
			else
				kprint("This tile don't has sprite defined");

			kprint("");

			if (game_map.tile_set[ts].sound != NULL)
				textprintf_ex(screen, font, 0, ykprint+=text_height(font), ckprint, -1, "Sound: %s", game_map.tile_set[ts].sound_name );

			kprint("");

			if (game_map.tile_set[ts].exit_level)
				kprint("Is a level exit block");

			kprint("");

			if (game_map.tile_set[ts].solid)
			{
				kprint("Is a solid wall");
				textprintf_ex(screen, font, 0, ykprint+=text_height(font), ckprint,-1, "Bounce factor: %f", game_map.tile_set[ts].bounce_factor );
			}

			kprint("");

			if (game_map.tile_set[ts].is_a_prize)
			{
				kprint("Is a prize");
				if (game_map.tile_set[ts].indispensable)
					kprint("Is indispensable for grant level exit");

				kprint("");

				textprintf_ex(screen, font, 0, ykprint+=text_height(font), ckprint,-1, "Score: %d", game_map.tile_set[ts].score);
			}

			kprint("");

			textprintf_ex(screen, font, 0, ykprint+=text_height(font), ckprint,-1, "Add factor       : x:%f, y:%f, z:%f", game_map.tile_set[ts].adx, game_map.tile_set[ts].ady, game_map.tile_set[ts].adz );

			kprint("");

			textprintf_ex(screen, font, 0, ykprint+=text_height(font), ckprint,-1, "Multiplier factor: x:%f, y:%f, z:%f", game_map.tile_set[ts].mdx, game_map.tile_set[ts].mdy, game_map.tile_set[ts].mdz );

			kprint("");
			kprint("");
			kprint("");
			kprint("-- Press any key to continue --");

			clear_keybuf();
			rest(100);
			readkey();

			unscare_mouse();
			do_change_mouse_cursor();
			dirty = true;
		}

		if (key[KEY_B]) // select background
		{
			int t = 0;

			set_mouse_sprite(NULL);
			ykprint = 0;
			ckprint = makecol(255,255,0);
			scare_mouse();

			while (t != KEY_ENTER)
			{
				ykprint = 0;
				clear_bitmap(screen);
				kprint("Select background with arrow keys, and press ENTER");
				kprint("");
				textprintf_ex(screen, font, 0, 32, makecol(255,255,255),-1,"Current: %d", game_map.background_index);

				// show the current one
				blit(background.get_background("backgr.dat", game_map.background_index), screen, 0,0,0,64,SCREEN_W, SCREEN_H);

				t = readkey() >> 8;
				rest(100);
				if (t == KEY_UP || t == KEY_LEFT)
					game_map.background_index--;
				if (t == KEY_DOWN || t == KEY_RIGHT)
					game_map.background_index++;
				if (t == KEY_PGUP )
					game_map.background_index-=10;
				if (t == KEY_PGDN )
					game_map.background_index+=10;
				if (game_map.background_index < 0)
					game_map.background_index = 255;
				if (game_map.background_index > 255)
					game_map.background_index = 0;
			}

			unscare_mouse();
			do_change_mouse_cursor();
			dirty = true;
		}
		
		if (key[KEY_M]) // select music
		{
			int t = 0;

			set_mouse_sprite(NULL);
			ykprint = 0;
			ckprint = makecol(255,255,0);
			scare_mouse();

			while (t != KEY_ENTER)
			{
				ykprint = 0;
				clear_bitmap(screen);
				kprint("Select music with arrow keys, and press ENTER ; hit SPACE to play/stop the song");
				kprint("");
				textprintf_ex(screen, font, 0, 32, makecol(255,255,255),-1,"Current: %d", game_map.music_index);
				
				clear_keybuf();
				t = readkey() >> 8;
				rest(100);
				
				if (t == KEY_UP || t == KEY_LEFT)
					game_map.music_index--;
				
				if (t == KEY_DOWN || t == KEY_RIGHT)
					game_map.music_index++;
				
				if (t == KEY_PGUP )
					game_map.music_index-=10;
				
				if (t == KEY_PGDN )
					game_map.music_index+=10;
				
				if (game_map.music_index < 0)
					game_map.music_index = 255;
				
				if (game_map.music_index > 255)
					game_map.music_index = 0;
				
				if (t == KEY_SPACE)
				{
					// play the tune...
					CSoundWrapper soundw; // sound system
					CMusicLvl music_loader; // loader system for music from datafile
					DUH *mm = music_loader.get_music("music_l.dat", game_map.music_index);
					if (!mm)
					{
						clear(screen);
						kprint("Sorry, can't play that tune ; must be non existant");
						rest(100);
						kprint("-- press any key --");
						readkey();
						rest(10);
					}
					else
					{	
						clear(screen);
						kprint("Now playing tune...");
						kprint("-- press any key to stop--");
						
						soundw.music_load(mm);
						
						soundw.music_start();
						
						clear_keybuf();

						while (!keypressed())
							soundw.music_poll();
						
						clear_keybuf();
						rest(100);

						soundw.music_stop();

						music_loader.free_memory();
					}
				}
			}

			unscare_mouse();
			do_change_mouse_cursor();
			dirty = true;
		}

		if (key[KEY_C] ) // clear map with C
		{
			set_mouse_sprite(NULL);
			if (alert("Clear the map", "Are you sure?", NULL, "Yes", "No", 'y', 'n') == 1)
			{
				game_map.empty_the_map();
				dirty = true;
			}
			do_change_mouse_cursor();
		}

		if (key[KEY_S]) // save map
		{
			set_mouse_sprite(NULL);
			if (file_select_ex("Save map to file", str, NULL, 2048, (int)(SCREEN_W*0.85), (int)(SCREEN_H*0.85)))
			{
				if (game_map.save_map_to_file(str))
				{
					// failed :(
					alert("ERROR", "File saving failed :(", NULL, "OK", NULL, 0,0);
				}
				else
				{
					// ok!
					alert("SUCESS", "File saved! :^D", NULL, "Great!", NULL, 0,0);
				}
			}
			do_change_mouse_cursor();
		}

		if (key[KEY_L]) // load map - THIS IS DESTRUCTIVE, IF FAILS, IT WILL CORRUPT CURRENT MAP!!!
		{
			set_mouse_sprite(NULL);
			if (alert("Warning!", "If file load fails,", "your MAP will be reset to empty!", "Continue", "Cancel", 0,0) == 1)
			{
				if (file_select_ex("Load map from file", str, NULL, 2048, (int)(SCREEN_W*0.85), (int)(SCREEN_H*0.85)))
				{
					if (game_map.load_map_from_file(str))
					{
						// failed :(
						alert("ERROR", "File load failed :(", "Your MAP probably has garbage, clear it with 'C' key!", "Crap!", NULL, 0,0);
					}
					else
					{
						// ok!
						alert("SUCESS", "File loaded! :^D", NULL, "Great!", NULL, 0,0);
						// go to map entry point
						tx = (game_map.sxp-(SCREEN_W/2/TMAPS_W)) * TMAPS_W;
						ty = (game_map.syp-(SCREEN_H/2/TMAPS_H)) * TMAPS_H;
					}
					dirty = true;
				}
			}
			do_change_mouse_cursor();
		}

		if (key[KEY_D]) // dump tile settings to file
		{
			set_mouse_sprite(NULL);
			if (alert("Dump tile settings", "Dump tile set settings to file?","File will be TILE_SET_CFG.TXT", "Do it", "Cancel", 0,0) == 1)
			{
				scare_mouse();
				textout_centre_ex(screen, font, "Please wait... writing file...", SCREEN_W/2, SCREEN_H/2, makecol(255,255,255), makecol(128,0,0));
				unscare_mouse();
				game_map.save_tile_set_config_to_file("TILE_SET_CFG.TXT");
				alert("SUCESS", "File saved as TILE_SET_CFG.TXT", NULL, "OK", NULL, 0,0);
				dirty = true;
			}

			do_change_mouse_cursor();
		}

		if (key[KEY_G] ) // toggle grid
		{
			draw_grid = !draw_grid;
			dirty = true;
		}

		if (key[KEY_F] ) // goto player's start point
		{
			tx = (game_map.sxp-(SCREEN_W/2/TMAPS_W)) * TMAPS_W;
			ty = (game_map.syp-(SCREEN_H/2/TMAPS_H)) * TMAPS_H;
			dirty = true;
		}

		if (key[KEY_F1])
		{
			set_mouse_sprite(NULL);
			map_editor_help_message();
			do_change_mouse_cursor();
			dirty = true;
		}

		if (key[KEY_V]) // toggle 'show only current layer'
		{
			socl = !socl;
			dirty = true;
		}


		if (key[KEY_PLUS_PAD] || key[KEY_EQUALS]) // keypad plus + = change edit layer
		{
			tl++;
			dirty = true;
		}

		if (key[KEY_MINUS_PAD] || key[KEY_MINUS]) // keypad plus - = change edit layer
		{
			tl--;
			dirty = true;
		}


		if (key[KEY_P]) // set map properties (time, etc)
		{
			char st1[255], st2[255]; // d_edit_buffers
			int it1 = 0, it2 = 0;
			// dialog to set time
			DIALOG set_time_dlg[] =
			    {
			        /* (proc)        (x) (y) (w)  (h)  (fg) (bg) (key) (flags) (d1) (d2) (dp)                         (dp2) (dp3) */
			        { d_box_proc,    0,  0,  216, 104, 0,   0,   0,    0,      0,   0,   NULL,                        NULL, NULL },
			        { d_text_proc,   8,  8,  144, 8,   0,   0,   0,    0,      0,   0,   (void *)"Set map properties",        NULL, NULL },
			        { d_text_proc,   8,  24, 40,  16,  0,   0,   0,    0,      0,   0,   (void *)"Time to complete the map:", NULL, NULL },
			        { d_text_proc,   8,  40, 56,  16,  0,   0,   0,    0,      0,   0,   (void *)"Minutes:",                  NULL, NULL },
			        { d_text_proc,   8,  56, 56,  16,  0,   0,   0,    0,      0,   0,   (void *)"Seconds:",                  NULL, NULL },
			        { d_edit_proc,   88, 40, 56,  16,  0,   0,   0,    0,      3,   0,   (void *)"edit",                      NULL, NULL },
			        { d_edit_proc,   88, 56, 56,  16,  0,   0,   0,    0,      2,   0,   (void *)"edit",                      NULL, NULL },
			        { d_button_proc, 8,  72, 200, 24,  0,   0,   0,    D_EXIT,      0,   0,   (void *)"OK",                        NULL, NULL },
			        { NULL,          0,  0,  0,   0,   0,   0,   0,    0,      0,   0,   NULL,                        NULL, NULL }
			    };

			// set initial data
			usprintf(st1, "%d", game_map.time_m);
			usprintf(st2, "%d", game_map.time_s);
			set_time_dlg[5].dp = st1;
			set_time_dlg[6].dp = st2;

			// do input dialog
			set_mouse_sprite(NULL);
			set_dialog_color(set_time_dlg, makecol(0,0,0), makecol(255,255,255));
			centre_dialog(set_time_dlg);
			popup_dialog(set_time_dlg, -1);

			// validate new data
			it1 = atoi(st1);
			it2 = atoi(st2);
			if (it1 >= 0 && it2 >= 0 && it1 < 256 && it2 <= 60)
			{
				// set new data
				game_map.time_m = it1;
				game_map.time_s = it2;
				usprintf(st1, "Time of map set to %d:%d", game_map.time_m, game_map.time_s);
				alert("Done", st1, NULL, "OK", NULL, 0,0);
			}
			else
			{
				alert("ERROR", "Range of input invalid", "Valid: 0 <= minutes <= 255, and 0 <= seconds <= 60", "OK", NULL, 0,0);
			}

			do_change_mouse_cursor();
			dirty = true;
		}


		if (tl < 0)
			tl = MAP_LAYERS-1;
		if (tl > MAP_LAYERS-1)
			tl = 0;

		if (tx < 0)
			tx = 0;
		if (ty < 0)
			ty = 0;
		if (tx > TMAP_SIZE*TMAPS_W-SCREEN_W)
			tx = TMAP_SIZE * TMAPS_W-SCREEN_W;
		if (ty > TMAP_SIZE*TMAPS_H-SCREEN_H)
			ty = TMAP_SIZE * TMAPS_H-SCREEN_H;

		clear_keybuf();
	}

	// free memory
	destroy_bitmap(dbuffer);
	destroy_bitmap(mcursor);
	game_map.free_memory();
	set_mouse_sprite(NULL);

}


