#include <SDL.h>

#ifdef _WIN32
#include <io.h>
#define F_OK 0
#define access _access
#else
#include <unistd.h>
#endif

#include "Audio.h"
#include "Bitmap.h"
#include "Console.h"
#include "ExMath.h"
#include "Game.h"
#include "Render.h"
#include "Inventory.h"
#include "Level.h"
#include "LevelGen.h"
#include "Log.h"
#include "Menus.h"
#include "Particle.h"
#include "RPK.h"
#include "System.h"
#include "Version.h"
#include "Video.h"
#include "VM.h"

static void InitAll(RPKFile *, struct Options *);
static void CleanupAll(void);

static bool keepRunning = true;
Bmp_t screen;

int main(int argc, char **argv)
{
	struct Options *options;
	RPKFile *rpkfile;
	int width, height, scale;

	const char *rpkname = NULL;
	if (argc > 1)
		rpkname = argv[1];

	OS_SetupCrashHandler();
	Math_SeedRNG(69420);

	/* Load options file and get the desired width/height etc. of the window */
	options = Game_LoadOptions();
	scale = Vid_ScaleResolution(options->width, options->height);
	width = options->width / scale;
	height = options->height / scale;

	screen = Bmp_Create(width, height);
	Vid_Init(width, height, scale, options->fullscreen);

	/* Find a default RPK file to load if none was specified */
	if (rpkname == NULL)
	{
		if (access("rovgend.rpk", F_OK) == 0)
			rpkname = "rovgend.rpk";
		else if (access("rovdemo.rpk", F_OK) == 0)
			rpkname = "rovdemo.rpk";
		else
		{
			Msg_Fatal("Unable to find game data file ('rovgend.rpk' or 'rovdemo.rpk')");
			return 1;
		}
	}
	/* Open the mod file and intialize all the various subsystems */
	rpkfile = RPK_OpenFile(rpkname);
	if (rpkfile == NULL)
	{
		Msg_Fatal("Unable to load game content from %s", rpkname);
		return 1;
	}
	InitAll(rpkfile, options);
	RPK_CloseFile(rpkfile);

	const Uint64 msPerTick = 1000 / GAME_TICKS_PER_SECOND;
	Uint64 lastTime = SDL_GetTicks64();
	Uint64 processedTime = lastTime;

	while (keepRunning)
	{
		Vid_HandleEvents();

		Uint64 now = SDL_GetTicks64();
		float deltaTime = (now - lastTime) / 1000.0f;
		lastTime = now;

		while ((processedTime + msPerTick) < now)
		{
			Game_Tick();
			processedTime += msPerTick;
		}
		Game_Update(deltaTime);
		Game_Render(&screen, (now - processedTime) / (float)msPerTick);

		Vid_DrawFrame();
	}

	CleanupAll();
	Bmp_Destroy(&screen);
	Vid_Shutdown();

	return 0;
}

static void InitAll(RPKFile *rpkfile, struct Options *options)
{
	Gfx_Init(rpkfile, &screen);
	Bmp_InitFonts(rpkfile);

	LvlGen_Init(rpkfile);

	Audio_Init(rpkfile, NULL);
	Audio_SetVolume(options->masterVolume, options->sfxVolume, options->musicVolume);
	Audio_SetMusic("rovg");

	VM_Init(rpkfile);
	AI_Init(rpkfile);
	Items_Init(rpkfile);

	Lvl_Setup();

	Inv_InitGfx(rpkfile);
	Game_Init(rpkfile);

	SetupMenus(rpkfile);
}

static void CleanupAll(void)
{
	CleanupMenus();
	Game_Cleanup();
	Inv_CleanupGfx();
	Lvl_Cleanup();
	Items_Cleanup();
	AI_Cleanup();
	VM_Cleanup();
	LvlGen_Cleanup();
	Audio_Cleanup();
	Gfx_Cleanup();
}

void Main_QuitGame(void)
{
	keepRunning = false;
}
