/* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 2 -*-
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 * SPDX-FileCopyrightText: Michael Terry
 */

using GLib;

internal class Rclone : Object
{
  public static string? fill_envp_from_backend(DejaDup.Backend? backend, ref List<string> envp)
  {
    // In case we mess up and forget to set the config password, ask Rclone to
    // never wait for user input and just bail right away.
    envp.append("RCLONE_ASK_PASSWORD=false");

    // If we're inside a container with a custom XDG_CONFIG_HOME, clear it out,
    // so that we use the user's normal ~/.config/rclone/rclone.conf file.
    // If they are typically overriding XDG_CONFIG_HOME themselves... well.
    // It wouldn't have been right before anyway.
    if (DejaDup.InstallEnv.instance().has_custom_config_home())
      envp.append("XDG_CONFIG_HOME=");

    var google_backend = backend as DejaDup.BackendGoogle;
    if (google_backend != null)
      return google_backend.fill_envp(ref envp);

    var microsoft_backend = backend as DejaDup.BackendMicrosoft;
    if (microsoft_backend != null)
      return microsoft_backend.fill_envp(ref envp);

    var rclone_backend = backend as DejaDup.BackendRclone;
    if (rclone_backend != null)
      return rclone_backend.fill_envp(ref envp);

    return null;
  }

  public static string rclone_command()
  {
    var testing_str = Environment.get_variable("DEJA_DUP_TESTING");
    if (testing_str != null && int.parse(testing_str) > 0)
      return "rclone";
    else
      return Config.RCLONE_COMMAND;
  }

  public static async Subprocess? run(string[] argv, DejaDup.Backend? backend = null, bool add_target = true)
  {
    var envp = new List<string>();
    var target = Rclone.fill_envp_from_backend(backend, ref envp);

    var launcher = new SubprocessLauncher(
      SubprocessFlags.STDOUT_PIPE | SubprocessFlags.STDERR_SILENCE
    );
    launcher.set_environ(DejaDup.copy_env(envp));

    // Gather args
    var args = new StrvBuilder();
    args.add(Rclone.rclone_command());
    args.addv(argv);
    if (target != null && add_target)
      args.add(target);

    // Launch it!
    try {
      return launcher.spawnv(args.end());
    } catch (Error err) {
      warning("Could not launch Rclone: %s", err.message);
      return null;
    }
  }

  public static async void get_space(DejaDup.Backend backend, out uint64 free, out uint64 total)
  {
    free = DejaDup.Backend.INFINITE_SPACE;
    total = DejaDup.Backend.INFINITE_SPACE;

    var rclone = yield Rclone.run({"about", "--json"}, backend);
    if (rclone == null)
      return;

    // Parse stdout
    var stdout_raw = rclone.get_stdout_pipe();
    var parser = new Json.Parser();
    try {
      yield parser.load_from_stream_async(stdout_raw);
    } catch (Error err) {
      warning("Could not parse rclone about: %s", err.message);
      return;
    } finally {
      rclone.force_exit();
    }
    if (parser.get_root() == null)
      return; // remote probably doesn't support 'about'

    var reader = new Json.Reader(parser.get_root());

    if (reader.read_member("free")) {
      var parsed = reader.get_int_value();
      if (parsed > 0)
        free = parsed;
      reader.end_member();
    }

    if (reader.read_member("total")) {
      var parsed = reader.get_int_value();
      if (parsed > 0)
        total = parsed;
      reader.end_member();
    }
  }

  public static async List<string> list_files(DejaDup.Backend backend, int max)
  {
    var rclone = yield Rclone.run({"lsf"}, backend);
    if (rclone == null)
      return new List<string>();

    // Grab stdout
    var stdout_raw = rclone.get_stdout_pipe();
    var stdout = new DataInputStream(stdout_raw);

    // And gather up the first `max` lines
    var files = new List<string>();
    while (files.length() < max) {
      try {
        var line = yield stdout.read_line_utf8_async(Priority.LOW);
        if (line == null)
          break;
        if (line[0] == '.')
          continue;
        if (line.data[line.length - 1] == '/')
          line.data[line.length - 1] = 0;
        files.append(line);
      } catch (Error err) {
        warning("Could not parse Rclone output: %s", err.message);
        break;
      }
    }

    rclone.force_exit();
    return files;
  }
}
