// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause

#include "SMP/Common/vtkSMPToolsImpl.h"
#include "SMP/STDThread/vtkSMPToolsImpl.txx"

#if defined(__EMSCRIPTEN__)
#include <emscripten/threading.h>
#endif

#include <cstdlib> // For std::getenv()
#include <thread>  // For std::thread::hardware_concurrency()

namespace vtk
{
namespace detail
{
namespace smp
{
VTK_ABI_NAMESPACE_BEGIN
static int specifiedNumThreadsSTD; // Default initialized to zero

//------------------------------------------------------------------------------
int GetNumberOfThreadsSTDThread()
{
#if defined(__EMSCRIPTEN__PTHREAD__)
  int nWASMThreads = EM_ASM_INT({ return PThread.unusedWorkers.length });
  return specifiedNumThreadsSTD ? specifiedNumThreadsSTD : nWASMThreads;
#else
  return specifiedNumThreadsSTD ? specifiedNumThreadsSTD : std::thread::hardware_concurrency();
#endif
}

//------------------------------------------------------------------------------
template <>
void vtkSMPToolsImpl<BackendType::STDThread>::Initialize(int numThreads)
{
#if defined(__EMSCRIPTEN__PTHREAD__)
  // How do we handle maxThreads = 0
  const int maxThreads = EM_ASM_INT({ return PThread.unusedWorkers.length });
#else
  const int maxThreads = std::thread::hardware_concurrency();
#endif
  if (numThreads == 0)
  {
    const char* vtkSmpNumThreads = std::getenv("VTK_SMP_MAX_THREADS");
    if (vtkSmpNumThreads)
    {
      numThreads = std::atoi(vtkSmpNumThreads);
    }
    else
    {
      specifiedNumThreadsSTD = 0;
    }
  }
  if (numThreads > 0)
  {
    numThreads = std::min(numThreads, maxThreads);
    specifiedNumThreadsSTD = numThreads;
  }
}

//------------------------------------------------------------------------------
template <>
int vtkSMPToolsImpl<BackendType::STDThread>::GetEstimatedNumberOfThreads()
{
#if defined(__EMSCRIPTEN__PTHREAD__)
  const int maxThreads = EM_ASM_INT({ return PThread.unusedWorkers.length });
  return specifiedNumThreadsSTD > 0 ? specifiedNumThreadsSTD : maxThreads;
#else
  return specifiedNumThreadsSTD > 0 ? specifiedNumThreadsSTD : std::thread::hardware_concurrency();
#endif
}

//------------------------------------------------------------------------------
template <>
int vtkSMPToolsImpl<BackendType::STDThread>::GetEstimatedDefaultNumberOfThreads()
{
#if defined(__EMSCRIPTEN__PTHREAD__)
  const int maxThreads = EM_ASM_INT({ return PThread.unusedWorkers.length });
  return maxThreads;
#else
  return std::thread::hardware_concurrency();
#endif
}

//------------------------------------------------------------------------------
template <>
bool vtkSMPToolsImpl<BackendType::STDThread>::GetSingleThread()
{
  return vtkSMPThreadPool::GetInstance().GetSingleThread();
}

template <>
void vtkSMPToolsImpl<BackendType::STDThread>::SetNumberOfBackendThreads(int threadCount)
{
  return vtkSMPThreadPool::GetInstance().SetNumberOfThreads(threadCount);
}

//------------------------------------------------------------------------------
template <>
bool vtkSMPToolsImpl<BackendType::STDThread>::IsParallelScope()
{
  return vtkSMPThreadPool::GetInstance().IsParallelScope();
}

VTK_ABI_NAMESPACE_END
} // namespace smp
} // namespace detail
} // namespace vtk
