/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "crashreporter.h"

#include <algorithm>
#include <sys/wait.h>

#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>

using namespace CrashReporter;
using std::ios_base;
using std::sort;
using std::string;
using std::vector;

struct FileData {
  time_t timestamp;
  string path;
};

static bool CompareFDTime(const FileData& fd1, const FileData& fd2) {
  return fd1.timestamp > fd2.timestamp;
}

void UIPruneSavedDumps(const string& directory) {
  DIR* dirfd = opendir(directory.c_str());
  if (!dirfd) return;

  vector<FileData> dumpfiles;

  while (dirent* dir = readdir(dirfd)) {
    FileData fd;
    fd.path = directory + '/' + dir->d_name;
    if (fd.path.size() < 5) continue;

    if (fd.path.compare(fd.path.size() - 4, 4, ".dmp") != 0) continue;

    struct stat st;
    if (stat(fd.path.c_str(), &st)) {
      closedir(dirfd);
      return;
    }

    fd.timestamp = st.st_mtime;

    dumpfiles.push_back(fd);
  }

  closedir(dirfd);

  sort(dumpfiles.begin(), dumpfiles.end(), CompareFDTime);

  while (dumpfiles.size() > kSaveCount) {
    // get the path of the oldest file
    string path = dumpfiles[dumpfiles.size() - 1].path;
    UIDeleteFile(path.c_str());

    // s/.dmp/.extra/
    path.replace(path.size() - 4, 4, ".extra");
    UIDeleteFile(path.c_str());

    dumpfiles.pop_back();
  }
}

bool UIRunProgram(const string& exename, const vector<string>& args,
                  bool wait) {
  pid_t pid = fork();

  if (pid == -1) {
    return false;
  } else if (pid == 0) {
    // Child
    size_t argvLen = args.size() + 2;
    vector<char*> argv(argvLen);

    argv[0] = const_cast<char*>(exename.c_str());

    for (size_t i = 0; i < args.size(); i++) {
      argv[i + 1] = const_cast<char*>(args[i].c_str());
    }

    argv[argvLen - 1] = nullptr;

    // Run the program
    int rv = execv(exename.c_str(), argv.data());

    if (rv == -1) {
      exit(EXIT_FAILURE);
    }
  } else {
    // Parent
    if (wait) {
      waitpid(pid, nullptr, 0);
    }
  }

  return true;
}

bool UIEnsurePathExists(const string& path) {
  int ret = mkdir(path.c_str(), S_IRWXU);
  int e = errno;
  if (ret == -1 && e != EEXIST) return false;

  return true;
}

bool UIFileExists(const string& path) {
  struct stat sb;
  int ret = stat(path.c_str(), &sb);
  if (ret == -1 || !(sb.st_mode & S_IFREG)) return false;

  return true;
}

bool UIDeleteFile(const string& file) { return (unlink(file.c_str()) != -1); }

std::ifstream* UIOpenRead(const string& filename, ios_base::openmode mode) {
  return new std::ifstream(filename.c_str(), mode);
}

std::ofstream* UIOpenWrite(const string& filename, ios_base::openmode mode) {
  return new std::ofstream(filename.c_str(), mode);
}

string UIGetEnv(const string& name) {
  const char* var = getenv(name.c_str());
  if (var && *var) {
    return var;
  }

  return "";
}
