/* Bob and entheh */
#include <string.h>
#include <time.h>
#include <math.h>

#include <allegro.h>
#include <aldumb.h>

#include "main.h"
#include "screen.h"
#include "timeloop.h"
#include "zcontrol.h"
#include "log.h"
#include "options.h"
#include "menu.h"
#include "m_main.h"
#include "ripples.h"
#include "level.h"
#include "intro.h"
#include "s_pwrup.h"
#include "s_expp.h"
#include "draw.h"


int screen_w, screen_h;

FONT *MenuFont = NULL;
FONT *LargeFont = NULL, *SmallFont = NULL;

BITMAP *PowerUpBox = NULL;
BITMAP *Controler[4] = { NULL, NULL, NULL, NULL };
BITMAP *Pod[N_PLAYERS][8], *BloodyPod[N_PLAYERS][8];
BITMAP *Logo = NULL, *LogoBomb = NULL, *MiniBomb = NULL, *Logo2 = NULL;
BITMAP *Animals[4];
BITMAP *StartLight[4];
BITMAP *Powerup[N_POWER_UPS][N_POWERUP_ANIM_FRAMES];
BITMAP *Bomb[8];
BITMAP *MagBomb;

BITMAP *Animal[4];
BITMAP *Portal[7];

BITMAP *block_info[N_TEXTURES][N_SURFACES][N_BLOOD_LEVELS][N_DAMAGE_LEVELS];

BITMAP *LeftArrow, *RightArrow;

Sample_t Sample;

RGB *GamePal;
DATAFILE *music_dat;
DUH *Music[4];
AL_DUH_PLAYER *adp = NULL;


/* An array of pointers to the players' names. */
const char *player_orig_name[N_PLAYERS] = {"Cat", "Rabbit", "Duck", "Chipmunk"};
char *player_name[N_PLAYERS] = {NULL, NULL, NULL, NULL};

/* Global datafile. datafile.h contains object indices. */
DATAFILE *dat;
RGB_MAP palette_map;
COLOR_MAP add_map;
RIPPLE_COLOURS water_colours;
RIPPLE_COLOURS acid_colours;


static void create_add_map() {
 int x, y;
 for (y = 0; y < 256; y++) {
  for (x = 0; x < 256; x++) {
   add_map.data[y][x] = rgb_map->data[MIN(GamePal[x].r + GamePal[y].r, 63) >> 1]
                                     [MIN(GamePal[x].g + GamePal[y].g, 63) >> 1]
                                     [MIN(GamePal[x].b + GamePal[y].b, 63) >> 1];
  }
 }
}


/* The main module must do the following (in this order):
   - Set the config file, "bomb.ini".
   - Load the datafile, "bomb.dat".
   - Load, or else create and save, various tables (see below).
   - Load all the levels. If this fails, exit. See level.h.

   - initialise_timeloop();
   - initialise_control();

   - Load the options (see options.h).

   It must save and/or shut down all these things in reverse order at the
   end.

   The tables required are as follows:

   - An RGB_MAP for the palette in the datafile. Only colours 1 to 239 must
     be mapped, so we use a duplicate palette with colours 240 to 255 set to
     black (equal to colour 1) in order to create this table. Only the first
     of any equal colours will be used, which is 1.

   - A set of partial colour maps for the explosions. create_explosion_map()
     in s_expp.h will create these.
*/

void load_game_data() {

	int load_w = SCREEN_W / 2, load_h = SCREEN_H / 20;
	int total_steps = 11, step = 0;
	int i, j;

	int r, g, b;

	textout(screen, font, "Loading...", SCREEN_W / 2 - load_w / 2, SCREEN_H / 2 - load_h / 2 - 10, makecol(255, 255, 255));
	rect(screen, SCREEN_W / 2 - load_w / 2, SCREEN_H / 2 - load_h / 2, SCREEN_W / 2 + load_w / 2, SCREEN_H / 2 + load_h / 2, makecol(255, 0, 0));

#define UPDATE_LOAD_BAR(foo) { \
	hsv_to_rgb(step * 100.0 / total_steps, 1, 1, &r, &g, &b); \
	rectfill(screen, SCREEN_W / 2 - load_w / 2 + 2 + step * (load_w - 3) / total_steps, SCREEN_H / 2 - load_h / 2 + 2, \
		SCREEN_W / 2 - load_w / 2 + (step + 1) * (load_w - 3) / total_steps, SCREEN_H / 2 + load_h / 2 - 2, makecol(r, g, b)); \
		text_mode(0); \
		textout(screen, font, foo, SCREEN_W / 2 - load_w / 2, SCREEN_H / 2 + load_h / 2 + 2, makecol(255, 255, 255)); \
		step++; \
	}
	
	if (bitmap_color_depth(screen) != 8)
		set_color_conversion(COLORCONV_TOTAL);
	else
		set_color_conversion(COLORCONV_NONE);

	/* Load Bomb data file, convert to color depth, scale if necessary */
	dat = load_datafile("data/bomb.dat");
	if (!dat) {
		destroy_screen();
		allegro_message("Can't load datafile!\ndata/bomb.dat\n");
		exit(-2);
	}
	
	/* Set the loaded palette here */
	GamePal = (RGB*)dat[GAME_PAL].dat;

	if (bitmap_color_depth(screen) == 8)
		set_palette(GamePal);
	else
		select_palette(GamePal);

	UPDATE_LOAD_BAR("Color mapping tables     ");

	if (!exists("data/colormap.bin")) {
		PACKFILE *file;

		/* Create all RGB and COLOR_MAP tables */

        /* Colours 240 to 255 should never be used by the RGB table. We
           achieve this by setting them equal to black, equal to colour 1;
           the first colour is always chosen when some are identical.
        */
        PALETTE rgbpal;
        memcpy(rgbpal, GamePal, sizeof(RGB *) * 240);
        memset(rgbpal + 240, 0, sizeof(RGB *) * 16);
		create_rgb_table(&palette_map, rgbpal, NULL);

		/* palette_map.data[31][0][31] = 0; - let's hope we don't need this :-P */
		rgb_map = &palette_map;

        create_add_map();

        create_ripple_colours(&water_colours,
                              /* highlight */ 255, 255, 255,
                              /* sky       */  32, 128, 255,
                              /* level     */ 160, 160, 160);

        create_ripple_colours(&acid_colours,
                              /* highlight */  96, 192,  64,
                              /* sky       */  32, 128,  32,
                              /* level     */  96,  96,  64);

        generate_explosion_colours();

		/* Write them to the datafile */
		file = pack_fopen("data/colormap.bin", F_WRITE_PACKED);

		if (file) {
			pack_fwrite(&palette_map, sizeof(RGB_MAP), file);
			pack_fwrite(&add_map, sizeof(COLOR_MAP), file);
			pack_fwrite(&water_colours, sizeof(RIPPLE_COLOURS), file);
			pack_fwrite(&acid_colours, sizeof(RIPPLE_COLOURS), file);
            pack_fwrite(&explosion_colours, sizeof(explosion_colours), file);

			pack_fclose(file);
		}
	}
	else {
		PACKFILE *file;

		file = pack_fopen("data/colormap.bin", F_READ_PACKED);

		if (file) {
			pack_fread(&palette_map, sizeof(RGB_MAP), file);
			pack_fread(&add_map, sizeof(COLOR_MAP), file);
			pack_fread(&water_colours, sizeof(RIPPLE_COLOURS), file);
			pack_fread(&acid_colours, sizeof(RIPPLE_COLOURS), file);
            pack_fread(&explosion_colours, sizeof(explosion_colours), file);

			pack_fclose(file);
		}
		rgb_map = &palette_map;
	}
	set_add_blender(0, 0, 0, 255);


	UPDATE_LOAD_BAR("Fonts                    ");
	MenuFont  = (FONT*)dat[FONT_LARGE].dat;
	LargeFont = (FONT*)dat[FONT_LARGE].dat;
	SmallFont = (FONT*)dat[FONT_SMALL].dat;

	UPDATE_LOAD_BAR("Overlay images           ");
	PowerUpBox = dat[POWERUPBOX_BMP].dat;
	Logo       = dat[LOGO_BMP].dat;
	Logo2      = dat[LOGO2_BMP].dat;
	LogoBomb   = dat[LOGOBOMB_BMP].dat;
	MiniBomb   = dat[MINIBOMB_BMP].dat;
	for (i = 0; i < 4; i++)
		StartLight[i] = dat[START_LIGHT_1_BMP + i].dat;

	UPDATE_LOAD_BAR("Controller images        ");
	Controler[0] = dat[CTRL_OFF_BMP].dat;
	Controler[1] = dat[CTRL_ZIG_AI_BMP].dat;
	Controler[2] = dat[CTRL_KEYBOARD_BMP].dat;
	Controler[3] = dat[CTRL_JOYPAD_BMP].dat;
	LeftArrow =  dat[LEFT_ARROW_BMP].dat;
	RightArrow = dat[RIGHT_ARROW_BMP].dat;

	UPDATE_LOAD_BAR("Player images            ");
	for (j = 0; j < N_PLAYERS; j++) {
		for (i = 0; i < 8; i++) {
			Pod[j][i] = dat[POD00_BMP + i + j * (POD10_BMP - POD00_BMP)].dat;
			BloodyPod[j][i] = dat[BP00_BMP + i + j * (BP10_BMP - BP00_BMP)].dat;
		}
	}

	UPDATE_LOAD_BAR("Powerup images           ");
	Powerup[0][0] = NULL;  Powerup[0][1] = NULL;  Powerup[0][2] = NULL;
	for (i = 1; i < 8; i++) {
		Powerup[i][0] = dat[PWUP1A_BMP + i - 1].dat;
		Powerup[i][1] = dat[PWUP1B_BMP + i - 1].dat;
		Powerup[i][2] = dat[PWUP1C_BMP + i - 1].dat;
	}

	for (i = 0; i < 7; i++)
		Portal[i] = dat[PORTAL0_BMP + i].dat;

	for (i = 0; i < 8; i++)
		Bomb[i] = dat[BOMB0_BMP + i].dat;
	MagBomb = dat[MAGBOMB_BMP].dat;

	UPDATE_LOAD_BAR("Animals                  ");
	Animal[0] = dat[COW_BMP].dat;
	Animal[1] = dat[DONKEY_BMP].dat;
	Animal[2] = dat[PIG_BMP].dat;
	Animal[3] = dat[SHEEP_BMP].dat;

	UPDATE_LOAD_BAR("Tile Textures            ");

#include "inittex.inc"

	UPDATE_LOAD_BAR("Sound effects            ");
	Sample.Explosion[0] = dat[EXPLO1_WAV].dat;
	Sample.Explosion[1] = dat[EXPLO2_WAV].dat;
	Sample.WallBang = dat[BANG_WAV].dat;
	Sample.MenuBeep1 = dat[BEEP_WAV].dat;
	Sample.MenuBeep2 = dat[BEEP2_WAV].dat;
	Sample.Error = dat[ERROR_WAV].dat;
	Sample.Warning = dat[WARNING_WAV].dat;
	Sample.Shift = dat[SHIFT_WAV].dat;


	UPDATE_LOAD_BAR("Sound tracks             ");
	music_dat = load_datafile("data/music.dat");
	if (music_dat) {
		Music[0] = music_dat[0].dat;
		Music[1] = music_dat[1].dat;
		Music[2] = music_dat[2].dat;
		Music[3] = music_dat[3].dat;
	} else {
		Music[0] = NULL;
		Music[1] = NULL;
		Music[2] = NULL;
		Music[3] = NULL;
	}

	UPDATE_LOAD_BAR("Level Data               ");
    load_levels();
}

void unload_game_data() {

	unload_levels();

	unload_datafile(dat);
	unload_datafile(music_dat);
}

#ifdef FORTIFY
void a_Fortify_LeaveScope(void) {
	Fortify_LeaveScope();
}
#endif

int main() {

	int i;

    #ifdef FORTIFY
		Fortify_SetOutputFunc(LOG);

		Fortify_EnterScope();

		atexit(a_Fortify_LeaveScope);
    #endif

	allegro_init();
	set_config_file("bomb.ini");

	i = get_config_int("sound", "quality", 2);
	if ((unsigned int)i > 2) i = 2;
	set_config_int("sound", "quality", i);

	/* Log init */
	LOG("\n\n\n");
	LOG("******************************************\n");
	LOG("*                                         \n");
	LOG("*  Set Up Us The Bomb !!!  v" VERSION    "\n");
	LOG("*                                         \n");
	LOG("******************************************\n");
	LOG("Starting:\n");

	LOG("Initializing Allegro modules ");
	install_keyboard();
	install_joystick(JOY_TYPE_AUTODETECT);
	install_timer();
	reserve_voices(64, -1);
	set_volume_per_voice(1);
	srandom(time(NULL));

	install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL);

	atexit(&dumb_exit);
	dumb_register_dat_it(DUMB_DAT_IT); // TEMPORARY?
	dumb_register_dat_xm(DUMB_DAT_XM);

	set_display_switch_mode(SWITCH_BACKAMNESIA);
	set_display_switch_callback(SWITCH_IN, SwitchCallback);
	set_window_title("Set Up Us The Bomb !!!");

	install_multilang(get_language());

	for (i = 0; i < N_PLAYERS; i++)
		player_name[i] = (char*)translate(player_orig_name[i]);


	set_keyboard_rate(0, 0);
	LOG("[ok]\n");


	/* Init modules */
	LOG("Initializing Timer and Control ");
	initialise_timeloop();
	initialise_control();
	init_powerups();
	LOG("[ok]\n");


	LOG("Loading game options ");
	screen_w = get_config_int("[screen]", "width", 640);
	screen_h = get_config_int("[screen]", "height", 480);

	load_options();

	LOG("[ok]\n");

	/* Set GFX mode */
	LOG("Setting up display ");
	create_screen(screen_w, screen_h);
	LOG("[ok]\n");

	/* Load data */
	LOG("Loading game data:\n");
	load_game_data();
	LOG("Game data loaded.\n");

	/* Run main loop */
	run_intro();

	adp = al_start_duh(Music[random()&3], 2, 0, 1.0f, 4096, 44100);

    run_main_menu();

	/* Unload everything */
	LOG("Shutting down...");
	shut_down_control();
	clean_up_draw();

	al_stop_duh(adp);
	adp = NULL;
	unload_game_data();

	save_options();

#ifdef FORTIFY
	Fortify_OutputStatistics();
#endif

	return 0;
} END_OF_MAIN();

