/*
 * Copyright (C) 2024 Open Source Robotics Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

#include <filesystem>
#include <regex>

#include <sdf/config.hh>
#include <sdf/InstallationDirectories.hh>

namespace sdf
{
inline namespace SDF_VERSION_NAMESPACE {

// We locally import the gz::common::joinPaths function
// See https://github.com/gazebosim/gz-physics/pull/507#discussion_r1186919267
// for more details

// Function imported from
// https://github.com/gazebosim/gz-common/blob/ignition-common4_4.6.2/src/FilesystemBoost.cc#L507
#ifndef WIN32
static const char preferred_separator = '/';
#else  // Windows
static const char preferred_separator = '\\';
#endif
const std::string separator(const std::string &_p)
{
  return _p + preferred_separator;
}

// Function imported from
// https://github.com/gazebosim/gz-common/blob/ignition-common4_4.6.2/src/Filesystem.cc#L227
std::string checkWindowsPath(const std::string &_path)
{
  if (_path.empty())
    return _path;

  // Check if this is a http or https, if so change backslashes generated by
  // jointPaths to '/'
  if ((_path.size() > 7 && 0 == _path.compare(0, 7, "http://")) ||
      (_path.size() > 8 && 0 == _path.compare(0, 8, "https://")))
  {
    return std::regex_replace(_path, std::regex(R"(\\)"), "/");
  }

  // This is a Windows path, convert all '/' into backslashes
  std::string result = std::regex_replace(_path, std::regex(R"(/)"), "\\");
  std::string drive_letters;

  // only Windows contains absolute paths starting with drive letters
  if (result.length() > 3 && 0 == result.compare(1, 2, ":\\"))
  {
    drive_letters = result.substr(0, 3);
    result = result.substr(3);
  }
  result = drive_letters + std::regex_replace(
    result, std::regex("[<>:\"|?*]"), "");
  return result;
}

// Function imported from
// https://github.com/gazebosim/gz-common/blob/gz-common5_5.6.0/src/Filesystem.cc#L142
std::string joinPaths(const std::string &_path1,
                      const std::string &_path2)
{
  namespace fs = std::filesystem;
  fs::path p1{_path1};
  fs::path p2{_path2};

  if (p1.empty())
  {
    p1 = std::string{fs::path::preferred_separator};
  }

  bool is_url = false;

  if (_path1.find("://") == std::string::npos)
    p1 = p1.lexically_normal();
  else
    is_url = true;

  // TODO(mjcarroll) Address the case that path2 is also a URI.
  // It's likely not a valid scenario, but not currently covered by our test
  // suite and doesn't return an error.
  if (_path2.find("://") == std::string::npos)
    p2 = p2.lexically_normal();
  else
    is_url = true;

  if (p2.string()[0] == fs::path::preferred_separator)
  {
    p2 = fs::path{p2.string().substr(1)};
  }

  auto ret = (p1 / p2);

  if (is_url)
  {
    std::string path = ret.string();
    std::replace(path.begin(), path.end(),
        static_cast<char>(fs::path::preferred_separator), '/');
    return path;
  }
  else
  {
    return ret.lexically_normal().string();
  }
}

std::string getSharePath()
{
  return sdf::joinPaths(
      getInstallPrefix(), CMAKE_INSTALL_RELATIVE_DATAROOTDIR);
}

}
}
