#include <irrlicht.h>
#include <iostream>
#include <string>
#include <vector>

#include "main_loop.h"

#include "config/stk_config.h"
#include "config/user_config.h"

#include "graphics/central_settings.h"
#include "graphics/graphics_restrictions.h"
#include "graphics/irr_driver.h"
#include "graphics/material_manager.h"

#include "guiengine/dialog_queue.h"
#include "guiengine/engine.h"

#include "input/device_manager.h"
#include "input/input_manager.h"
#include "input/keyboard_device.h"

#include "io/file_manager.h"
#include "io/xml_node.h"

#include "states_screens/main_menu_screen.h"
#include "states_screens/state_manager.h"

#include "utils/command_line.h"
#include "utils/crash_reporting.h"
#include "utils/log.h"

#include "maps/map.h"
#include "maps/map_manager.h"
#include "game/game_manager.h"






/*



*/





static void cleanLaundredSheep();
static void cleanUserConfig();

using namespace irr;

//=============================================================================

void cmdLineHelp()
{
    fprintf(stdout,
    "Usage: %s [OPTIONS]\n\n"
    "Run LaundredSheep, a platformer game with frank from project gooseberry.\n\n"
    "Options:\n"

    "  -t,  --map=NAME         Start at map NAME, not implemented yet.\n"
    "  -f,  --fullscreen       Select fullscreen display.\n"
    "  -w,  --windowed         Windowed display (default).\n"
    "  -s,  --screensize=WxH   Set the screen size (e.g. 320x200).\n"
    "  -v,  --version          Show version of SuperTuxKart.\n"
    "       --no-graphics      Do not display the actual race.\n"

    "       --no-console       Does not write messages in the console but to\n"
    "                          stdout.log.\n"
    "       --console          Write messages in the console and files\n"
    "  -h,  --help             Show this help.\n"
    "\n",
    CommandLine::getExecName().c_str()
    );
}   // cmdLineHelp

//=============================================================================

int handleCmdLineOutputModifier()
{
    if (CommandLine::has("--help") || CommandLine::has("-help") ||
        CommandLine::has("-h"))
    {
        cmdLineHelp();
        cleanUserConfig();
        exit(0);
    }

    if(CommandLine::has("--version") || CommandLine::has("-v"))
    {
        Log::info("main", "==============================");

        //Log::info("main", "LaundredSheep, %s.", LS_VERSION ) ;
        //to do: create version

        // IRRLICHT_VERSION_SVN
        Log::info("main", "Irrlicht version %i.%i.%i (%s)",
                          IRRLICHT_VERSION_MAJOR , IRRLICHT_VERSION_MINOR,
                          IRRLICHT_VERSION_REVISION, IRRLICHT_SDK_VERSION );
        Log::info("main", "==============================");
        cleanUserConfig();
        exit(0);
    }

    int n;
    if(CommandLine::has("--log", &n))
        Log::setLogLevel(n);

    if(CommandLine::has("--log=nocolor"))
    {
        Log::disableColor();
        Log::verbose("main", "Colours disabled.");
    }

    if(CommandLine::has("--console"))
        UserConfigParams::m_log_errors_to_console=true;
    if(CommandLine::has("--no-console"))
        UserConfigParams::m_log_errors_to_console=false;


    return 0;
}

//=============================================================================
/** For base options that don't need much to be inited (and, in some cases,
 *  that need to be read before initing stuff) - it only assumes that
 *  user config is loaded (necessary to check for blacklisted screen
 *  resolutions), but nothing else (esp. not kart_properties_manager and
 *  track_manager, since their search path might be extended by command
 *  line options).
 */
int handleCmdLinePreliminary()
{

    if(CommandLine::has("--debug=memory"))
        UserConfigParams::m_verbosity |= UserConfigParams::LOG_MEMORY;
    if(CommandLine::has("--debug=mgui"))
        UserConfigParams::m_verbosity |= UserConfigParams::LOG_GUI;
    if(CommandLine::has("--debug=mist"))
        UserConfigParams::m_verbosity |= UserConfigParams::LOG_MISC;
    if(CommandLine::has("--debug=all") )
        UserConfigParams::m_verbosity |= UserConfigParams::LOG_ALL;

    std::string s;
    if(CommandLine::has("--ls-config", &s))
    {
        ls_config->load(file_manager->getAsset(s));
        Log::info("main", "STK config will be read from %s.",s.c_str());
    }

    if(CommandLine::has("--no-graphics") || CommandLine::has("-l"))
    {
        UserConfigParams::m_log_errors_to_console=true;
    }

    if(CommandLine::has("--screensize", &s) || CommandLine::has("-s", &s))
    {
        //Check if fullscreen and new res is blacklisted
        int width, height;
        if (sscanf(s.c_str(), "%dx%d", &width, &height) == 2)
        {
            // Reassemble the string in case that the original width or
            // height contained a leading 0
            std::ostringstream o;
            o << width << "x" << height;
            std::string res = o.str();

        }
        else
        {
            Log::fatal("main", "Error: --screensize argument must be "
                "given as WIDTHxHEIGHT");
        }
    }
    return 0;
}   // handleCmdLinePreliminary



//=============================================================================
/** Initialises the minimum number of managers to get access to user_config.
 */
void initUserConfig()
{
    file_manager = new FileManager();
    user_config             = new UserConfig();     // needs file_manager
    user_config->loadConfig();
    // Some parts of the file manager needs user config (paths for models
    // depend on artist debug flag). So init the rest of the file manager
    // after reading the user config file.
    file_manager->init();
    ls_config              = new LSConfig();      // in case of --stk-config

}   // initUserConfig



//----------------------------------------------------------------------------------------------------

void initRest()
{

    ls_config->load(file_manager->getAsset("ls_config.xml"));


    irr_driver            = new IrrDriver();
    StkTime::init();

    irr_driver->initDevice();

    IrrlichtDevice* device = irr_driver->getDevice();

    video::IVideoDriver* driver = device->getVideoDriver();



    GUIEngine::init(device, driver, StateManager::get());

    material_manager        = new MaterialManager      ();
    map_manager = new MapManager();





    irr_driver->setMaxTextureSize();

    map_manager->loadMapList(); //problem here: no map file path!

    GUIEngine::addLoadingIcon(irr_driver->getTexture(FileManager::GUI,
                                                     "unknown.png"      ) );

    game_manager = new GameManager();

}


// ----------------------------------------------------------------------------
int main(int argc, char *argv[] )
{
    CommandLine::init(argc, argv);

    CrashReporting::installHandlers();

    srand(( unsigned ) time( 0 ));

    try
    {
        std::string s;

        handleCmdLineOutputModifier();

        if(CommandLine::has("--root", &s))
        {
            FileManager::addRootDirs(s);
        }

        // Init the minimum managers so that user config exists, then
        // handle all command line options that do not need (or must
        // not have) other managers initialised:
        initUserConfig();

        handleCmdLinePreliminary();

        initRest();

        input_manager = new InputManager ();



        // Get into menu mode initially.

        input_manager->setMode(InputManager::MENU);
        main_loop = new MainLoop();
        material_manager->loadMaterial();

        // Load the font textures - they are all lazily loaded
        // so no need to push a texture search path. They will actually
        // be loaded from ScalableFont.
        file_manager->pushTextureSearchPath(file_manager->getAsset(FileManager::FONT, ""));
        material_manager->addSharedMaterial(
                   file_manager->getAsset(FileManager::FONT,"materials.xml"));
        file_manager->popTextureSearchPath();








        //if(materials_file!="")
        //{
            // Some of the materials might be needed later, so just add
            // them all permanently (i.e. as shared). Adding them temporary
            // will actually not be possible: powerup_manager adds some
            // permanent icon materials, which would (with the current
            // implementation) make the temporary materials permanent anyway.
        //    material_manager->addSharedMaterial(materials_file);
        //}




        file_manager->popTextureSearchPath();



        //if(!handleCmdLine()) exit(0);

        MainMenuScreen::getInstance()->push();

        main_loop->run();

    }  // try
    catch (std::exception &e)
    {
        Log::error("main", "Exception caught : %s.",e.what());
        Log::error("main", "Aborting Laundred Sheep.");
    }

    /* Program closing...*/


    // If the window was closed in the middle of a race, remove players,
    // so we don't crash later when StateManager tries to access input devices.

    if(input_manager) delete input_manager; // if early crash avoid delete NULL
    Log::info("main", "before clean");
    cleanLaundredSheep();
    Log::info("main", "after clean");


#ifdef DEBUG
    MemoryLeaks::checkForLeaks();
#endif

#ifndef WIN32
    if (user_config) //close logfiles
    {
        Log::closeOutputFiles();
#endif
        fclose(stderr);
        fclose(stdout);
#ifndef WIN32
    }
#endif

//    Log::info("main", "before delete file_manager");
    delete file_manager;
//    Log::info("main", "after delete file_manager");


    return 0 ;
}   // main

// ============================================================================
#ifdef WIN32
//routine for running under windows
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     LPTSTR lpCmdLine, int nCmdShow)
{
    return main(__argc, __argv);
}
#endif


//=============================================================================
/** Frees all manager and their associated memory.
 */

static void cleanLaundredSheep()
{

    delete main_loop;

    irr_driver->updateConfigIfRelevant();


    if(material_manager)        delete material_manager;

    GUIEngine::DialogQueue::deallocate();

    // Now finish shutting down objects which a separate thread. The
    // RequestManager has been signaled to shut down as early as possible,
    // the NewsManager thread should have finished quite early on anyway.
    // But still give them some additional time to finish. It avoids a
    // race condition where a thread might access the file manager after it
    // was deleted (in cleanUserConfig below), but before STK finishes and
    // the os takes all threads down.



    cleanUserConfig();

    StateManager::deallocate();
    GUIEngine::EventHandler::deallocate();

}   // cleanSuperTuxKart

//=============================================================================

//=============================================================================
/**
 * Frees all the memory of initUserConfig()
 */
static void cleanUserConfig()
{
    if(ls_config)              delete ls_config;
    if (user_config)
    {
        // In case that abort is triggered before user_config exists
        if (UserConfigParams::m_crashed) UserConfigParams::m_crashed = false;
        user_config->saveConfig();
        delete user_config;
    }

    if(irr_driver)              delete irr_driver;
}   // cleanUserConfig

//----------------------------------------------------------------------------------------------------

//http://stackoverflow.com/questions/20007961/error-running-a-compiled-c-file-uses-opengl-error-inconsistency-detected
#include <pthread.h>
void junk() {
  int i;
  i=pthread_getconcurrency();
};

