/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkResourceFileLocator.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkResourceFileLocator.h"

#include "vtkObjectFactory.h"
#include <vtksys/SystemTools.hxx>

#if defined(_WIN32) && !defined(__CYGWIN__)
#define VTK_PATH_SEPARATOR "\\"
#else
#define VTK_PATH_SEPARATOR "/"
#endif

#define VTK_FILE_LOCATOR_DEBUG_MESSAGE(x)                                                          \
  if (this->PrintDebugInformation)                                                                 \
  {                                                                                                \
    cout << "# vtk: " x << endl;                                                                   \
  }

#if defined(_WIN32) && !defined(__CYGWIN__)
// Implementation for Windows win32 code but not cygwin
#include <windows.h>
#else
#include <dlfcn.h>
#endif

vtkStandardNewMacro(vtkResourceFileLocator);
//----------------------------------------------------------------------------
vtkResourceFileLocator::vtkResourceFileLocator()
  : PrintDebugInformation(false)
{
}

//----------------------------------------------------------------------------
vtkResourceFileLocator::~vtkResourceFileLocator() = default;

//----------------------------------------------------------------------------
std::string vtkResourceFileLocator::Locate(
  const std::string& anchor, const std::string& landmark, const std::string& defaultDir)
{
  return this->Locate(anchor, { std::string() }, landmark, defaultDir);
}

//----------------------------------------------------------------------------
std::string vtkResourceFileLocator::Locate(const std::string& anchor,
  const std::vector<std::string>& landmark_prefixes, const std::string& landmark,
  const std::string& defaultDir)
{
  VTK_FILE_LOCATOR_DEBUG_MESSAGE("looking for '" << landmark << "'");
  std::vector<std::string> path_components;
  vtksys::SystemTools::SplitPath(anchor, path_components);
  while (!path_components.empty())
  {
    std::string curanchor = vtksys::SystemTools::JoinPath(path_components);
    for (const std::string& curprefix : landmark_prefixes)
    {
      const std::string landmarkdir =
        curprefix.empty() ? curanchor : curanchor + VTK_PATH_SEPARATOR + curprefix;
      const std::string landmarktocheck = landmarkdir + VTK_PATH_SEPARATOR + landmark;
      if (vtksys::SystemTools::FileExists(landmarktocheck))
      {
        VTK_FILE_LOCATOR_DEBUG_MESSAGE("trying file " << landmarktocheck << " -- success!");
        return landmarkdir;
      }
      else
      {
        VTK_FILE_LOCATOR_DEBUG_MESSAGE("trying file " << landmarktocheck << " -- failed!");
      }
    }
    path_components.pop_back();
  }
  return defaultDir;
}

//----------------------------------------------------------------------------
std::string vtkResourceFileLocator::GetLibraryPathForSymbolUnix(const char* symbolname)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
  (void)symbolname;
  return std::string();
#else
  void* handle = dlsym(RTLD_DEFAULT, symbolname);
  if (!handle)
  {
    return std::string();
  }

  Dl_info di;
  int ret = dladdr(handle, &di);
  if (ret == 0 || !di.dli_saddr || !di.dli_fname)
  {
    return std::string();
  }

  return std::string(di.dli_fname);
#endif
}

//----------------------------------------------------------------------------
std::string vtkResourceFileLocator::GetLibraryPathForSymbolWin32(const void* fptr)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
  MEMORY_BASIC_INFORMATION mbi;
  VirtualQuery(fptr, &mbi, sizeof(mbi));
  char pathBuf[16384];
  if (!GetModuleFileName(static_cast<HMODULE>(mbi.AllocationBase), pathBuf, sizeof(pathBuf)))
  {
    return std::string();
  }

  return std::string(pathBuf);
#else
  (void)fptr;
  return std::string();
#endif
}

//----------------------------------------------------------------------------
void vtkResourceFileLocator::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "PrintDebugInformation: " << this->PrintDebugInformation << endl;
}
