#include "scene/OEScene.h"

#include <osg/Shape>
#include <osg/ShapeDrawable>
#include <osg/PositionAttitudeTransform>
#include <osg/Program>
#include <osg/LightSource>
#include <osgDB/ReadFile>
#include <osgEarth/GLUtils>
#include <osgEarth/EarthManipulator>

#include <osgShadow/ShadowedScene>
#include <osgShadow/ViewDependentShadowMap>
#include <osgViewer/ViewerEventHandlers>

#include "config.h"
#include "common/SpdLogger.h"
#include "common/RecourceHelper.h"
#include "scene/ScopedTimer.h"
#include "viewer/OsgView.h"
#include "viewer/OsgCameraManipulator.h"
#include "scene/ScaleBarHandler.h"

const osgEarth::SpatialReference* g_srs_{ nullptr };


OEScene::OEScene() {
    osgDB::FilePathList& pathList = osgDB::Registry::instance()->getDataFilePathList();
    const std::string& basePath = RecourceHelper::Get().GetBasePath().toStdString();
    pathList.push_back(basePath + "/resources/earth/");
    pathList.push_back(basePath + "/resources/textures/");
}


void OEScene::InitEventHandle(class OsgView* view) {
    if (nullptr == view) {
        LOG_WARN("view is nullptr");
        return;
    }

    //view->GetView()->addEventHandler(new osgEarth::Util::EarthManipulator());

    view->GetView()->addEventHandler(new osgViewer::HelpHandler);
    view->GetView()->addEventHandler(new osgViewer::StatsHandler);
}

void OEScene::AttachView(OsgView* view) {
    if (nullptr == view) {
        LOG_WARN("view is nullptr");
        return;
    }

    earthRootNode_ = osgDB::readNodeFile("triton.earth");
    if (!earthRootNode_) {
        LOG_ERROR("read earth node(triton.earth) failed");
        return;
    }
    logarithmicDepthBuffer_ = std::make_unique<osgEarth::Util::LogarithmicDepthBuffer>();

    earthMapNode_ = osgEarth::MapNode::get(earthRootNode_);
    LOG_INFO("earth map node get success: {}", earthMapNode_.valid());

    entityRoot_ = new osg::Group;
    if (earthMapNode_) {
        earthMapNode_->addChild(entityRoot_);
        g_srs_ = earthMapNode_->getMapSRS();
        dyt_check(nullptr != g_srs_);
    }

    skyDome_ = osgEarth::SkyNode::create();
    if (!earthMapNode_) {
        LOG_WARN("eart map node is nullptr");
        return;
    }

    skyDome_->addChild(earthMapNode_);

    osg::Node* node = view->GetView()->getSceneData();
    if (nullptr == node) {
        LOG_INFO("view scene data is nullptr, root valid:{}", skyDome_.valid());
        view->GetView()->setSceneData(skyDome_);
    } else {
        osg::Group* group = node->asGroup();
        if (nullptr != group) {
            LOG_INFO("node is group");
            group->addChild(skyDome_);
        } else {
            LOG_INFO("node is not group");
        }
    }
 
    skyDome_->attach(view->GetView());

    skyDome_->setAtmosphereVisible(true);
    skyDome_->setSunVisible(true);
    skyDome_->setMoonVisible(true);
    skyDome_->setStarsVisible(true);

    skyDome_->setDateTime(osgEarth::DateTime(2024, 12, 24, 3));
    skyDome_->setSimulationTimeTracksDateTime(true);

    logarithmicDepthBuffer_->install(view->GetView()->getCamera());

}

void OEScene::DetachView(OsgView* view) {
    if (nullptr != earthRootNode_) {
        std::vector<osg::Group*> parents = earthRootNode_->getParents();
        for (const auto& parent : parents) {
            parent->removeChild(earthRootNode_);
        }
    }
    logarithmicDepthBuffer_->uninstall(view->GetView()->getCamera());
    view->GetView()->setSceneData(nullptr);
    
}

#define USE_CUSTOM_SHADER

osg::ref_ptr<osg::TextureCubeMap> OEScene::LoadCubeMapTextures(const std::string& dir) {
    enum {
        POS_X, NEG_X, POS_Y, NEG_Y, POS_Z, NEG_Z
    };

    std::string filenames[6];

    std::string basePath = RecourceHelper::Get().GetBasePath().toStdString();

    filenames[POS_X] = basePath + "/resources/textures/" + dir + "/east.png";
    filenames[NEG_X] = basePath + "/resources/textures/" + dir + "/west.png";
    filenames[POS_Z] = basePath + "/resources/textures/" + dir + "/north.png";
    filenames[NEG_Z] = basePath + "/resources/textures/" + dir + "/south.png";
    filenames[POS_Y] = basePath + "/resources/textures/" + dir + "/down.png";
    filenames[NEG_Y] = basePath + "/resources/textures/" + dir + "/up.png";

    osg::ref_ptr<osg::TextureCubeMap> cubeMap = new osg::TextureCubeMap;
    cubeMap->setInternalFormat(GL_RGBA);

    cubeMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
    cubeMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
    cubeMap->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
    cubeMap->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);

    cubeMap->setImage(osg::TextureCubeMap::NEGATIVE_X, osgDB::readImageFile(filenames[NEG_X]));
    cubeMap->setImage(osg::TextureCubeMap::POSITIVE_X, osgDB::readImageFile(filenames[POS_X]));
    cubeMap->setImage(osg::TextureCubeMap::NEGATIVE_Y, osgDB::readImageFile(filenames[NEG_Y]));
    cubeMap->setImage(osg::TextureCubeMap::POSITIVE_Y, osgDB::readImageFile(filenames[POS_Y]));
    cubeMap->setImage(osg::TextureCubeMap::NEGATIVE_Z, osgDB::readImageFile(filenames[NEG_Z]));
    cubeMap->setImage(osg::TextureCubeMap::POSITIVE_Z, osgDB::readImageFile(filenames[POS_Z]));

    return cubeMap;
}

osg::Group* OEScene::GetData() {
    return earthMapNode_;
}



void OEScene::AddToScene(osg::Node* node) {
    dyt_check(nullptr != entityRoot_);
    entityRoot_->addChild(node);
   /* osgOcean::OceanScene* oceanScene = GetOceanScene();
    if (nullptr == oceanScene) {
        LOG_WARN("oceanScene is nullptr");
        return;
    }

    node->setNodeMask(oceanScene->getNormalSceneMask() |
        oceanScene->getReflectedSceneMask() |
        oceanScene->getRefractedSceneMask() |
        CAST_SHADOW | RECEIVE_SHADOW);
    oceanScene->addChild(node);*/
    /*  osg::Group* root = GetScene();
      root->addChild(node);*/
}

OESceneUI* OEScene::GetOrCreateSceneUI() {
    if (sceneUI_) {
        LOG_INFO("scene ui is already attached");
        return sceneUI_.get();
    }

    sceneUI_ = new OESceneUI(this);
    return sceneUI_.get();
}