text: #include <iostream> 
  #include <sstream> 
  
  #include "p2Defs.h"
  #include "p2Log.h"
  #include "brofiler/Brofiler.h"
  
  #include "j1Window.h"
  #include "j1Input.h"
  #include "j1Render.h"
  #include "j1Textures.h"
  #include "j1Audio.h"
  #include "j1Scene.h"
  #include "j1Map.h"
  #include "EntityManager.h"
  #include "Collision_Manager.h"
  #include "j1Pathfinding.h"
  #include "j1Gui.h"
  #include "j1Fonts.h"
  #include "Console.h"
  #include "j1App.h"
  
  // Constructor
  j1App::j1App(int argc, char* args[]) : argc(argc), args(args)
  {
  	PERF_START(ptimer);
  	frames = 0;
  	want_to_save = want_to_load = false;
  	colliders = new Collider_Manager();
  
  	input = new j1Input();
  	win = new j1Window();
  	render = new j1Render();
  	tex = new j1Textures();
  	audio = new j1Audio();
  	scene = new j1Scene();
  	map = new j1Map();
  	entity_manager = new EntityManager;
  	pathfinding = new j1PathFinding();
  	font = new j1Fonts();
  	gui = new j1Gui();
  	console = new Console();
  
  	// Ordered for awake / Start / Update
  	// Reverse order of CleanUp
  	AddModule(input);
  	AddModule(win);
  	AddModule(tex);
  	AddModule(audio);
  	AddModule(map);
  	AddModule(pathfinding);
  	AddModule(gui);
  	AddModule(console);
  	AddModule(scene);
  	AddModule(entity_manager);
  	AddModule(font);
  
  	// render last to swap buffer
  	AddModule(render);
  
  	map->active = false;
  	entity_manager->active = false;
  	
  
  	PERF_PEEK(ptimer);
  }
  
  double j1App::GetDT() {
  	if (dt != 0)
  		return dt;
  	else
  		return dt_when_paused;
  }
  
  // Destructor
  j1App::~j1App()
  {
  	// release modules
  	p2List_item<j1Module*>* item = modules.end;
  
  	while(item != NULL)
  	{
  		RELEASE(item->data);
  		item = item->prev;
  	}
  
  	modules.clear();
  }
  
  void j1App::AddModule(j1Module* module)
  {
  	module->Init();
  	modules.add(module);
  }
  
  // Called before render is available
  bool j1App::Awake()
  {
  	PERF_START(ptimer);
  
  	pugi::xml_node		config;
  	pugi::xml_node		app_config;
  
  	bool ret = false;
  
  	pugi::xml_document	config_file;
  	config = LoadConfig(config_file);
  
  	//Window name setup
  	if(config.empty() == false)
  	{
  		ret = true;
  		app_config = config.child("app");
  		title.create(app_config.child("title").child_value());
  		organization.create(app_config.child("organization").child_value());
  		fps_max = app_config.child("max_fps").first_attribute().as_uint();
  		fps_min = app_config.child("min_fps_cap").first_attribute().as_uint();
  		fps_limit = fps_min;
  	}
  
  	if(ret == true)
  	{
  		p2List_item<j1Module*>* item;
  		item = modules.start;
  
  		while(item != NULL && ret == true)
  		{
  			ret = item->data->Awake(config.child(item->data->name.GetString()));
  			item = item->next;
  		}
  	}
  	load_game = save_game = app_config.child("save_game_source").child_value();
  
  	pugi::xml_document result;
  	if (result.load_file(load_game.GetString()))
  	{
  		existSaveFile = true;
  	}
  
  	PERF_PEEK(ptimer);
  
  	return ret;
  }
  
  // Called before the first frame
  bool j1App::Start()
  {
  
  	PERF_START(ptimer);
  
  	bool ret = true;
  	p2List_item<j1Module*>* item;
  	item = modules.start;
  
  	while(item != NULL && ret == true)
  	{
  		ret = item->data->Start();
  		item = item->next;
  	}
  
  	startup_time.Start();
  
  	PERF_PEEK(ptimer);
  
  	return ret;
  }
  
  // Called each loop iteration
  bool j1App::Update()
  { BROFILER_CATEGORY("Update", Profiler::Color::Orchid)
  	bool ret = true;
  	PrepareUpdate();
  
  	if(input->GetWindowEvent(WE_QUIT) == true)
  		ret = false;
  
  	if(ret == true)
  		ret = PreUpdate();
  
  	if(ret == true)
  		ret = DoUpdate();
  
  	if(ret == true)
  		ret = PostUpdate();
  
  	FinishUpdate();
  	return ret;
  }
  
  // ---------------------------------------------
  pugi::xml_node j1App::LoadConfig(pugi::xml_document& config_file) const
  {
  	pugi::xml_node ret;
  
  	pugi::xml_parse_result result = config_file.load_file("config.xml");
  
  	if(result == NULL)
  		LOG("Could not load map xml file config.xml. pugi error: %s", result.description());
  	else
  		ret = config_file.child("config");
  
  	return ret;
  }
  
  // ---------------------------------------------
  void j1App::PrepareUpdate()
  {
  	frame_count++;
  	last_sec_frame_count++;
  
  	dt = frame_time.ReadSec();
  
  	if (dt >= 1.0f) 
  	{
  		dt = 1.0f;
  	}
  	if (stop_game == true) {
  		dt_when_paused = dt;
  		dt = 0.0f;
  	}
  
  	frame_time.Start();
  }
  
  // ---------------------------------------------
  void j1App::FinishUpdate()
  {
  	if(want_to_save == true)
  		SavegameNow();
  
  	if(want_to_load == true)
  		LoadGameNow();
  
  	// Framerate calculations --
  	if (last_sec_frame_time.Read() > 1000)
  	{
  		last_sec_frame_time.Start();
  		prev_last_sec_frame_count = last_sec_frame_count;
  		last_sec_frame_count = 0;
  	}
  
  	float avg_fps = float(frame_count) / startup_time.ReadSec();
  	float seconds_since_startup = startup_time.ReadSec();
  	uint32 last_frame_ms = frame_time.Read();
  	uint32 frames_on_last_update = prev_last_sec_frame_count;
  
  	static char title[256];
  	//sprintf_s(title, 256, "Av.FPS: %.2f Last Frame Ms: %02u Last sec frames: %i  Time since startup: %.3f Frame dt: %f ",
  	//	avg_fps, last_frame_ms, frames_on_last_update, seconds_since_startup, dt);
  
  	p2SString cap = "";
  	if (fps_limit == fps_min) 
  	{
  		cap = "ON";
  	}
  	else
  	{
  		cap = "OFF";
  	}
  
  	sprintf_s(title, 256, "FPS %i / average FPS %.2f / MS of the last frame %02u (Cap %s + Vsync %s)",
  		frames_on_last_update, avg_fps, last_frame_ms, cap.GetString(), App->render->vsync.GetString());
  	App->win->SetTitle(title);
  
  	//LOG("%u", last_frame_ms);
  	float wait_time = (1000.f / (float)fps_limit) - (float)last_frame_ms;
  	SDL_Delay(fabs(wait_time));
  
  }
  
  // Call modules before each loop iteration
  bool j1App::PreUpdate()
  {BROFILER_CATEGORY("PreUpdate", Profiler::Color::Black)
  	bool ret = true;
  	p2List_item<j1Module*>* item;
  	item = modules.start;
  	j1Module* pModule = NULL;
  
  	for(item = modules.start; item != NULL && ret == true; item = item->next)
  	{
  		pModule = item->data;
  
  		if(pModule->active == false) {
  			continue;
  		}
  
  		ret = item->data->PreUpdate();
  	}
  
  	return ret;
  }
  
  // Call modules on each loop iteration
  bool j1App::DoUpdate()
  {
  	bool ret = true;
  	p2List_item<j1Module*>* item;
  	item = modules.start;
  	j1Module* pModule = NULL;
  
  	for(item = modules.start; item != NULL && ret == true; item = item->next)
  	{
  		pModule = item->data;
  
  		if(pModule->active == false) {
  			continue;
  		}
  
  		ret = item->data->Update((float)dt);
  	}
  
  	return ret;
  }
  
  // Call modules after each loop iteration
  bool j1App::PostUpdate()
  {BROFILER_CATEGORY("PostUpdate", Profiler::Color::Red)
  	bool ret = true;
  	p2List_item<j1Module*>* item;
  	j1Module* pModule = NULL;
  
  	for(item = modules.start; item != NULL && ret == true; item = item->next)
  	{
  		pModule = item->data;
  
  		if(pModule->active == false) {
  			continue;
  		}
  
  		ret = item->data->PostUpdate();
  	}
  
  	return ret;
  }
  
  // Called before quitting
  bool j1App::CleanUp()
  {
  	PERF_START(ptimer);
  	delete colliders;
  	bool ret = true;
  	p2List_item<j1Module*>* item;
  	item = modules.end;
  
  	while(item != NULL && ret == true)
  	{
  		ret = item->data->CleanUp();
  		item = item->prev;
  	}
  	PERF_PEEK(ptimer);
  	return ret;
  }
  
  // ---------------------------------------
  int j1App::GetArgc() const
  {
  	return argc;
  }
  
  // ---------------------------------------
  const char* j1App::GetArgv(int index) const
  {
  	if(index < argc)
  		return args[index];
  	else
  		return NULL;
  }
  
  // ---------------------------------------
  const char* j1App::GetTitle() const
  {
  	return title.GetString();
  }
  
  // ---------------------------------------
  const char* j1App::GetOrganization() const
  {
  	return organization.GetString();
  }
  
  // Load / Save
  void j1App::LoadGame()
  {
  	if (scene->isMainMenu) 
  	{
  		scene->isMainMenu = false;
  		App->gui->ClearUI();
  		App->gui->ReturnConsole();
  	}
  	want_to_load = true;
  }
  
  // ---------------------------------------
  void j1App::SaveGame() const
  {
  
  	want_to_save = true;
  }
  
  // ---------------------------------------
  
  bool j1App::LoadGameNow()
  {
  	bool ret = false;
  
  	pugi::xml_document data;
  	pugi::xml_node root;
  
  	pugi::xml_parse_result result = data.load_file(load_game.GetString());
  
  	if(result != NULL)
  	{
  		LOG("Loading new Game State from %s...", load_game.GetString());
  
  		root = data.child("game_state");
  
  		p2List_item<j1Module*>* item = modules.start;
  		ret = true;
  
  		while(item != NULL && ret == true)
  		{
  			ret = item->data->Load(root.child(item->data->name.GetString()));
  			item = item->next;
  		}
  
  		data.reset();
  		if(ret == true)
  			LOG("...finished loading");
  		else
  			LOG("...loading process interrupted with error on module %s", (item != NULL) ? item->data->name.GetString() : "unknown");
  	}
  	else
  		LOG("Could not parse game state xml file %s. pugi error: %s", load_game.GetString(), result.description());
  
  	want_to_load = false;
  	return ret;
  }
  
  bool j1App::SavegameNow() const
  {
  	bool ret = true;
  	LOG("Saving Game State to %s...", save_game.GetString());
  
  	// xml object were we will store all data
  	pugi::xml_document data;
  	pugi::xml_node root;
  
  	root = data.append_child("game_state");
  
  	p2List_item<j1Module*>* item = modules.start;
  
  	while(item != NULL && ret == true)
  	{
  		ret = item->data->Save(root.append_child(item->data->name.GetString()));
  		item = item->next;
  	}
  
  	if(ret == true)
  	{
  		data.save_file(save_game.GetString());
  		LOG("... finished saving", );
  	}
  	else
  		LOG("Save process halted from an error in module %s", (item != NULL) ? item->data->name.GetString() : "unknown");
  
  	data.reset();
  	want_to_save = false;
  	return ret;
  }