// terrain_pgt.cxx -- data structures and routines for managing scenery.
//
// Written by Curtis Olson, started May 1997.
//
// Copyright (C) 1997  Curtis L. Olson  - http://www.flightgear.org/~curt
//
// 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// $Id$


#include <config.h>
#include <simgear/simgear_config.h>

#ifdef ENABLE_GDAL

#include <simgear/scene/material/mat.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>

#include <Main/globals.hxx>
#include <Main/fg_props.hxx>
#include <Viewer/splash.hxx>

#include "terrain_pgt.hxx"
#include "scenery.hxx"

using namespace flightgear;
using namespace simgear;

using flightgear::SceneryPager;

// Terrain Management system
FGPgtTerrain::FGPgtTerrain() : 
    _scenery_loaded(fgGetNode("/sim/sceneryloaded", true)),
    _scenery_override(fgGetNode("/sim/sceneryloaded-override", true))
{
    _inited  = false;
}

FGPgtTerrain::~FGPgtTerrain()
{
}

// Initialize the Scenery Management system
void FGPgtTerrain::init( osg::Group* terrain ) {
    // Already set up.
    if (_inited)
        return;

    SG_LOG(SG_TERRAIN, SG_INFO, "FGPgtTerrain::init");

    // remember the scene terrain branch on scenegraph
    terrain_branch = terrain;

    // load the whole planet tile - database pager handles 
    // the quad tree / loading the highres tiles
    osg::ref_ptr<simgear::SGReaderWriterOptions> options;

    // drops the previous options reference
    options = new simgear::SGReaderWriterOptions;
    options->setPropertyNode(globals->get_props());

    osgDB::FilePathList &fp = options->getDatabasePathList();
    const PathList &sc = globals->get_fg_scenery();
    fp.clear();
    PathList::const_iterator it;
    for (it = sc.begin(); it != sc.end(); ++it) {
        fp.push_back(it->local8BitStr());
    }

    options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().utf8Str());

    options->setPluginStringData("SimGear::BARE_LOD_RANGE",
                                 fgGetString("/sim/rendering/static-lod/bare-delta",
                                             std::to_string(SG_OBJECT_RANGE_BARE)));
    options->setPluginStringData("SimGear::ROUGH_LOD_RANGE",
                                 fgGetString("/sim/rendering/static-lod/rough-delta",
                                             std::to_string(SG_OBJECT_RANGE_ROUGH)));
    options->setPluginStringData("SimGear::ROUGH_LOD_DETAILED",
                                 fgGetString("/sim/rendering/static-lod/detailed",
                                             std::to_string(SG_OBJECT_RANGE_DETAILED)));
    options->setPluginStringData("SimGear::RENDER_BUILDING_MESH", fgGetBool("/sim/rendering/building-mesh", false) ? "true" : "false");

    options->setPluginStringData("SimGear::FG_EARTH", "ON");

    // tunables
    options->setPluginStringData("SimGear::SPT_PAGE_LEVELS", fgGetString("/sim/scenery/lod-levels", "1 3 5 7 9" ));
    options->setPluginStringData("SimGear::SPT_RANGE_MULTIPLIER", fgGetString("/sim/scenery/lod-range-mult", "2" ));
    options->setPluginStringData("SimGear::SPT_MESH_RESOLUTION", fgGetString("/sim/scenery/lod-res", "1" ));
    options->setPluginStringData("SimGear::SPT_LOD_TEXTURING", fgGetString("/sim/scenery/lod-texturing", "bluemarble" ));
    options->setMaterialLib(globals->get_matlib());

    // a DEM can contain multiple levels from multiple locations
    // priority is based on first found...
    _dem = new SGDem;
    if ( _dem ) {
        for (osgDB::FilePathList::const_iterator i = fp.begin(); i != fp.end(); ++i) {
            SGPath demPath(*i);
            demPath.append("DEM");

            int numLevels = _dem->addRoot(demPath);
            if ( numLevels ) {
                SG_LOG(SG_TERRAIN, SG_INFO, "Terrain init - dem path " << demPath << " has " << numLevels << " LOD Levels " );
            } else {
                SG_LOG(SG_TERRAIN, SG_INFO, "Terrain init - dem path " << demPath << " has NO LOD Levels " );
            }
        }

        options->setDem(_dem);

        SG_LOG(SG_TERRAIN, SG_INFO, "Terrain init - Load w180s90-360x180.pgt" );
#if OSG_VERSION_LESS_THAN(3,4,0)
        osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFile("w180s90-360x180.pgt", options.get());
#else
        osg::ref_ptr<osg::Node> loadedModel = osgDB::readRefNodeFile("w180s90-360x180.pgt", options.get());
#endif
        if ( loadedModel ) {
            terrain_branch->addChild( loadedModel.get() );

            // Toggle the setup flag.
            _inited = true;
        }
    }
}

void FGPgtTerrain::reinit()
{

}

void FGPgtTerrain::shutdown()
{
    terrain_branch = NULL;

    // Toggle the setup flag.
    _inited = false;
}


void FGPgtTerrain::update(double dt)
{
    // scenery loading check, triggers after each sim (tile manager) reinit
    if (!_scenery_loaded->getBoolValue())
    {
        bool fdmInited = fgGetBool("sim/fdm-initialized");
        bool positionFinalized = fgGetBool("sim/position-finalized");
        bool sceneryOverride = _scenery_override->getBoolValue();

        // we are done if final position is set and the scenery & FDM are done.
        // scenery-override can ignore the last two, but not position finalization.
        if (positionFinalized && (sceneryOverride || fdmInited))
        {
            _scenery_loaded->setBoolValue(true);
            fgSplashProgress("");
        }
        else
        {
            if (!positionFinalized) {
                fgSplashProgress("finalize-position");
            } else {
                fgSplashProgress("loading-scenery");
            }

            // be nice to loader threads while waiting for initial scenery, reduce to 20fps
            SGTimeStamp::sleepForMSec(50);
        }
    }
}

void FGPgtTerrain::bind() 
{

}

void FGPgtTerrain::unbind() 
{

}

bool
FGPgtTerrain::get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
                                   double& alt,
                                   const simgear::BVHMaterial** material,
                                   const osg::Node* butNotFrom)
{
    SGGeod geod = SGGeod::fromCart(pos);
    geod.setElevationM(geod.getElevationM() + max_altoff);

    return get_elevation_m(geod, alt, material, butNotFrom);
}

static simgear::BVHMaterial def_mat;

bool
FGPgtTerrain::get_elevation_m(const SGGeod& geod, double& alt,
                              const simgear::BVHMaterial** material,
                              const osg::Node* butNotFrom)
{
    alt = 100.0;
    if (material) {
      *material = &def_mat;
    } else {
//      SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::get_elevation_m: alt " << alt << " no material " );
    }

    return true;
}

bool FGPgtTerrain::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir,
                                           SGVec3d& nearestHit,
                                           const osg::Node* butNotFrom)
{
    return true;
}

bool FGPgtTerrain::scenery_available(const SGGeod& position, double range_m)
{
    if( schedule_scenery(position, range_m, 0.0) )
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool FGPgtTerrain::schedule_scenery(const SGGeod& position, double range_m, double duration)
{
    // sanity check (unfortunately needed!)
    if (!position.isValid()) {
        SG_LOG(SG_TERRAIN, SG_INFO, "FGSptTerrain::schedule_scenery - position invalid");
        return false;
    }

    return true;
}

void FGPgtTerrain::materialLibChanged()
{
    // PSADRO: TODO - passing down new regional textures won't work.  these need to be set in the 
    // lod tree at init time, as OSGDBPager generates the load request, not the tile cache.

    // _options->setMaterialLib(globals->get_matlib());
}

#endif
