#include <cstdlib>
#include <iostream>

#include "Engine.h"

#include "Controller.h"
#include "View.h"
#include "Model.h"
#include "Physics.h"
#include "EntityFactory.h"
#include "Game.h"

#include <boost/filesystem.hpp>

#ifdef WIN32
#include <Windows.h>
#endif

using namespace std;

/* Returns a path where files such as logs and config files can be
 * written to
 */
std::string create_user_path () {
	std::string result_dir = ".";
	std::string test_file_path = result_dir;

	// first we check in $HOME/.fysxasteroids
	char* env_home_dir = getenv("HOME");
	result_dir = env_home_dir;
	result_dir += "/.fysxasteroids";

	boost::filesystem::path result_dir_path(result_dir);
	if(!boost::filesystem::is_directory (result_dir_path)) {
		if (!boost::filesystem::create_directory(result_dir_path)) {
			cerr << "Warning: could not create user data directory " << result_dir<< endl;
			result_dir = "";
		}
	}

	test_file_path = result_dir;
	test_file_path += "/game.log";
	ofstream test_file (test_file_path.c_str(), ios_base::app);
	if (!test_file) {
		test_file.close();
		cerr << "Warning: user data directory not writable! " << result_dir << endl;
		result_dir = "";
	} else {
		test_file.close();
		return result_dir;
	}

	// then we check the local directory
	result_dir = ".";
	test_file_path = result_dir;
	test_file_path += "/game.log";
	test_file.open (test_file_path.c_str(), ios_base::out);
	if (test_file) {
		test_file.close();
		return result_dir;
	} else {
		cerr << "Warning could not find suitable user data directory" << endl;
		result_dir = "";
	}
	test_file.close();

	return result_dir;
}

std::string find_game_data_dir () {
	std::string result;

	std::vector<std::string> paths;
	paths.push_back(".");
	paths.push_back("/usr/local/share/fysxasteroids");
	paths.push_back("/usr/share/fysxasteroids");

	std::vector<std::string>::iterator iter = paths.begin();
	for (iter; iter != paths.end(); iter++) {
		std::string test_path = *iter;
		
		if (!boost::filesystem::is_directory(test_path + "/data/fonts"))
			continue;
		if (!boost::filesystem::is_directory(test_path + "/data/levels"))
			continue;
		if (!boost::filesystem::is_directory(test_path + "/data/sounds"))
			continue;
		if (!boost::filesystem::is_directory(test_path + "/data/textures"))
			continue;

		break;
	}

	if (iter != paths.end())
		return *iter;

	cerr << "Could not find game data" << endl;
	return result;
}

int main (int argc, char* argv[]) {
	cout << "Game Start" << endl;

#ifdef WIN32
	HWND hWnd = GetConsoleWindow();
	ShowWindow( hWnd, SW_HIDE );
#endif

	Engine::Engine engine;

	engine.SetEntityFactory (new asteroids::EntityFactory);
	engine.SetController (new asteroids::Controller);
	engine.SetModel (new asteroids::Model);
	engine.SetPhysics (new asteroids::Physics);
	engine.SetView (new asteroids::View);

	SetLogPrintLevel (Engine::LogLevelWarning);

	std::string user_path = "";
	std::string game_data_path = "";

#ifndef WIN32
	user_path = create_user_path();
	game_data_path = find_game_data_dir();
#endif

	boost::filesystem::path cpath = boost::filesystem::initial_path<boost::filesystem::path>();
	std::cerr << "cwd: " << cpath.string() << std::endl;

	// we assume the user path to be local folder
	std::string log_file_path = user_path;
	log_file_path += "/game.log";

	user_path = boost::filesystem::path(cpath.string() + user_path).file_string();
	game_data_path = boost::filesystem::path(cpath.string() + game_data_path).file_string();
	log_file_path = boost::filesystem::path (cpath.string() + log_file_path).file_string();

	cout << "User Data Dir = " << user_path << endl;
	engine.SetUserDataPath (user_path);

	Engine::SetLogFilename (log_file_path);

	engine.SetGameDataPath (game_data_path);
	cout << "Game Data Dir = " << game_data_path << endl;

 	if (engine.Init (argc, argv) != 0) {
		cout << "Could not start engine!" << endl;
		exit (-1);
	}

	// Load the icon
	Uint32          colorkey;
	SDL_Surface     *image = NULL;
	image = SDL_LoadBMP(Engine::GetResourceFullPath("/data/textures/icon.bmp").c_str());
	if (!image)
		Engine::LogWarning("Could not load icon: data/textures/icon.bmp");
	else
		SDL_WM_SetIcon(image,NULL);

	SDL_WM_SetCaption("fysxasteroids -RC2-","fysxasteroids -RC2-");

	engine.GetView()->SetGridSize (8,8);

	/// \todo get rid of the world settings in asteroids
	dynamic_cast<asteroids::Physics*>(engine.GetPhysics())->SetWorldSize (26, 20);
	engine.GetPhysics()->SetWorldBounds (vector3d (-13, 0, -10), vector3d (13, 0, 10));
	engine.GetPhysics()->EnableWorldWarp(Engine::PhysicsBase::WorldWarpModeX);
	engine.GetPhysics()->EnableWorldWarp(Engine::PhysicsBase::WorldWarpModeZ);

	// run the default commands and load the configuration
	Engine::RunCommand ("exec asteroids.rc");
	Engine::RunCommand ("exec config.rc");

	Engine::PlayMusic (Engine::GetResourceFullPath("/data/sounds/intro_music.ogg"));
	Engine::LoadSound(Engine::GetResourceFullPath("/data/sounds/thrust.wav"));
	Engine::LoadSound(Engine::GetResourceFullPath("/data/sounds/ship_destroyed.wav"));
	Engine::LoadSound(Engine::GetResourceFullPath("/data/sounds/rock_destroyed.wav"));

	// load highscore list (either remote or local, depending on settings)
	asteroids::GetModel()->LoadHighscoreList();

	engine.MainLoop ();

	// save the configuration
	std::ofstream config_file (engine.GetUserDirFullPath("/config.rc").c_str());
	config_file << "set effects_volume " << Engine::GetEffectsVolume() << std::endl;
	config_file << "set music_volume " << Engine::GetMusicVolume() << std::endl;
	config_file << "set use_server_highscore " << Engine::GetVariableString("use_server_highscore") << std::endl;
	config_file << "set use_server_highscore_asked " << Engine::GetVariableString ("use_server_highscore_asked") << std::endl;

	if (Engine::GetVariableBool("cheat_mode"))
		config_file << "set cheat_mode " << true << std::endl;
	config_file.close();

	SDL_WM_SetIcon(NULL,NULL);
	SDL_FreeSurface (image);

	engine.Destroy ();

	cout << "Game Quit" << endl;

	return 0;
}
