// SPDX-License-Identifier: Unlicense

#include "skybox.h"

#ifdef SHR3D_ENVIRONMENT_SKYBOX

#include "file.h"
#include "global.h"
#include "opengl.h"
#include "shader.h"
#include "texture.h"
#include "geometry.h"

void Skybox::init()
{
  const std::vector<std::u8string> skyboxFilepaths = File::filesInDirectory(Settings::pathSkybox.c_str());

  for (const std::u8string& filepath : skyboxFilepaths)
  {
    const std::u8string extension = File::extension(filepath.c_str());

    if (extension != std::u8string(
#ifdef __ANDROID__
      u8".astc" ) && extension != std::u8string(u8".astc6"
#else // __ANDROID__
      u8".dds"
#endif // __ANDROID__
    )
#ifdef SHR3D_PNG_DECODER
      && extension != std::u8string(u8".png")
#endif // SHR3D_PNG_DECODER
      )
      continue;

    {
      std::u8string skyboxName = File::filename(filepath.c_str());
      skyboxName = skyboxName.substr(0, skyboxName.size() - extension.size());

      Global::environmentSkyboxNames.push_back(File::filename(filepath.c_str()));
    }
  }
}

void Skybox::tick()
{
  static std::u8string activeSkybox;
  if (activeSkybox != Settings::environmentSkybox)
  {
    activeSkybox = Settings::environmentSkybox;

    if (!Settings::environmentSkybox.empty())
    {
      const std::u8string textureFilepath = Settings::pathSkybox + Settings::environmentSkybox;

      if (Global::skyboxTexture != 0)
        GL(glDeleteTextures(1, &Global::skyboxTexture));

//      if (Settings::environmentSkybox.ends_with(u8"_cube.png"))
//      {
//        Global::skyboxTextureType = SkyboxTextureType::cubemap;
//
//        const std::vector<u8> textureData = File::read(textureFilepath.c_str());
//
//        Global::skyboxTexture = Texture::openGlLoadCubeTexturePng(textureData.data(), i32(textureData.size()));
//        ASSERT(Global::skyboxTexture != 0);
//        return;
//      }
#ifdef __ANDROID__
      if (Settings::environmentSkybox.ends_with(u8".astc6"))
      {
        Global::skyboxTextureType = SkyboxTextureType::cubemap;

        const std::vector<u8> textureData = File::read(textureFilepath.c_str());

        Global::skyboxTexture = Texture::openGlLoadCubeTextureAstc6(textureData.data(), i32(textureData.size()));
        ASSERT(Global::skyboxTexture != 0);
        return;
      }
#else // __ANDROID
      if (Settings::environmentSkybox.ends_with(u8"_cube.dds"))
      {
        Global::skyboxTextureType = SkyboxTextureType::cubemap;

        const std::vector<u8> textureData = File::read(textureFilepath.c_str());

        Global::skyboxTexture = Texture::openGlLoadCubeTextureDds(textureData.data(), i32(textureData.size()));
                ASSERT(Global::skyboxTexture != 0);
        return;
      }
#endif // __ANDROID__
//      if (Settings::environmentSkybox.find(u8"_?") != std::string::npos)
//      {
//        Global::skyboxTextureType = SkyboxTextureType::cubemap;
//
//        const std::u8string extension = File::extension(Settings::environmentSkybox.c_str());
//        std::u8string skyboxName = File::filename(Settings::environmentSkybox.c_str());
//        skyboxName = skyboxName.substr(0, skyboxName.size() - extension.size() - 1);
//
//        if (!Settings::environmentSkybox.empty())
//        {
//          const std::u8string textureFilepaths[] = {
//            Settings::pathSkybox + skyboxName + u8"right" + extension,
//            Settings::pathSkybox + skyboxName + u8"left" + extension,
//            Settings::pathSkybox + skyboxName + u8"up" + extension,
//            Settings::pathSkybox + skyboxName + u8"down" + extension,
//            Settings::pathSkybox + skyboxName + u8"back" + extension,
//            Settings::pathSkybox + skyboxName + u8"front" + extension,
//          };
//
//          ASSERT(File::exists(textureFilepaths[0].c_str()));
//          ASSERT(File::exists(textureFilepaths[1].c_str()));
//          ASSERT(File::exists(textureFilepaths[2].c_str()));
//          ASSERT(File::exists(textureFilepaths[3].c_str()));
//          ASSERT(File::exists(textureFilepaths[4].c_str()));
//          ASSERT(File::exists(textureFilepaths[5].c_str()));
//
//          {
//            const std::vector<u8> textureData[] = {
//              File::read(textureFilepaths[0].c_str()),
//              File::read(textureFilepaths[1].c_str()),
//              File::read(textureFilepaths[2].c_str()),
//              File::read(textureFilepaths[3].c_str()),
//              File::read(textureFilepaths[4].c_str()),
//              File::read(textureFilepaths[5].c_str())
//            };
//
//            const u8* dataPtrs[] =
//            {
//              &textureData[0][0],
//              &textureData[1][0],
//              &textureData[2][0],
//              &textureData[3][0],
//              &textureData[4][0],
//              &textureData[5][0],
//            };
//            const i32 dataSizes[] =
//            {
//              i32(textureData[0].size()),
//              i32(textureData[1].size()),
//              i32(textureData[2].size()),
//              i32(textureData[3].size()),
//              i32(textureData[4].size()),
//              i32(textureData[5].size())
//            };
//
//            Global::skyboxTexture = Texture::openGlLoadCubeTexture(dataPtrs, dataSizes);
//            ASSERT(Global::skyboxTexture != 0);
//          }
//        }
//        return;
//      }

      {
        Global::skyboxTextureType = SkyboxTextureType::skysphere360degree;

        const std::vector<u8> textureData = File::read(textureFilepath.c_str());

        Global::skyboxTexture = Texture::openGlLoadTexture(textureData.data(), i32(textureData.size()));
        ASSERT(Global::skyboxTexture != 0);
        return;
      }
    }
  }
}

void Skybox::render(const mat4& viewProjectionSkyboxMat)
{
  if (Settings::environmentSkybox.empty())
    return;

  switch (Global::skyboxTextureType)
  {
    case SkyboxTextureType::cubemap:
      GL(glUseProgram(Shader::skyboxCubeTexture));
      GL(glBindTexture(GL_TEXTURE_CUBE_MAP, Global::skyboxTexture));
      break;
    case SkyboxTextureType::skysphere360degree:
      GL(glUseProgram(Shader::skybox));
      GL(glUniform1f(Shader::skyboxUniformRotation, Settings::environmentSkyboxRotation));
      GL(glBindTexture(GL_TEXTURE_2D, Global::skyboxTexture));
      break;
    default:
      unreachable();
  }

  const mat4 model
  {
    .m00 = Settings::highwayViewDistance * 1.8f,
    .m11 = Settings::highwayViewDistance * 1.8f,
    .m22 = Settings::highwayViewDistance * 1.8f
  };

  const mat4 mvp = vec::multiply(viewProjectionSkyboxMat, model);
  GL(glUniformMatrix4fv(Shader::skyboxUniformModelViewProjection, 1, GL_FALSE, &mvp.m00));
  GL(glDepthMask(GL_FALSE));
  GL(glBindVertexArray(Global::staticDrawVao));
  GL(glDrawElements_(skysphere));
  GL(glBindVertexArray(Global::dynamicDrawVao));
  GL(glDepthMask(GL_TRUE));

  GL(glBindTexture(GL_TEXTURE_2D, Global::texture));
}

#endif // SHR3D_ENVIRONMENT_SKYBOX