//
//  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 "game_manager.h"

#include <iostream>
#include <algorithm>


#include "../config/stk_config.h"
#include "../config/user_config.h"
#include "../graphics/irr_driver.h"
#include "../input/device_manager.h"
#include "../input/input_manager.h"

#include "../modes/viewer_world.h"

#include "../modes/world.h"



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

#include "../maps/map_manager.h"
#include "../utils/ptr_vector.h"

GameManager* game_manager= NULL;

/** Constructs the race manager.
 */
GameManager::GameManager()
{
    m_map_number       = 0;

    setMap("map1");
}   // RaceManager

//-----------------------------------------------------------------------------
/** Destructor for the race manager.
 */
GameManager::~GameManager()
{
}   // ~RaceManager

//-----------------------------------------------------------------------------
/** Resets the race manager in preparation for a new race. It sets the
 *  counter of finished karts to zero.
 */
void GameManager::reset()
{
   // not needed yet
}  // reset




//-----------------------------------------------------------------------------
/** Sets a single track to be used in the next race.
 *  \param track The identifier of the track to use.
 */
void GameManager::setMap(const std::string& ls_map)
{
    m_maps.clear();
    m_maps.push_back(ls_map);

}   // setMap




//-----------------------------------------------------------------------------
/** \brief Starts the next (or first) race.
 *  It sorts the kart status data structure
 *  according to the number of points, and then creates the world().
 */
 /*)
void GameManager::startNextRace()
{

    // Uncomment to debug audio leaks
    // sfx_manager->dump();

    stk_config->getAllScores(&m_score_for_position, m_num_karts);
    IrrlichtDevice* device = irr_driver->getDevice();
    GUIEngine::renderLoading();
    device->getVideoDriver()->endScene();
    device->getVideoDriver()->beginScene(true, true, video::SColor(255,100,101,140));


    m_num_finished_karts   = 0;
    m_num_finished_players = 0;

    // if subsequent race, sort kart status structure
    // ==============================================
    if (m_track_number > 0)
    {
        // In follow the leader mode do not change the first kart,
        // since it's always the leader.
        int offset = (m_minor_mode==MINOR_MODE_FOLLOW_LEADER) ? 1 : 0;

        // Keep players at the end if needed
        int player_last_offset = 0;
        if (UserConfigParams::m_gp_player_last)
        {
            // Doing this is enough to keep player karts at
            // the end because of the simple reason that they
            // are at the end when getting added. Keep them out
            // of the later sorting and they will stay there.
            player_last_offset = (int)m_player_karts.size();
        }

        std::sort(m_kart_status.begin()+offset,
                  m_kart_status.end() - player_last_offset);
        // reverse kart order if flagged in user's config
        if (UserConfigParams::m_gp_most_points_first)
        {
            std::reverse(m_kart_status.begin()+offset,
                         m_kart_status.end() - player_last_offset);
        }
    }   // not first race

    // the constructor assigns this object to the global
    // variable world. Admittedly a bit ugly, but simplifies
    // handling of objects which get created in the constructor
    // and need world to be defined.
    if(DemoWorld::isDemoMode())
        World::setWorld(new DemoWorld());
    else if(ProfileWorld::isProfileMode())
        World::setWorld(new ProfileWorld());
    else if(m_minor_mode==MINOR_MODE_FOLLOW_LEADER)
        World::setWorld(new FollowTheLeaderRace());
    else if(m_minor_mode==MINOR_MODE_NORMAL_RACE ||
            m_minor_mode==MINOR_MODE_TIME_TRIAL)
        World::setWorld(new StandardRace());
    else if(m_minor_mode==MINOR_MODE_TUTORIAL)
        World::setWorld(new TutorialWorld());
    else if(m_minor_mode==MINOR_MODE_3_STRIKES)
        World::setWorld(new ThreeStrikesBattle());
    else if(m_minor_mode==MINOR_MODE_SOCCER)
        World::setWorld(new SoccerWorld());
    else if(m_minor_mode==MINOR_MODE_OVERWORLD)
        World::setWorld(new OverWorld());
    else if(m_minor_mode==MINOR_MODE_CUTSCENE)
        World::setWorld(new CutsceneWorld());
    else if(m_minor_mode==MINOR_MODE_EASTER_EGG)
        World::setWorld(new EasterEggHunt());
    else
    {
        Log::error("RaceManager", "Could not create given race mode.");
        assert(0);
    }

    // A second constructor phase is necessary in order to be able to
    // call functions which are overwritten (otherwise polymorphism
    // will fail and the results will be incorrect . Also in init() functions
    // can be called that use World::getWorld().
    World::getWorld()->init();

    // Now initialise all values that need to be reset from race to race
    // Calling this here reduces code duplication in init and restartRace()
    // functions.
    World::getWorld()->reset();

    irr_driver->onLoadWorld();

    // Save the current score and set last time to zero. This is necessary
    // if someone presses esc after finishing a gp, and selects restart:
    // The race is rerun, and the points and scores get reset ... but if
    // a kart hasn't finished the race at this stage, last_score and time
    // would be undefined.
    for(int i=0; i<m_num_karts; i++)
    {
        m_kart_status[i].m_last_score = m_kart_status[i].m_score;
        m_kart_status[i].m_last_time  = 0;
    }

    StartGameProtocol* protocol = static_cast<StartGameProtocol*>(
            ProtocolManager::getInstance()->getProtocol(PROTOCOL_START_GAME));
    if (protocol)
        protocol->ready();

}   // startNextRace
*/

/*
//-----------------------------------------------------------------------------
void RaceManager::next()
{
    World::deleteWorld();
    m_num_finished_karts   = 0;
    m_num_finished_players = 0;
    m_track_number++;
    if(m_track_number<(int)m_tracks.size())
    {
        if(m_major_mode==MAJOR_MODE_GRAND_PRIX && !NetworkWorld::getInstance()->isRunning())
        {
            // Saving GP state
            saveGP();
        }
        startNextRace();
    }
    else
    {
        exitRace();
    }
}   // next
*/


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

void GameManager::exitGame()
{
/*
    // Only display the grand prix result screen if all tracks
    // were finished, and not when a race is aborted.
    if (m_major_mode==MAJOR_MODE_GRAND_PRIX && m_track_number==(int)m_tracks.size())
    {
        PlayerManager::getCurrentPlayer()->grandPrixFinished();
        if(m_major_mode==MAJOR_MODE_GRAND_PRIX&& !NetworkWorld::getInstance()->isRunning())
        {
            if(m_saved_gp != NULL)
                m_saved_gp->remove();
        }
        StateManager::get()->resetAndGoToScreen( MainMenuScreen::getInstance() );

        bool someHumanPlayerWon = false;
        const unsigned int kart_status_count = (unsigned int)m_kart_status.size();

        const int loserThreshold = 3;

        std::string winners[3];
        std::vector<std::string> humanLosers; // because we don't care about AIs that lost
        for (unsigned int i=0; i < kart_status_count; ++i)
        {
            if(UserConfigParams::logMisc())
            {
                Log::info("RaceManager", "%s has GP final rank %d",
                    m_kart_status[i].m_ident.c_str(), m_kart_status[i].m_gp_rank);
            }

            const int rank = m_kart_status[i].m_gp_rank;
            if (rank >= 0 && rank < loserThreshold)
            {
                winners[rank] = m_kart_status[i].m_ident;
                if (m_kart_status[i].m_kart_type == KT_PLAYER ||
                    m_kart_status[i].m_kart_type == KT_NETWORK_PLAYER)
                {
                    someHumanPlayerWon = true;
                }
            }
            else if (rank >= loserThreshold)
            {
                if (m_kart_status[i].m_kart_type == KT_PLAYER ||
                    m_kart_status[i].m_kart_type == KT_NETWORK_PLAYER)
                {
                    humanLosers.push_back(m_kart_status[i].m_ident);
                }
            }
        }

        if (delete_world) World::deleteWorld();
        delete_world = false;

        StateManager::get()->enterGameState();
        race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
        race_manager->setNumKarts(0);
        race_manager->setNumPlayers(0);
        race_manager->setNumLocalPlayers(0);

        if (someHumanPlayerWon)
        {
            race_manager->startSingleRace("gpwin", 999, race_manager->raceWasStartedFromOverworld());
            GrandPrixWin* scene = GrandPrixWin::getInstance();
            scene->push();
            scene->setKarts(winners);
        }
        else
        {
            race_manager->startSingleRace("gplose", 999, race_manager->raceWasStartedFromOverworld());
            GrandPrixLose* scene = GrandPrixLose::getInstance();
            scene->push();

            if (humanLosers.size() >= 1)
            {
                scene->setKarts(humanLosers);
            }
            else
            {
                Log::error("RaceManager", "There are no winners and no losers."
                           "This should have never happend\n");
                std::vector<std::string> karts;
                karts.push_back(UserConfigParams::m_default_kart);
                scene->setKarts(karts);
            }
        }
    }

    if (delete_world) World::deleteWorld();

    m_saved_gp = NULL;
    m_track_number = 0;
    */
}   // exitRace





//-----------------------------------------------------------------------------
/** \brief Higher-level method to start a GP without having to care about
 *  the exact startup sequence.
 * \param trackIdent Internal name of the track to race on
 * \param num_laps   Number of laps to race, or -1 if number of laps is
 *        not relevant in current mode
 */
void GameManager::startSingleGame(const std::string &map_ident)
{
    StateManager::get()->enterGameState();

    setMap(map_ident);
    IrrlichtDevice* device = irr_driver->getDevice();
    //GUIEngine::renderLoading(); no loading screen yet
    device->getVideoDriver()->endScene();
    Log::info("GameManager::startSingleGame", "endScene()");
    device->getVideoDriver()->beginScene(true, true, video::SColor(100,244,125,20));
    Log::info("GameManager::startSingleGame", "startScene()");
    World::setWorld(new ViewerWorld());
    Log::info("GameManager::startSingleGame", "creating world");
    World::getWorld()->init();
    Log::info("GameManager::startSingleGame", "init world");
    irr_driver->onLoadWorld();

    //device->getVideoDriver()->endScene(); // test

    //device->getVideoDriver()->beginScene(true, true, video::SColor(100,0,125,255));

    //startNew();
}


/* EOF */


