// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/fml/file.h"

#include "flutter/fml/unique_fd.h"

#include "spdlog/spdlog.h"

namespace fml {

static fml::UniqueFD CreateDirectory(const fml::UniqueFD& base_directory,
                                     const std::vector<std::string>& components,
                                     FilePermission permission,
                                     size_t index) {
  const char* file_path = components[index].c_str();

  auto directory = OpenDirectory(base_directory, file_path, true, permission);

  if (!directory.is_valid()) {
    return {};
  }

  if (index == components.size() - 1) {
    return directory;
  }

  return CreateDirectory(directory, components, permission, index + 1);
}

fml::UniqueFD CreateDirectory(const fml::UniqueFD& base_directory,
                              const std::vector<std::string>& components,
                              FilePermission permission) {
  if (!IsDirectory(base_directory)) {
    return {};
  }

  if (components.empty()) {
    return {};
  }

  return CreateDirectory(base_directory, components, permission, 0);
}

ScopedTemporaryDirectory::ScopedTemporaryDirectory()
    : path_(CreateTemporaryDirectory()) {
  if (path_ != "") {
    dir_fd_ = OpenDirectory(path_.c_str(), false, FilePermission::kRead);
  }
}

ScopedTemporaryDirectory::~ScopedTemporaryDirectory() {
  // POSIX requires the directory to be empty before UnlinkDirectory.
  if (path_ != "") {
    if (!RemoveFilesInDirectory(dir_fd_)) {
      SPDLOG_ERROR("Could not clean directory: {}", path_);
    }
  }

  // Windows has to close UniqueFD first before UnlinkDirectory
  dir_fd_.reset();
  if (path_ != "") {
    if (!UnlinkDirectory(path_.c_str())) {
      SPDLOG_ERROR("Could not remove directory: {}", path_);
    }
  }
}

bool VisitFilesRecursively(const fml::UniqueFD& directory,
                           const FileVisitor& visitor) {
  FileVisitor recursive_visitor = [&recursive_visitor, &visitor](
                                      const UniqueFD& directory,
                                      const std::string& filename) {
    if (!visitor(directory, filename)) {
      return false;
    }
    if (IsDirectory(directory, filename.c_str())) {
      UniqueFD sub_dir = OpenDirectoryReadOnly(directory, filename.c_str());
      if (!sub_dir.is_valid()) {
        SPDLOG_ERROR("Can't open sub-directory: {}", filename);
        return true;
      }
      return VisitFiles(sub_dir, recursive_visitor);
    }
    return true;
  };
  return VisitFiles(directory, recursive_visitor);
}

fml::UniqueFD OpenFileReadOnly(const fml::UniqueFD& base_directory,
                               const char* path) {
  return OpenFile(base_directory, path, false, FilePermission::kRead);
}

fml::UniqueFD OpenDirectoryReadOnly(const fml::UniqueFD& base_directory,
                                    const char* path) {
  return OpenDirectory(base_directory, path, false, FilePermission::kRead);
}

bool RemoveFilesInDirectory(const fml::UniqueFD& directory) {
  fml::FileVisitor recursive_cleanup = [&recursive_cleanup](
                                           const fml::UniqueFD& directory,
                                           const std::string& filename) {
    bool removed;
    if (fml::IsDirectory(directory, filename.c_str())) {
      fml::UniqueFD sub_dir =
          OpenDirectoryReadOnly(directory, filename.c_str());
      removed = VisitFiles(sub_dir, recursive_cleanup) &&
                fml::UnlinkDirectory(directory, filename.c_str());
    } else {
      removed = fml::UnlinkFile(directory, filename.c_str());
    }
    return removed;
  };
  return VisitFiles(directory, recursive_cleanup);
}

bool RemoveDirectoryRecursively(const fml::UniqueFD& parent,
                                const char* directory_name) {
  auto dir = fml::OpenDirectory(parent, directory_name, false,
                                fml::FilePermission::kReadWrite);
  return RemoveFilesInDirectory(dir) && UnlinkDirectory(parent, directory_name);
}

}  // namespace fml
