text: #include <iostream> 
  
  #include "p2Defs.h"
  #include "p2Log.h"
  
  #include "j1Window.h"
  #include "j1Input.h"
  #include "j1Render.h"
  #include "j1Textures.h"
  #include "j1Audio.h"
  #include "j1Scene.h"
  #include "j1Map.h"
  #include "j1App.h"
  #include "j1FadeToBlack.h"
  #include "j1Collisions.h"
  #include "j1Scene2.h"
  #include "j1EntityManager.h"
  #include "j1Pathfinding.h"
  #include "Brofiler\Brofiler.h"
  #include "j1Player.h"
  #include "j1Fonts.h"
  #include "j1Gui.h"
  #include "j1SceneIntro.h"
  
  // Constructor
  j1App::j1App(int argc, char* args[]) : argc(argc), args(args)
  {
  	PERF_START(ptimer);
  
  	want_to_save = want_to_load = false;
  
  	input = new j1Input();
  	win = new j1Window();
  	tex = new j1Textures();
  	audio = new j1Audio();
  	path = new j1PathFinding();
  	map = new j1Map();
  	scene = new j1Scene();
  	scene2 = new j1Scene2();
  	sceneintro = new j1SceneIntro();
  	collisions = new j1Collisions();
  	entitymanager = new j1EntityManager();
  	render = new j1Render();
  	gui = new j1Gui();
  	font = new j1Fonts();
  	fade = new j1FadeToBlack();
  
  
  	// Ordered for awake / Start / Update
  	// Reverse order of CleanUp
  	AddModule(input);
  	AddModule(win);
  	AddModule(tex);
  	AddModule(audio);
  	AddModule(map);
  	AddModule(path);
  	AddModule(scene);
  	AddModule(scene2);
  	AddModule(sceneintro);
  	AddModule(collisions);
  	AddModule(entitymanager);
  	AddModule(gui);
  	AddModule(font);
  	AddModule(fade);
  
  
  	// render last to swap buffer
  	AddModule(render);
  
  	PERF_PEEK(ptimer);
  }
  
  // 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_document	config_file;
  	pugi::xml_node		config;
  	pugi::xml_node		app_config;
  
  	bool ret = false; 
  	save_game = "save_game.xml";
  	load_game = "save_game.xml";
  
  	config = LoadConfig(config_file);
  
  	if(config.empty() == false)
  	{
  		// self-config
  		ret = true;
  		app_config = config.child("app");
  		title.create(app_config.child("title").child_value());
  		organization.create(app_config.child("organization").child_value());
  		// TODO 1: Read from config file your framerate cap
  		frame_cap = config.child("app").attribute("framerate_cap").as_int(-1);
  		if (frame_cap > 0)
  		{
  			capped_ms = 1000 / frame_cap;
  		}
  	}
  
  	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;
  		}
  	}
  	PERF_PEEK(ptimer);
  
  	return ret;
  }
  
  // Called before the first frame
  bool j1App::Start()
  {
  	//DISABLE MODULES YOU DON'T WANT
  	scene->Disable();
  	scene2->Disable();
  
  	PERF_START(ptimer);
  
  	bool ret = true;
  	p2List_item<j1Module*>* item;
  	item = modules.start;
  
  	while(item != NULL && ret == true)
  	{
  		if(item->data->active)
  			ret = item->data->Start();
  		item = item->next;
  	}
  
  	PERF_PEEK(ptimer);
  
  	return ret;
  }
  
  // Called each loop iteration
  bool j1App::Update()
  {
  	BROFILER_CATEGORY("Update", Profiler::Color::Green)
  
  	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++;
  
  	ptimer.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;
  	}
  
  	avg_fps = float(frame_count) / startup_time.ReadSec();
  	seconds_since_startup = startup_time.ReadSec();
  	uint32 current_ms_frame = ptimer.ReadMs();
  	last_frame_ms = current_ms_frame;
  
  	if (App->input->GetKey(SDL_SCANCODE_F11) == KEY_DOWN)
  		cap_frames = !cap_frames;
  
  	if (cap_frames) {
  
  		if (capped_ms > 0 && last_frame_ms < capped_ms) {
  			j1PerfTimer t;
   			SDL_Delay(capped_ms - last_frame_ms);
  			//LOG("We waited for %d milliseconds and got back in %f", capped_ms - last_frame_ms, t.ReadMs());
  		}
  	}
  
  	float framerate = 1000.0f / ptimer.ReadMs();
  	dt = 1.0f / framerate;
  	if (App->entitymanager->GetPlayer() != nullptr) {
  		p2SString title("FPS: %i Av.FPS: %.2f Last Frame Ms: %02u Cap: %i VSync %i - Tails Odyssey Map:%dx%d - GODMODE:%i",
  			prev_last_sec_frame_count, avg_fps, last_frame_ms, cap_frames, App->render->vsync, App->map->data.width, App->map->data.height, App->entitymanager->GetPlayer()->godmode);
  		App->win->SetTitle(title.GetString());
  	}
  	else {
  		p2SString title("FPS: %i Av.FPS: %.2f L.Frame Ms: %02u Cap: %i VSync %i - Tails Odyssey Map:%dx%d - GODMODE:%i",
  			prev_last_sec_frame_count, avg_fps, last_frame_ms, cap_frames, App->render->vsync, App->map->data.width, App->map->data.height, 0);
  		App->win->SetTitle(title.GetString());
  	}
  		
  }
  
  // Call modules before each loop iteration
  bool j1App::PreUpdate()
  {
  	BROFILER_CATEGORY("Preupdate", Profiler::Color::Orchid);
  
  	if (gui->b_settings)
  		dt = 0;
  	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(dt);
  	}
  
  	return ret;
  }
  
  // Call modules after each loop iteration
  bool j1App::PostUpdate()
  {
  	BROFILER_CATEGORY("PostUpdate", Profiler::Color::Blue)
  	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()
  {
  	bool ret = true;
  	p2List_item<j1Module*>* item;
  	item = modules.end;
  
  	while(item != NULL && ret == true)
  	{
  		ret = item->data->CleanUp();
  		item = item->prev;
  	}
  
  	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()
  {
  	// we should be checking if that file actually exist
  	// from the "GetSaveGames" list
  	want_to_load = true;
  }
  
  // ---------------------------------------
  void j1App::SaveGame() const
  {
  	// we should be checking if that file actually exist
  	// from the "GetSaveGames" list ... should we overwrite ?
  
  	want_to_save = true;
  }
  
  // ---------------------------------------
  void j1App::GetSaveGames(p2List<p2SString>& list_to_fill) const
  {
  	// need to add functionality to file_system module for this to work
  }
  
  pugi::xml_node j1App::GetSaveData() {
  	pugi::xml_node root;
  
  	pugi::xml_parse_result result = save_gamedata.load_file(load_game.GetString());
  
  	if (result != NULL)
  	{
  		LOG("Loading new Game State from %s...", load_game.GetString());
  
  		root = save_gamedata.child("game_state");
  		return root;
  	}
  	else
  		LOG("Could not parse game state xml file %s. pugi error: %s", load_game.GetString(), result.description());
  	return root;
  }
  
  bool j1App::LoadGameNow()
  {
  	BROFILER_CATEGORY("LoadGame", Profiler::Color::Orange)
  	bool ret = false;
  
  	pugi::xml_node root = GetSaveData();
  
  	if(root != NULL)
  	{
  		LOG("Loading new Game State from %s...", load_game.GetString());
  
  		p2List_item<j1Module*>* item = modules.start;
  		ret = true;
  
  		while(item != NULL && ret == true)
  		{
  			if (item->data->IsEnabled())
  				ret = item->data->Load(root.child(item->data->name.GetString()));
  			item = item->next;
  		}
  
  		save_gamedata.reset();
  		if(ret == true)
  			LOG("...finished loading");
  		else
  			LOG("...loading process interrupted with error on module %s", (item != NULL) ? item->data->name.GetString() : "unknown");
  	}
  
  	want_to_load = false;
  	return ret;
  }
  
  bool j1App::SavegameNow() const
  {
  	BROFILER_CATEGORY("SaveGame", Profiler::Color::Purple)
  	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)
  	{
  		if(item->data->IsEnabled())
  			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;
  }
  
  void j1App::calcTime(uint& timer) {
  	int i = (int)p_timer.ReadMs() / 1000;
  	if (i == 1) {
  		p_timer.Start();
  		if(!gui->b_settings)
  			timer += 1;
  	}
  }
  
  void j1App::setTime(uint& timer) {
  	b_timer = timer;
  }
  
  void j1App::getTime(uint& timer) {
  	timer = b_timer;
  }
  
  void j1App::setScore(uint& timer) {
  	b_score = timer;
  }
  
  void j1App::getScore(uint& score) {
  	score = b_score;
  }