//
//  SuperTuxKart - a fun racing game with go-kart
//  Copyright (C) 2006-2015 SuperTuxKart-Team
//
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; either version 3
//  of the License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

#include "modes/world.h"



#include "../config/user_config.h"
#include "../graphics/irr_driver.h"
#include "../graphics/camera.h"
#include "../io/file_manager.h"
#include "../input/device_manager.h"
#include "../input/keyboard_device.h"




#include "../game/game_manager.h"


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

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

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

#include "../utils/constants.h"
#include "../utils/profiler.h"
#include "../utils/string_utils.h"

#include <algorithm>
#include <assert.h>
#include <ctime>
#include <sstream>
#include <stdexcept>


World* World::m_world = NULL;

/** The main world class is used to handle the track and the karts.
 *  The end of the race is detected in two phases: first the (abstract)
 *  function isRaceOver, which must be implemented by all game modes,
 *  must return true. In which case enterRaceOverState is called. At
 *  this time a winning (or losing) animation can be played. The WorldStatus
 *  class will in its enterRaceOverState switch to DELAY_FINISH_PHASE,
 *  but the remaining AI kart will keep on racing during that time.
 *  After a time period specified in stk_config.xml WorldStatus will
 *  switch to FINISH_PHASE and call terminateRace. Now the finishing status
 *  of all karts is set (i.e. in a normal race the arrival time for karts
 *  will be estimated), highscore is updated, and the race result gui
 *  is being displayed.
 *  Rescuing is handled via the three functions:
 *  getNumberOfRescuePositions() - which returns the number of rescue
 *           positions defined.
 *  getRescuePositionIndex(AbstractKart *kart) - which determines the
 *           index of the rescue position to be used for the given kart.
 *  getRescueTransform(unsigned int index) - which returns the transform
 *           (i.e. position and rotation) for the specified rescue
 *           position.
 *  This allows the world class to do some tests to make sure all rescue
 *  positions are valid (when started with --track-debug). It tries to
 *  place all karts on all rescue positions. If there are any problems
 *  (e.g. a rescue position not over terrain (perhaps because it is too
 *  low); or the rescue position is on a texture which will immediately
 *  trigger another rescue), a warning message will be printed.
 */

//-----------------------------------------------------------------------------
/** Constructor. Note that in the constructor it is not possible to call any
 *  functions that use World::getWorld(), since this is only defined
 *  after the constructor. Those functions must be called in the init()
 *  function, which is called immediately after the constructor.
 */
World::World() : WorldStatus(), m_clear_color(155,50,251,140)
{

#ifdef DEBUG
    m_magic_number = 0xB01D6543;
#endif

    //m_saved_race_gui     = NULL;
    m_map              = NULL;
    m_schedule_pause     = false;
    m_schedule_unpause   = false;
    m_self_destruct      = false;

    WorldStatus::setClockMode(CLOCK_CHRONO);

}   // World

// ----------------------------------------------------------------------------
/** This function is called after instanciating. The code here can't be moved
 *  to the contructor as child classes must be instanciated, otherwise
 *  polymorphism will fail and the results will be incorrect . Also in init()
 *  functions can be called that use World::getWorld().
 */
void World::init()
{


    // Create the race gui before anything else is attached to the scene node
    // (which happens when the track is loaded). This allows the race gui to
    // do any rendering on texture. Note that this function can NOT be called
    // in the World constuctor, since it might be overwritten by a the game
    // mode class, which would not have been constructed at the time that this
    // constructor is called, so the wrong race gui would be created.
    //createGameGUI();

    // Grab the map file
    // error here: map_manager does not exist yet, need to create it later.
    m_map = map_manager->getMap(game_manager->getMapName());
    if(!m_map)
    {
        std::ostringstream msg;
        msg << "Map '" << game_manager->getMapName()
            << "' not found.\n";
        throw std::runtime_error(msg.str());
    }
    //m_map->loadMapModel();
    Camera::createCamera();
    irr_driver->applyObjectPassShader();

    // Must be called after all karts are created
    //m_game_gui->init();

}   // init

//-----------------------------------------------------------------------------
/** This function is called before a race is started (i.e. either after
 *  calling init() when starting a race for the first time, or after
 *  restarting a race, in which case no init() is called.
 */
void World::reset()
{
    // If m_saved_race_gui is set, it means that the restart was done
    // when the race result gui was being shown. In this case restore the
    // race gui (note that the race result gui is cached and so never really
    // destroyed).


    m_schedule_pause = false;
    m_schedule_unpause = false;

    WorldStatus::reset();


    // Note: track reset must be called after all karts exist, since check
    // objects need to allocate data structures depending on the number
    // of karts.
    m_map->reset();

    // Reset the race gui.
    //m_game_gui->reset();





    game_manager->reset();


    // Reset all data structures that depend on number of karts.
    //irr_driver->reset(); not needed yet


}

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

/*
void World::createGameGUI()
{
    //m_game_gui = new GameGUI();
}
*/


//-----------------------------------------------------------------------------
World::~World()
{
    //irr_driver->onUnloadWorld();
    Log::warn("World", "reimplement onUnloadWorld!!!");




    // In case that a race is aborted (e.g. track not found) m_track is 0.
    //if(m_map)
    //    m_map->cleanup();

    // Delete the in-race-gui:
    /*
    if(m_saved_race_gui)
    {
        // If there is a save race gui, this means that the result gui is
        // currently being shown. The race result gui is a screen and so
        // is deleted by the state manager. So we only have to delete
        // the actual race gui:
        delete m_saved_race_gui;
    }

    else
    {
        // No race result gui is shown, so m_race_gui is the in-race
        // gui and this must be deleted.
        delete m_race_gui;
    }
    */







    m_world = NULL;

    irr_driver->getSceneManager()->clear();

#ifdef DEBUG
    m_magic_number = 0xDEADBEEF;
#endif

}   // ~World

//-----------------------------------------------------------------------------
/** Called when 'go' is being displayed for the first time. Here the brakes
 *  of the karts are released.
 */
void World::onGo()
{

}   // onGo

//-----------------------------------------------------------------------------
/** Called at the end of a race. Updates highscores, pauses the game, and
 *  informs the unlock manager about the finished race. This function must
 *  be called after all other stats were updated from the different game
 *  modes.
 */
void World::terminateGame()
{
    Log::warn("[World]", "terminate not fully implemented");
    WorldStatus::terminateRace();
}   // terminateRace





// ----------------------------------------------------------------------------
void World::schedulePause(Phase phase)
{
    if (m_schedule_unpause)
    {
        m_schedule_unpause = false;
    }
    else
    {
        m_schedule_pause = true;
        m_scheduled_pause_phase = phase;
    }
}   // schedulePause

// ----------------------------------------------------------------------------
void World::scheduleUnpause()
{
    if (m_schedule_pause)
    {
        m_schedule_pause = false;
    }
    else
    {
        m_schedule_unpause = true;
    }
}   // scheduleUnpause

//-----------------------------------------------------------------------------
/** This is the main interface to update the world. This function calls
 *  update(), and checks then for the end of the race. Note that race over
 *  handling can not necessarily be done in update(), since not all
 *  data structures might have been updated (e.g.LinearWorld must
 *  call World::update() first, to get updated kart positions. If race
 *  over would be handled in World::update, LinearWorld had no opportunity
 *  to update its data structures before the race is finished).
 *  \param dt Time step size.
 */
void World::updateWorld(float dt)
{
    try
    {
        update(dt);
    }
    catch (AbortWorldUpdateException& e)
    {
        (void)e;   // avoid compiler warning
        return;
    }
}   // updateWorld

#define MEASURE_FPS 0

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



//-----------------------------------------------------------------------------
/** Updates the physics, all karts, the track, and projectile manager.
 *  \param dt Time step size.
 */
void World::update(float dt)
{
#ifdef DEBUG
    assert(m_magic_number == 0xB01D6543);
#endif


    PROFILER_PUSH_CPU_MARKER("World::update()", 0x00, 0x7F, 0x00);

#if MEASURE_FPS
    static float time = 0.0f;
    time += dt;
    if (time > 5.0f)
    {
        time -= 5.0f;
        printf("%i\n",irr_driver->getVideoDriver()->getFPS());
    }
#endif



#ifdef DEBUG
    assert(m_magic_number == 0xB01D6543);
#endif
    WorldStatus::update(dt);
    PROFILER_PUSH_CPU_MARKER("World::update (camera)", 0x60, 0x7F, 0x00);
    for(unsigned int i=0; i<Camera::getNumCameras(); i++)
    {
        Camera::getCamera(i)->update(dt);
    }
    PROFILER_POP_CPU_MARKER();
}   // update

// ----------------------------------------------------------------------------
/** Only updates the track. The order in which the various parts of STK are
 *  updated is quite important (i.e. the track can't be updated as part of
 *  the standard update call):
 *  the track must be updated after updating the karts (otherwise the
 *  checklines would be using the previous kart positions to determine
 *  new laps, but linear world which determines distance along track would
 *  be using the new kart positions --> the lap counting line will be
 *  triggered one frame too late, potentially causing strange behaviour of
 *  the icons.
 *  Similarly linear world must update the position of all karts after all
 *  karts have been updated (i.e. World::update() must be called before
 *  updating the position of the karts). The check manager (which is called
 *  from Track::update()) needs the updated distance along track, so track
 *  update has to be called after updating the race position in linear world.
 *  That's why there is a separate call for trackUpdate here.
 */
void World::updateMap(float dt)
{
    m_map->update(dt);
}   // update Track
// ----------------------------------------------------------------------------







//-----------------------------------------------------------------------------
/** Pauses the music (and then pauses WorldStatus).
 */
void World::pause(Phase phase)
{
    // doing nothing yet

}   // pause

//-----------------------------------------------------------------------------
void World::unpause()
{
    // doing nothing yet

}   // pause

//-----------------------------------------------------------------------------
/** Call when the world needs to be deleted but you can't do it immediately
 * because you are e.g. within World::update()
 */
void World::delayedSelfDestruct()
{
    m_self_destruct = true;
}

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

void World::escapePressed()
{
    // doing nothing yet
}


/* EOF */
