/*
	TODO
	----
*/

#include <stdio.h>
#include <string.h>

#include "allegro.h"
#include "dime.h"
#include "object.h"
#include "mission.h"
#include "scenery.h"
#include "soldier.h"
#include "unit.h"
#include "building.h"
#include "vehicle.h"
#include "pickup.h"
#include "config.h"

// datafiles headers
#include "../data/general.h"

// timing variables
volatile int fps, frame_count, game_count;

// bitmaps and the like
BITMAP *mini_map;
BITMAP *swap_screen;
PALETTE game_pal;

// datafiles
DATAFILE *gen = NULL;	// general stuff, fonts etc

// light maps
RGB_MAP rgb_table;
COLOR_MAP dark_map;
COLOR_MAP trans_table;

Toptions options;
mission *mis = NULL;	// pointer to current mission
object *obj = NULL;		// pointer to current object (held)
int object_mode = 0;	// mode of held object 
int rect_mode = 0;		// mode of rectangle select
int add_object_type=0;	// type of object to add
int add_object_img=0;
int add_object_ang=0;
char add_object_file[1024];
int selected_object = -1;
int picked_object = -1;
bool quit_edit = FALSE;
bool wp_mode = FALSE;
bool sub_mode = FALSE;
bool add_target = FALSE;
unit *wp_unit = NULL;
int current_wp = -1;
int current_sub = -1;
char current_file[1024];
char woody_str[] = "woodchuck";

// global stuff
int cx, cy, ca;  // camera
int zoom = 100;		// mini map zoom


// some declarations
void load_mission();
void save_mission();


///////////////////////////////////////////////////////////////
// MENU STUFF
///////////////////////////////////////////////////////////////

MENU default_menu[32] = {
   { "Add Object",					NULL,	NULL,	0,	NULL  },
   { "Add Zone",					NULL,	NULL,	0,	NULL  },
   { "",                            NULL,	NULL,	0,	NULL  },
   { "Sub Mission Mode",			NULL,	NULL,	0,	NULL  },
   { "",                            NULL,	NULL,	0,	NULL  },
   { "New Mission",					NULL,	NULL,	0,	NULL  },
   { "Load Mission",				NULL,	NULL,	0,	NULL  },
   { "Save Mission",				NULL,	NULL,	0,	NULL  },
   { "",                            NULL,	NULL,	0,	NULL  },
   { "Properties",					NULL,	NULL,	0,	NULL  },
   { "",                            NULL,	NULL,	0,	NULL  },
   { "Quit",						NULL,	NULL,	0,	NULL  },
   { NULL,                          NULL,	NULL,	0,	NULL  }
};

MENU object_menu[32] = {
   { "Properties",					NULL,	NULL,	0,	NULL  },
   { "",                            NULL,	NULL,	0,	NULL  },
   { "Delete",						NULL,	NULL,	0,	NULL  },
   { NULL,                          NULL,	NULL,	0,	NULL  }
};

MENU wp_menu[32] = {
   { "Insert",						NULL,	NULL,	0,	NULL  },
   { "Delete",						NULL,	NULL,	0,	NULL  },
   { "",                            NULL,	NULL,	0,	NULL  },
   { "Properties",					NULL,	NULL,	0,	NULL  },
   { "Reset",						NULL,	NULL,	0,	NULL  },
   { "",                            NULL,	NULL,	0,	NULL  },
   { "Exit Path Mode",              NULL,	NULL,	0,	NULL  },
   { NULL,                          NULL,	NULL,	0,	NULL  }
};

MENU zone_menu[32] = {
   { "Move",						NULL,	NULL,	0,	NULL  },
   { "Delete",						NULL,	NULL,	0,	NULL  },
   { NULL,                          NULL,	NULL,	0,	NULL  }
};

MENU main_sub_menu[32] = {
   { "New Sub Mission",             NULL,	NULL,	0,	NULL  },
   { "",                            NULL,	NULL,	0,	NULL  },
   { "Exit Sub Mode",				NULL,	NULL,	0,	NULL  },
   { NULL,                          NULL,	NULL,	0,	NULL  }
};

MENU sub_menu[32] = {
   { "Add Targets",              NULL,	NULL,	0,	NULL  },
   { "Reset Targets",               NULL,	NULL,	0,	NULL  },
   { "",                            NULL,	NULL,	0,	NULL  },
   { "Properties",		            NULL,	NULL,	0,	NULL  },
   { "",                            NULL,	NULL,	0,	NULL  },
   { "Delete",                      NULL,	NULL,	0,	NULL  },
   { NULL,                          NULL,	NULL,	0,	NULL  }
};

///////////////////////////////////////////////////////////////
// DEBUG STUFF
///////////////////////////////////////////////////////////////

// shows a text message
// char *func - name of calling function (null terminated)
// char *txt - message to be shown (null terminated)
void my_alert(char *func, char *txt) {
//	PALETTE p;

//	get_palette(p);
//	set_palette(desktop_palette);
	alert("Heli Alert", func, txt, "Righty", "Amazing", 13, 27);
//	set_palette(p);
}


char *get_pwd() {
	return woody_str;
}


///////////////////////////////////////////////////////////////
// TIMER STUFF
///////////////////////////////////////////////////////////////

//	keeps track of frames each second
void fps_counter(void) {
	fps = frame_count;
	frame_count = 0;
}
END_OF_FUNCTION(fps_counter);

// keeps track of internal game speed
void game_counter(void) {
	game_count++;
}
END_OF_FUNCTION(game_counter);

// initiates the timers
void init_timers() {
	install_timer();
	LOCK_VARIABLE(game_count);
	LOCK_VARIABLE(fps);
	LOCK_VARIABLE(frame_count);
	LOCK_FUNCTION(fps_counter);
	install_int(fps_counter,1000);
	fps=0;
	frame_count=0;
	LOCK_FUNCTION(game_counter);
	install_int(game_counter, FRAME_LENGTH);	
}


///////////////////////////////////////////////////////////////
// INIT, QUIT, LOAD, SAVE, ETC STUFF
///////////////////////////////////////////////////////////////

// makes colormaps
void make_colormaps() {
	create_rgb_table(&rgb_table, (RGB *)mis->gfx[0].dat, NULL);
	rgb_map = &rgb_table;
	create_light_table(&dark_map, (RGB *)mis->gfx[0].dat, 0, 0, 0, NULL);
	create_trans_table(&trans_table, (RGB *)mis->gfx[0].dat, 50, 50, 50, NULL);
	color_map = &trans_table;
	solid_mode();
}

//  inits the editor
void init_editor() {
	set_uformat(U_ASCII);

	allegro_init();
	dime_init();

	install_keyboard();
	init_timers();
	install_mouse();

	set_config_file("data/config.txt");
	options.detail_level = get_config_int("OPTIONS","Detail", DETAIL_HIGH);
	options.music_mute = get_config_int("OPTIONS","MusicMute", 0);
	options.music_volume = get_config_int("OPTIONS","MusicVolume", 200);
	options.sound_mute = get_config_int("OPTIONS","SoundMute", 0);
	options.sound_volume = get_config_int("OPTIONS","SoundVolume", 200);
	options.size = get_config_int("OPTIONS", "Size", 10);
	options.frame_step = get_config_int("OPTIONS", "FrameStep", 1);
	options.use_joy = get_config_int("OPTIONS", "UseJoy", FALSE);
	options.analog_joy = get_config_int("OPTIONS", "Analog", FALSE);
	options.shadow = get_config_int("OPTIONS", "Shadow", FALSE);
	options.smoke = get_config_int("OPTIONS", "Smoke", FALSE);

	set_color_depth(8);
	set_gfx_mode(GFX_AUTODETECT, 1024, 768, 0, 0);

	install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL);

	swap_screen = create_bitmap(SCREEN_W, SCREEN_H);
	mini_map = create_bitmap(160, 120);

	// load data
	packfile_password(woody_str);
	gen = load_datafile("data/general.dat");
	packfile_password(NULL);
	if (gen == NULL) {
		my_alert("init_game", "Failed to load general.dat.");
		exit(1);
	}
	set_palette((RGB *)gen[0].dat);
	get_palette(game_pal);

	dime_font = (FONT *) gen[FONT_DIALOG].dat;
	dime_title_font = (FONT *) gen[FONT_DIALOG_BOLD].dat;
	dime_button_font = (FONT *) gen[FONT_DIALOG].dat;
	font = (FONT *) gen[FONT_DIALOG].dat;

	gui_fg_color = 0;
	gui_bg_color = 1;
	set_mouse_sprite((BITMAP *)gen[MPTR_EDIT].dat);

	text_mode(-1);
}


// shuts allegro down, saves configs etc
void shut_down() {
	if (mis != NULL) mis->~mission();
	if (obj != NULL) obj->~object();
	allegro_exit();
}


///////////////////////////////////////////////////////////////
// DRAWING STUFF
///////////////////////////////////////////////////////////////

// a 'look up' screen 
void draw_mini_map() {
	int x1 = 80+(cx-HELI_X-100000)/zoom, 
		y1 = 60+(cy-HELI_Y-100000)/zoom, 
		x2 = 80+(cx-HELI_X-100000+SCREEN_W)/zoom,
		y2 = 60+(cy-HELI_Y-100000+SCREEN_H)/zoom;
	int col;
	clear_to_color(mini_map, 14);
	line(mini_map, 0, (y2-y1)/2+y1, 160, (y2-y1)/2+y1, 8);
	line(mini_map, (x2-x1)/2+x1, 0, (x2-x1)/2+x1, 120, 8);
	rectfill(mini_map, x1, y1, x2, y2, 10);
	rect(mini_map, x1-1, y1-1, x2+1, y2+1, 8);
	rect(mini_map, 0, 0, mini_map->w-1, mini_map->h-1, 15);

	for(int i=0;i<MAX_OBJECTS;i++)
		if (mis->obj[i] != NULL)
			if (!mis->obj[i]->inside) {
				if (mis->obj[i]->is_unit()) col = (mis->obj[i]->team == 1 ? 87 : 251);
				else col = (mis->obj[i]->type == OBJ_BUILDING ? 6 : (mis->obj[i]->type == OBJ_SCENERY ? 9 : 1));
				putpixel(mini_map, 80+(mis->obj[i]->x-100000)/zoom, 60+(mis->obj[i]->y-100000)/zoom, col);
			}
}

// blits a bmp to the center of the screen
void blit_to_screen(BITMAP *bmp) {
	if (SCREEN_W == 640)
		blit(bmp, screen, 0, 0, 0, 0, bmp->w, bmp->h);
	else
		blit(bmp, screen, 0, 0, SCREEN_W/2-bmp->w/2, SCREEN_H/2-bmp->h/2, bmp->w, bmp->h);
}

// displays an fps counter at x,y
void draw_fps(BITMAP *bmp, int x, int y) {
	rectfill(bmp, x, y, x+24, y+8, 1);
	textprintf(bmp, font, x+1, y+1, 0, "%3d", fps);
}

// draws the screen
void draw_screen(BITMAP *bmp) {
	int mx = mouse_x, my = mouse_y;
	clear_to_color(bmp, 254);

	// draw mission
	if (mis != NULL) {	
		// draw zones
		for(int i=1;i<MAX_ZONES;i++)
			if (mis->zone[i].active) {
				mis->draw_zone(swap_screen, &mis->zone[i], cx-1, cy-1, 15, i);
				mis->draw_zone(swap_screen, &mis->zone[i], cx, cy, 6, i);
			}

		color_map = &dark_map;
		drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
		for(int j=-64;j<SCREEN_W+64;j+=64) {
			line(swap_screen, j-cx%64, 0, j-cx%64, SCREEN_H, 224);
			line(swap_screen, 0, j-cy%64, SCREEN_W, j-cy%64, 224);
		}
		solid_mode();

		color_map = &trans_table;
		mis->render(bmp, cx, cy, itofix(ca));
		
		if (obj != NULL) {	// draw held object
			if (object_mode <= 0) rotate_sprite(swap_screen, (BITMAP *)mis->gfx[obj->base_image].dat, mx - obj->w/2, my - obj->h/2, obj->angle);
		}
	}

	textprintf(bmp, font, 0, 0, 1, "%d,%d", cx+mx-HELI_X, cy+my-HELI_Y);
	textprintf(bmp, font, 0, SCREEN_H-20, 1, "Editing: %s     [%s]", mis->name, current_file);
}


///////////////////////////////////////////////////////////////
// EDIT STUFF
///////////////////////////////////////////////////////////////

// shows confirm dialog
bool confirm_quit() {
	draw_screen(swap_screen);
	blit_to_screen(swap_screen);
	show_mouse(screen);
	return (dialogf("Really quit?", ALIGN_CENTRE, ALIGN_CENTRE, 200, "") == 1 ? TRUE : FALSE);
}

// creates a specific object
object *make_object(int type, char *buf) {
	int im = 0;
	if (mis == NULL) return NULL;
	object *o = NULL;

	if (type == -1) {
		type = add_object_type;
		buf = add_object_file;
		im = 1;
	}

	if (type == OBJ_SCENERY) {
		scenery *s = new scenery();
		s->set_from_file(buf);
		o = s;
	}
	if (type == OBJ_BUILDING) {
		building *b = new building();
		b->set_from_file(buf);
		o = b;
	}
	if (type == OBJ_PICKUP) {
		pickup *p = new pickup(1);
		p->set_from_file(buf);
		o = p;
	}
	if (type == OBJ_SOLDIER || type == OBJ_FOOT_SOLDIER || type == OBJ_BAZOOKA_SOLDIER) {
		soldier *s = new soldier(type);
		s->set_from_file(buf);
		o = s;
	}
	if (type == OBJ_TANK || type == OBJ_TRUCK || type == OBJ_JEEP || type == OBJ_HELI || type == OBJ_TOWER) {
		vehicle *v = new vehicle(type);
		v->set_from_file(buf);
		o = v;
	}

	if (o != NULL) {
		if (im) {
			o->base_image = add_object_img;
			o->angle = itofix(add_object_ang);
		}
		o->w = ((BITMAP*)mis->gfx[o->base_image].dat)->w;
		o->h = ((BITMAP*)mis->gfx[o->base_image].dat)->h;
		strcpy(o->file, get_filename(buf));
	}

	return o;
}

// shows list of possible objects
// set that object to obj
void add_object_menu(int x, int y) {
	static int list_index = 0;
	int obj_type=-1;
	char buf[1024];
	char type_str[128];

	if (mis == NULL) return;

	draw_screen(swap_screen);
	blit_to_screen(swap_screen);
	show_mouse(screen);

	strcpy(buf, "data/objects/");
	//if (!file_select_ex("Select object to add:", buf, "TXT", 12, 300, 600)) return;
	if (!file_select("Select object to add:", buf, "TXT")) return;

	config *c = new config();
	c->load_data(buf);
	c->get_data_string("type", type_str);
	c->~config();

	if (!stricmp(type_str, "soldier")) obj_type = OBJ_SOLDIER;
	else if (!stricmp(type_str, "foot_soldier")) obj_type = OBJ_FOOT_SOLDIER;
	else if (!stricmp(type_str, "bazooka_soldier")) obj_type = OBJ_BAZOOKA_SOLDIER;
	else if (!stricmp(type_str, "jeep")) obj_type = OBJ_JEEP;
	else if (!stricmp(type_str, "truck")) obj_type = OBJ_TRUCK;
	else if (!stricmp(type_str, "tank")) obj_type = OBJ_TANK;
	else if (!stricmp(type_str, "heli")) obj_type = OBJ_HELI;
	else if (!stricmp(type_str, "tower")) obj_type = OBJ_TOWER;
	else if (!stricmp(type_str, "scenery")) obj_type = OBJ_SCENERY;
	else if (!stricmp(type_str, "building")) obj_type = OBJ_BUILDING;
	else if (!stricmp(type_str, "pickup")) obj_type = OBJ_PICKUP;

	if (obj_type == -1) {
		my_alert("error in file", buf);
		return;
	}

	if (obj != NULL) obj->~object();
	strcpy(add_object_file, buf);
	obj = make_object(obj_type, buf);
}

// shows a dialog with all mission properties
bool properties_dialog() {
	if (mis == NULL) return FALSE;

	int ret = dialogf("Mission Properties", ALIGN_CENTER, ALIGN_CENTER, 300,
		"Mission Name:%string[64]"
		"Mission Description:%string[1024]"
		"Mission Icon:%int[0,4]"
		"%line[]"
		"Graphics:%filename[1024,DAT,Choose a graphics data file. (*.DAT)]"
		"%line[]"
		"Authorize level:%bool[]",
		mis->name,
		mis->text,
		&mis->micon,
		mis->gfx_file,
		&mis->password);

	if (ret == 2) return FALSE; // cancel

	sprintf(mis->gfx_file, "data/%s", get_filename(mis->gfx_file));

	if (mis->gfx != NULL) unload_datafile(mis->gfx);
	packfile_password(woody_str);
	mis->gfx = load_datafile(mis->gfx_file);
	packfile_password(NULL);
	if (mis->gfx == NULL) return FALSE;

	return TRUE;
}

// creates a new mission
void new_mission() {
	RGB blk = { 0, 0, 0 };
	set_color(0,&blk);

	if (mis != NULL) mis->~mission();
	mis = new mission(gen);

	if (!properties_dialog()) {
		mis->~mission();	
		mis = NULL;
	}

	set_palette((RGB *)mis->gfx[0].dat);
	make_colormaps();
	set_color(0,&blk);

	object *o = make_object(OBJ_HELI, "data/objects/heli.txt");
	o->x = o->y = 100000;
	mis->add_object(o, TRUE);		
	mis->o = &options;
	strcpy(current_file, "unnamed_mission");
}

// removes an object from the mission
void delete_object(int index) {
	if (!index) return; // dont remove obj 0 ( = player heli)
	mis->obj[index]->empty(mis);
	mis->obj[index]->~object();
	mis->obj[index] = NULL;
}


// returns -1 on cancel
int image_selector(char *title, char *type, int org) {
	char buf[256];
	char file[1024];
	int image_id[1024];
	int num_images = 0;
	FILE *fp;
	char *ret;
	int curr_img = 0;

	replace_extension(file, mis->gfx_file, "h", sizeof(file));	

	fp=fopen(file,"rt");
	if (!fp) return -1;
	ret = fgets(buf, 256, fp);  // read a line
	do {
		if (strstr(buf, type) != NULL) {	// found a row with 'type' in
			buf[50] = '\0';
			image_id[num_images] = atoi(&buf[32]);
			if (image_id[num_images] == org) curr_img = num_images;
			num_images++;
		}
		ret = fgets(buf, 256, fp);  // read a line
	} while(ret != NULL);
	
	fclose(fp);

	if (num_images == 0) return -1;
	
	// do selection stuff
	BITMAP *dialog = create_bitmap(240, 320);
	BITMAP *sub_dialog = create_sub_bitmap(dialog, 10, 30, 220, 220);
	BITMAP *img;
	int done = 0;
	bool redraw = TRUE;
	int mx, my, mb;
	int curr_but = 0;

	// build dialog
	draw_empty_button(dialog, 0, 0, 239, 319, 6, 1, 4, 9, 12);
	textout_centre(dialog, dime_title_font, title, 120, 5, 15);

	while (!done) {
		mx = mouse_x-(1024/2-120); my = mouse_y-(768/2-160); mb = mouse_b;
		if (redraw) {
			draw_text_button(dialog, 40, 260, 90, 276, 6, 15, 1, 4, 9, 12, font, "Previous");
			draw_text_button(dialog, 40, 280, 90, 296, 6, 15, 1, 4, 9, 12, font, "Next");
			draw_text_button(dialog, 140, 260, 190, 276, 6, 15, 1, 4, 9, 12, font, "Ok");
			draw_text_button(dialog, 140, 280, 190, 296, 6, 15, 1, 4, 9, 12, font, "Cancel");
			if (curr_but == 1) draw_text_button_down(dialog, 40, 260, 90, 276, 6, 15, 1, 4, 9, 12, font, "Previous");
			if (curr_but == 2) draw_text_button_down(dialog, 40, 280, 90, 296, 6, 15, 1, 4, 9, 12, font, "Next");
			if (curr_but == 3) draw_text_button_down(dialog, 140, 260, 190, 276, 6, 15, 1, 4, 9, 12, font, "Ok");
			if (curr_but == 4) draw_text_button_down(dialog, 140, 280, 190, 296, 6, 15, 1, 4, 9, 12, font, "Cancel");
			clear_to_color(sub_dialog, 254);
			rect(sub_dialog, 0, 0, 219, 219, 15);
			img = (BITMAP *)mis->gfx[image_id[curr_img]].dat;
			draw_sprite(sub_dialog, img, 110-img->w/2, 110-img->h/2);
			textprintf(sub_dialog, font, 5, 5, 15, "Image: %d/%d", curr_img+1, num_images);
			textprintf(sub_dialog, font, 4, 4, 1, "Image: %d/%d", curr_img+1, num_images);
			show_mouse(NULL);
			draw_sprite(screen, dialog, 1024/2-120, 768/2-160);
			show_mouse(screen);
			redraw = FALSE;
		}

		// check buttons
		if (mb == 1 && !curr_but) {
			if (mx>40 && mx<90 && my>260 && my<276) curr_but = 1;
			if (mx>40 && mx<90 && my>280 && my<296) curr_but = 2;
			if (mx>140 && mx<190 && my>260 && my<276) curr_but = 3;
			if (mx>140 && mx<190 && my>280 && my<296) curr_but = 4;
			if (curr_but) redraw = TRUE;
		}
		else if (!mb && curr_but) {	// button released
			if (curr_but == 1 && curr_img) curr_img --;
			if (curr_but == 2 && curr_img < num_images-1) curr_img ++;
			if (curr_but == 4) done = -1;
			if (curr_but == 3) return image_id[curr_img];
			curr_but = 0;
			redraw = TRUE;
		}
	}

	return done;
}


// short cut for m = { txt, NULL,NULL,0,NULL };
MENU make_menu_post(char *txt) {
	MENU m = { txt, NULL, NULL, 0, NULL };
	return m;
}

// shows the object menu
void do_object_menu(int mx, int my) {
	char buf[256];
	MENU m[32];
	int m_count = 0;
	int type = mis->obj[selected_object]->type;
	int i;

	// build object specific menu
	if (type == OBJ_BUILDING) {
		m[m_count++] = make_menu_post("Change Image");
		building *b = (building *)mis->obj[selected_object];
		if (b->num_occupants) {
			sprintf(buf, "Empty (%d)", b->num_occupants);
			m[m_count++] = make_menu_post(buf);
		}
	}
	if (type == OBJ_SCENERY) m[m_count++] = make_menu_post("Change Image");
	if (type == OBJ_TANK || type == OBJ_TRUCK || type == OBJ_JEEP || type == OBJ_HELI || type == OBJ_TOWER) {
		vehicle *v = (vehicle *)mis->obj[selected_object];
		if (v->num_passengers) {
			sprintf(buf, "Empty (%d)", v->num_passengers);
			m[m_count++] = make_menu_post(buf);
		}
	}
	if (type != OBJ_SCENERY && type != OBJ_BUILDING && type != OBJ_PICKUP) m[m_count++] = make_menu_post("Edit Path Nodes");

	// add rest of menu (in a nice one-liner :) )
	i = -1;	do { m[m_count++] = object_menu[++i]; } while(object_menu[i].text != NULL);

	int index = do_menu(m, mx, my);
	
	if (index != -1) {
		if (!strnicmp(m[index].text, "empty", 5)) mis->obj[selected_object]->empty(mis);
		else if (!stricmp(m[index].text, "change image")) {
			mis->obj[selected_object]->type_to_string(buf);
			char title[64];
			sprintf(title, "Select Image [%s]", buf);
			int im = image_selector(title, "OBJ", mis->obj[selected_object]->base_image);
			if (im != -1) { 
				mis->set_obj_image(selected_object, im);
				add_object_type = type;
				add_object_img = im;
			}
		}
		else if (!stricmp(m[index].text, "delete")) {
			delete_object(selected_object);
			selected_object = -1;
		}
		else if (!stricmp(m[index].text, "edit path nodes")) {
			// enter waypoint mode
			wp_mode = TRUE;
			wp_unit = (unit *)mis->obj[selected_object];
			if (!wp_unit->wp[0].active) {
				wp_unit->wp[0].x = wp_unit->x+100;
				wp_unit->wp[0].y = wp_unit->y+200;
				wp_unit->wp[0].active = TRUE;
			}
	
		}
		else if (!stricmp(m[index].text, "properties")) {
			if (type == OBJ_PICKUP) ((pickup*)mis->obj[selected_object])->props_dialog(); 
			if (type == OBJ_BUILDING) ((building *)mis->obj[selected_object])->props_dialog(mis->gfx); 
			if (type == OBJ_SCENERY) ((scenery *)mis->obj[selected_object])->props_dialog(mis->gfx); 
			if (type == OBJ_SOLDIER || type == OBJ_FOOT_SOLDIER || type == OBJ_BAZOOKA_SOLDIER ) ((soldier *)mis->obj[selected_object])->props_dialog(mis->gfx); 
			if (type == OBJ_TANK || type == OBJ_TRUCK || type == OBJ_JEEP || type == OBJ_HELI || type == OBJ_TOWER) ((vehicle *)mis->obj[selected_object])->props_dialog(mis->gfx); 
		}
		else {
			char buf[128]; 
			sprintf(buf, "unknown menu index: '%s' (%d)", m[index].text, index);
			my_alert("do_object_menu", buf);
		}
	}
}

// shows the default menu
void do_default_menu(int mx, int my) {
	int index = do_menu(default_menu, mx, my);
	switch(index) {
		case 0:// add object
			add_object_menu(mx, my-150);
			break;
		case 1:// add zone
			object_mode = 99;
			rect_mode = 1;
			break;
		case 3:// add sub mission
			sub_mode = TRUE;
			break;
		case 5:// new
			new_mission();
			break;
		case 6:// load
			load_mission();
			break;
		case 7:// save
			save_mission();
			break;
		case 9:// properties
			properties_dialog();
			break;
		case 11:	// quit
			quit_edit = confirm_quit();
			break;
		case -1:// outside menu
			// do_nothing();
			break;
		default:
			char buf[128]; 
			sprintf(buf, "unknown menu index: '%s' (%d)", default_menu[index].text, index);
			my_alert("do_default_menu", buf);
			break;
	}
}

// loads a mission
void load_mission() {
	mission *m = new mission(gen); 
	char path[1024];
	strcpy(path, "missions/");
	
	int ret = file_select("Load mission...", path, "MIS");

	if (ret) {
		if (m->load(path)) {
			if (m->password && !exists("missions/letmeope.nit")) {
				my_alert("load_mission", "You are not authorized to open this file.");
				m->~mission();
			}
			else {			
				RGB blk = { 0, 0, 0 };
				mis->~mission();
				mis = m;
				strcpy(mis->obj[0]->file, "heli.txt"); // todo: tmp
				mis->o = &options;
				strcpy(current_file, get_filename(path));
				set_palette((RGB *)mis->gfx[0].dat);
				set_color(0,&blk);
				make_colormaps();
			}
		}
		else {
			my_alert("load_mission", "failed");
		}
	}
}

// loads a mission
void save_mission() {
	char path[1024];
	strcpy(path, "missions/");
	strcat(path, current_file);
	if (file_select("Save mission as...", path, "MIS"))	{
		mis->save(path);
		char bkp[128];
		sprintf(bkp, "missions/bkp/%s_%05d.mis", get_filename(path), rand()%100000);
		mis->save(bkp);
	}
	strcpy(current_file, get_filename(path));
}

// updates all objects using their config files
void update_objects() {
	int i;
	char path[256];
	for(i=0;i<MAX_OBJECTS;i++)
		if (mis->obj[i] != NULL) {
			int type = mis->obj[i]->type;
			sprintf(path, "data/objects/%s", mis->obj[i]->file);
			if (type == OBJ_SOLDIER || type == OBJ_FOOT_SOLDIER || type == OBJ_BAZOOKA_SOLDIER) ((soldier *)mis->obj[i])->set_from_file(path);
			if (type == OBJ_PICKUP) ((pickup *)mis->obj[i])->set_from_file(path);
			if (type == OBJ_TANK || type == OBJ_TRUCK || type == OBJ_JEEP || type == OBJ_HELI || type == OBJ_TOWER) ((vehicle *)mis->obj[i])->set_from_file(path);
		}

	my_alert("All objects have been updated!","Great, huh?!");
}

void draw_nodes(BITMAP *bmp, unit *u, bool much) {
	int i;

	for(i=0;i<MAX_WAYPOINTS;i++)
		if (u->wp[i].active) {
			int x = u->wp[i].x-cx+HELI_X;
			int y = u->wp[i].y-cy+HELI_Y;

			if (i>0) line(bmp, x, y, u->wp[i-1].x-cx+HELI_X, u->wp[i-1].y-cy+HELI_Y, (much?1:8));
			else line(bmp, x, y, u->x-cx+HELI_X, u->y-cy+HELI_Y, 8);
			if (much) {
				circle(bmp, x+1, y+1, 20, (much?15:11));
				circle(bmp, x, y, 20, (much?1:8));
			}
			if (u->wp[i].type) circlefill(bmp, x, y, 12, (much?15:11));
			else {
				circle(bmp, x+1, y+1, 12, (much?15:11));
				circle(bmp, x, y, 12, (much?1:8));
			}

			switch(u->wp[i].type) {
				case WP_GOAL: 
					line(bmp, x-7, y-7, x+7, y+7, (much?1:8));
					line(bmp, x+7, y-7, x-7, y+7, (much?1:8));
					break;
				case WP_RESTART: 
					line(bmp, x-7, y-7, x-7, y+7, (much?1:8));
					triangle(bmp, x-7, y, x, y-7, x, y+7, (much?1:8));
					triangle(bmp, x, y, x+7, y-7, x+7, y+7, (much?1:8));
					break;
				case WP_REVERSE: 
					line(bmp, x+6, y+7, x+6, y-5, (much?1:8));
					line(bmp, x+6, y-5, x, y-7, (much?1:8));
					line(bmp, x, y-7, x-6, y-5, (much?1:8));
					line(bmp, x-6, y-5, x-6, y+7, (much?1:8));
					line(bmp, x-6, y+7, x-8, y+5, (much?1:8));
					line(bmp, x-6, y+7, x-4, y+5, (much?1:8));
					break;
				case WP_UNLOAD: 
					rect(bmp, x-7, y-7, x, y+7, (much?1:8));
					line(bmp, x-2, y, x+7, y, (much?1:8));
					line(bmp, x+2, y-3, x+7, y, (much?1:8));
					line(bmp, x+2, y+3, x+7, y, (much?1:8));
					break;
			}
		}
}

void handle_wp(int mx, int my) {
	int mb = mouse_b;
	int i;
	static handle_wp_mode = 0;

	if (current_wp == -1 && mb == 1 && !handle_wp_mode) {
		for(i=0;i<MAX_WAYPOINTS;i++)
			if (wp_unit->wp[i].active)
				if (mx+cx-HELI_X > wp_unit->wp[i].x - 20 && 
					mx+cx-HELI_X < wp_unit->wp[i].x + 20 && 
					my+cy-HELI_Y > wp_unit->wp[i].y - 20 && 
					my+cy-HELI_Y < wp_unit->wp[i].y + 20
				) {
					// pick up node
					current_wp = i;
					handle_wp_mode = -1;
				}
	}
	else { 
		wp_unit->wp[current_wp].x = mx+cx-HELI_X;
		wp_unit->wp[current_wp].y = my+cy-HELI_Y;
		handle_wp_mode = 1;
	}

	if (handle_wp_mode == -1 && mb == 0) handle_wp_mode = 0;
	if (handle_wp_mode == 1 && mb == 0) {
		current_wp = -1;
		handle_wp_mode = 0;
	}

	if (mb == 2) {
		int wp = -1;
		for(i=0;i<MAX_WAYPOINTS;i++)
			if (wp_unit->wp[i].active)
				if (mx+cx-HELI_X > wp_unit->wp[i].x - 20 && 
					mx+cx-HELI_X < wp_unit->wp[i].x + 20 && 
					my+cy-HELI_Y > wp_unit->wp[i].y - 20 && 
					my+cy-HELI_Y < wp_unit->wp[i].y + 20
				) {
					// remember node
					wp = i;
				}

		if (wp_unit->collision(cx+mx-HELI_X, cy+my-HELI_Y, 0, 0, COLL_RECTANGLE) || wp >= 0) {
			// launch menu
			show_mouse(screen);
			int index = do_menu(wp_menu, mx, my);
	
			if (index != -1) {
				if (!stricmp(wp_menu[index].text, "reset")) {
					wp_unit->reset_wp();
					wp_unit->wp[0].x = mx+cx-HELI_X + 40;
					wp_unit->wp[0].y = my+cy-HELI_Y + 40;
					wp_unit->wp[0].active = TRUE;
					wp_unit->wp[0].type = 0;
				}
				if (!stricmp(wp_menu[index].text, "insert")) {
					// move all wp's above wp up one step (lose last wp (TODO: warn))
					for(int j=MAX_WAYPOINTS-1;j>wp;j--)
						wp_unit->wp[j] = wp_unit->wp[j-1];
					wp_unit->wp[wp+1].x = wp_unit->wp[wp+1].x + 40;
					wp_unit->wp[wp+1].y = wp_unit->wp[wp+1].y + 10;
					wp_unit->wp[wp+1].type = 0;
				}
				else if (!stricmp(wp_menu[index].text, "exit path mode")) {
					wp_mode = FALSE;
				}
				if (wp != -1) {
					if (!stricmp(wp_menu[index].text, "delete")) {
						// move all wp's above wp down one step
						for(int j=wp;j<MAX_WAYPOINTS-1;j++)
							wp_unit->wp[j] = wp_unit->wp[j+1];
						wp_unit->wp[MAX_WAYPOINTS-1].active = FALSE;
					}
					else if (!stricmp(wp_menu[index].text, "properties")) {
						int a = (wp_unit->wp[wp].type == WP_GOAL    ? TRUE : FALSE);
						int b = (wp_unit->wp[wp].type == WP_REVERSE ? TRUE : FALSE);
						int c = (wp_unit->wp[wp].type == WP_RESTART ? TRUE : FALSE);
						int d = (wp_unit->wp[wp].type == WP_UNLOAD  ? TRUE : FALSE);
	
						dialogf("Node Properties" , mx, my, 300,
							"Stop %bool[]"
							"Reverse %bool[]"
							"Restart %bool[]"
							"Unload %bool[]",
							&a, &b, &c, &d);
	
						wp_unit->wp[wp].type = 0;
						if (a) wp_unit->wp[wp].type = WP_GOAL;
						if (b) wp_unit->wp[wp].type = WP_REVERSE;
						if (c) wp_unit->wp[wp].type = WP_RESTART;
						if (d) wp_unit->wp[wp].type = WP_UNLOAD;
					}
				}
			}
		}
	}
}

// draws sub missions
void draw_sub_mode(BITMAP *bmp, int mx, int my) {
	int i;

	// draw current targets
	if (current_sub != -1) 
		if (mis->sub[current_sub] != NULL)
			for(i=0;i<MAX_TARGETS;i++)
				if (mis->sub[current_sub]->target[i] != -1)
					if (mis->obj[mis->sub[current_sub]->target[i]] != NULL) {
						int x = mis->obj[mis->sub[current_sub]->target[i]]->x-cx+HELI_X;
						int y = mis->obj[mis->sub[current_sub]->target[i]]->y-cy+HELI_Y;
						circle(bmp, x, y, 24, 15);
						circle(bmp, x, y, 18, 1);
						circle(bmp, x, y, 20, 15);
						circle(bmp, x, y, 22, 1);
					}

	if (add_target) textout_centre(bmp, (FONT *)gen[FONT_DIALOG_BOLD].dat, "CLICK ON TARGET TO ADD IT", SCREEN_W/2, 22, 1);

	// draw menus
	rectfill(bmp, SCREEN_W-100, 121, SCREEN_W, SCREEN_H, 6);
	textout(bmp, (FONT *)gen[FONT_DIALOG_BOLD].dat, "SUBMENU", SCREEN_W-99, 122, 15);

	for(i=0;i<MAX_SUB_MISSIONS;i++) {
		if (mis->sub[i] != NULL) {
			if (i == current_sub) rectfill(bmp, SCREEN_W-98, 138+i*32, SCREEN_W, 158+i*32, 3);
			else rect(bmp, SCREEN_W-98, 138+i*32, SCREEN_W, 158+i*32, 3);
			textprintf(bmp, font, SCREEN_W-96, 140+i*32, 0, "%d: %s", i, mis->sub[i]->name);
		}
	}
}	


void make_new_mission() {
	int i=0;
	while(mis->sub[i] != NULL && i<MAX_SUB_MISSIONS) i++;
	if (i == MAX_SUB_MISSIONS) return;

	sub_mission *s = new sub_mission();
	s->props_dialog();
	mis->sub[i] = s;
}

// handles sub missions
void handle_sub_mode(int mx, int my) {
	int i, m = -1;
	int mb = mouse_b;

	if (mx > SCREEN_W - 100 && my > 121 && mb) { // show menu
		for(i=0;i<MAX_SUB_MISSIONS;i++) if (my > 140 + i*32 && my < 156 + i*32 && mis->sub[i] != NULL) m = i;
		if (m == -1) current_sub = -1;
		if (mb == 2 && m == -1) {
			show_mouse(screen);
			int index = do_menu(main_sub_menu, mx, my);
			if (index != -1) {
				if (!stricmp(main_sub_menu[index].text, "exit sub mode")) {
					sub_mode = FALSE;
				}
				if (!stricmp(main_sub_menu[index].text, "new sub mission")) {
					make_new_mission();
				}
			}
			show_mouse(NULL);
		}
		else if (m != -1) {
			current_sub = m;
			if (mb == 2) {
				show_mouse(screen);
				int index = do_menu(sub_menu, mx, my);
				if (index != -1) {
					if (!stricmp(sub_menu[index].text, "add targets")) {
						add_target = TRUE;
					}	
					if (!stricmp(sub_menu[index].text, "reset targets")) {
						for(i=0;i<MAX_TARGETS;i++) mis->sub[current_sub]->target[i] = -1;
						my_alert("Sub Mission", "All targets removed.");
					}
					if (!stricmp(sub_menu[index].text, "properties")) {
						mis->sub[current_sub]->props_dialog();
					}
					if (!stricmp(sub_menu[index].text, "delete")) {
						mis->sub[current_sub]->~sub_mission();
						mis->sub[current_sub] = NULL;
						current_sub = -1;
					}	
				}
				show_mouse(NULL);
			}
		}
	}
}

// the editor ;)
void edit() {
	int rx1,ry1,rx2,ry2;	// multi select rectangle
	int i;
	int mx, my, mb;
	bool mini_click;

	new_mission();

	// reset camera
	cx = mis->obj[0]->x;
	cy = mis->obj[0]->y;
	ca = 0;	

	while(!quit_edit) {
		mx = mouse_x;
		my = mouse_y;
		mb = mouse_b;

		/////// draw screen
		draw_screen(swap_screen);
		if (object_mode == 100) {
			rx2 = cx+mx-HELI_X;
			ry2 = cy+my-HELI_Y;
			rect(swap_screen, rx1+1-cx+HELI_X, ry1+1-cy+HELI_Y, rx2+1-cx+HELI_X, ry2+1-cy+HELI_Y, 15);
			rect(swap_screen, rx1-cx+HELI_X, ry1-cy+HELI_Y, rx2-cx+HELI_X, ry2-cy+HELI_Y, 1);
		}
		
		if (selected_object != -1 && obj == NULL) {
			if (mis->obj[selected_object]->is_unit()) draw_nodes(swap_screen, (unit *)mis->obj[selected_object], wp_mode);
		}
		

		if (sub_mode) draw_sub_mode(swap_screen, mx,my);

		draw_mini_map();
		draw_sprite(swap_screen, mini_map, SCREEN_W-mini_map->w, 0);
		draw_sprite(swap_screen, (BITMAP *)gen[MPTR_EDIT].dat, mx, my);
		blit_to_screen(swap_screen);

		/////// handle input

		if (wp_mode) handle_wp(mx,my);
		if (sub_mode) handle_sub_mode(mx,my);

		if (mb==1 && mx>SCREEN_W-mini_map->w && my<mini_map->h && object_mode != 100) {  // clicked on minimap
			cx = (mini_map->w - (SCREEN_W - mx) - mini_map->w/2)*zoom+100000;
			cy = (my-mini_map->h/2)*zoom+100000;
			mini_click = TRUE;
		} else mini_click = FALSE;

		if (mb == 1 && !mini_click && !wp_mode) {
			if (object_mode == 99) {
				object_mode = 100;
				rx1 = cx+mx-HELI_X;
				ry1 = cy+my-HELI_Y;
			}

			if (obj != NULL) { // holding object
				if (object_mode == 0) {  
					// put down object
					int obj2 = -1;
					for(i=0;i<MAX_OBJECTS;i++)
						if (mis->obj[i] != NULL) 
							if (mis->obj[i]->collision(cx+mx-HELI_X, cy+my-HELI_Y, 0,0, COLL_RECTANGLE) && !mis->obj[i]->inside) 
								obj2 = i; 

					if (obj2 != -1) {  // in another object
						if (mis->obj[obj2]->add_object(obj)) {
							mis->set_object(obj, picked_object);
							picked_object = -1;
							obj = NULL;
							object_mode = 10; // wait for lift of left mb
						}
					}
					else {
						obj->set_position(cx + mx - HELI_X, cy + my - HELI_Y);
						if (picked_object != -1) {
							mis->set_object(obj, picked_object);
							picked_object = -1;
						}
						else mis->add_object(obj, TRUE);
						object_mode = 1;	// rotate mode
					}
				}
				else {
					if (object_mode == 1) {		// rotate object
						if (key[KEY_LCONTROL]) obj->angle = fatan2(ftofix(obj->y-(cy+my-HELI_Y)), ftofix(obj->x-(cx+mx-HELI_X)));
						if (key[KEY_LSHIFT]) obj->angle = itofix((fixtoi(obj->angle)>>4)<<4);
					}
				}
			}
			else { // not holding object
				if (selected_object != -1 && object_mode == 0) { // an object has been selected
					// can I pick it up?
					if (mis->obj[selected_object]->collision(cx+mx-HELI_X, cy+my-HELI_Y, 0,0, COLL_RECTANGLE)) {
						// pick it up
						obj = mis->obj[selected_object];
						mis->obj[selected_object] = NULL;
						picked_object = selected_object;
						object_mode = 10;
					}
				}

				// selecting an object?
				if (object_mode == 0) {
					// check mouse againt mission objects
					selected_object = -1;
					if (mis != NULL)
						for(i=0;i<MAX_OBJECTS;i++)
							if (mis->obj[i] != NULL) {
								if (mis->obj[i]->collision(cx+mx-HELI_X, cy+my-HELI_Y, 0,0, COLL_RECTANGLE) && !mis->obj[i]->inside) {
									mis->obj[i]->selected = TRUE; 
									selected_object = i;
								}
								else if (!key[KEY_LCONTROL]) mis->obj[i]->selected = FALSE; 
							}
	
						if (selected_object != -1) object_mode = 10;
						else {  // init multi select rectangle
							object_mode = 100;
							rect_mode = 2;
							rx1 = cx+mx-HELI_X;
							ry1 = cy+my-HELI_Y;
						}
				}

				// selecting zone?
				int z = mis->get_zone(cx+mx-HELI_X, cy+my-HELI_Y, TRUE);
				if (z!=-1) {
					show_mouse(screen);
					int index = do_menu(zone_menu, mx, my);
					if (index != -1) {
						if (!stricmp(zone_menu[index].text, "move")) {
							my_alert("hahaha", "no can do");
						}
						if (!stricmp(zone_menu[index].text, "delete")) {
							mis->zone[z].active = FALSE;
						}
					}
				}
			}
		}

		if (mb == 0) {
			if (obj != NULL) {
				if (object_mode == 1) {		// drop object
					add_object_img = obj->base_image;
					add_object_type = obj->type;
					add_object_ang = fixtoi(obj->angle);
					obj = NULL;
					object_mode = 0;
				}
			}
			if (object_mode == 10) {
				object_mode = 0;
				if (add_target && current_sub != -1 && sub_mode && selected_object != -1) {
					if (!mis->sub[current_sub]->add_target(selected_object)) {
						my_alert("?out of targets error", "could not add target");
					}
					add_target = FALSE;
				}
			}
			if (object_mode == 100) {
				int x1 = MIN(rx1,rx2);
				int x2 = MAX(rx1,rx2);
				int y1 = MIN(ry1,ry2);
				int y2 = MAX(ry1,ry2);
				if (key[KEY_F]) { // make forest
					int trees = (x2-x1)*(y2-y1)/120000;
					for(i=0;i<trees;i++) {
						int xp = x1 + rand()%(x2-x1);
						int yp = y1 + rand()%(y2-y1);
						scenery *tree = (scenery *)make_object(OBJ_SCENERY, "data/objects/tree.txt");
						tree->set_position(xp, yp);
						tree->altitude = GL_GROUND_SCENERY;
						int r = rand()%100;
						if (r < 50) {
							if (r < 20) tree->base_image += 2;
							else tree->base_image ++;
						}
						tree->wreck_image = tree->shadow_image = tree->base_image;
						mis->add_object(tree, TRUE);
					}
				}
				else if (rect_mode == 2) { // multi select
					for(i=0;i<MAX_OBJECTS;i++)
						if (mis->obj[i] != NULL)
							if (mis->obj[i]->x>x1 && mis->obj[i]->x<x2 && mis->obj[i]->y>y1 && mis->obj[i]->y<y2)
								mis->obj[i]->selected = TRUE; 
				}
				else if (rect_mode == 1) { // add_zone
					if (!mis->add_zone(x1, y1, x2, y2)) my_alert("add_zone", "no zones left");
				}
				object_mode = 0;
			}
		}

		if (mb == 2 && !wp_mode && !sub_mode && object_mode == 0) {  // menu request
			draw_screen(swap_screen);
			blit_to_screen(swap_screen);
			show_mouse(screen);
			if (selected_object == -1 && obj == NULL) do_default_menu(mx ,my);
			if (selected_object != -1 && obj == NULL) 
				if (!mis->obj[selected_object]->inside) do_object_menu(mx, my);
		}

		int team = -1;
		if (key[KEY_0]) team = 0;
		if (key[KEY_1]) team = 1;
		if (key[KEY_2]) team = 2;
		if (key[KEY_3]) team = 3;
		if (key[KEY_4]) team = 4;
		if (team != -1) 
			for(i=0;i<MAX_OBJECTS;i++)
				if (mis->obj[i] != NULL)
					if (mis->obj[i]->selected)
						mis->obj[i]->team = team;

		int layer = -1;
		if (key[KEY_8]) layer = GL_GROUND_FLAT_SCENERY;
		if (key[KEY_9]) layer = GL_GROUND_SCENERY;
		if (key[KEY_0]) layer = GL_MIDDLE;
		if (layer != -1) 
			for(i=0;i<MAX_OBJECTS;i++)
				if (mis->obj[i] != NULL)
					if (mis->obj[i]->selected && mis->obj[i]->type == OBJ_SCENERY)
						mis->obj[i]->altitude = layer;

		if (key[KEY_ALT] && !wp_mode) {
			if (key[KEY_R] && obj == NULL) obj = make_object(-1, NULL);
			if (key[KEY_A] && obj == NULL) add_object_menu(mx, my-150);
			if (key[KEY_L])	load_mission();
			if (key[KEY_S]) save_mission();
			if (key[KEY_U]) update_objects();
			if (key[KEY_N]) {
				int first = image_selector("First Image", "OBJ", 1);
				int last = image_selector("Last Image", "OBJ", first);
				mis->random_noise(first, last);
			}
		}
		
		if (key[KEY_DEL])
			for(i=0;i<MAX_OBJECTS;i++)
				if (mis->obj[i] != NULL)
					if (mis->obj[i]->selected) {
						delete_object(i);
						selected_object = -1;
					}

		if (key[KEY_ESC]) quit_edit = confirm_quit();
		if (key[KEY_F1]) save_bmp("medit.bmp",swap_screen,NULL);
		if (key[KEY_UP] || my == 0) cy -= 8;
		if (key[KEY_DOWN] || my == SCREEN_H-1) cy += 8;
		if (key[KEY_LEFT] || mx == 0) cx -= 8;
		if (key[KEY_RIGHT] || mx == SCREEN_W-1) cx += 8;
		if (key[KEY_PGUP]) ca -= 1;
		if (key[KEY_PGDN]) ca += 1;
		
		// keep this line last
		show_mouse(NULL);  
	}	
}

///////////////////////////////////////////////////////////////
// MAIN STUFF
///////////////////////////////////////////////////////////////

// main
int main(int argc, char *argv[]) {
	init_editor();
	edit();
	return 0;
} END_OF_MAIN();


