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

using GLib;

// This class is a singleton that "looks like" the current operation.
// That is, calling stop() on this singleton will stop the current operation.
// Progress events from the current operation will also be emitted from this
// singleton. This is designed to make it easier to write code that watches
// for "what is Deja Dup doing now?". Note the "active" signal for tracking
// whether an operation is active or not.

public class DejaDup.OperationWatcher : DejaDup.Operation
{
  // Static instance support
  static OperationWatcher instance;
  public static OperationWatcher get_instance()
  {
    if (instance == null)
      instance = new OperationWatcher();
    return instance;
  }

  // Public operation API
  public bool active { get { return current_op_ref.get() != null; } }

  public void set_current_op(DejaDup.Operation op)
  {
    if (current_op_ref.get() != null) {
      warning("Overriding one Operation with another.");
      disconnect_from_op();
    }

    current_op_ref.set(op);
    connect_to_op();
    notify_property("active");
  }

  public override State get_state() {
    var op = (DejaDup.Operation)current_op_ref.get();
    return op.get_state();
  }
  public override void set_state(State state) {
    var op = (DejaDup.Operation)current_op_ref.get();
    op.set_state(state);
  }

  public async override void start() { warning("Cannot start OperationWatcher."); }
  public override bool stop_if_dormant() {
    var op = (DejaDup.Operation)current_op_ref.get();
    return op.stop_if_dormant();
  }
  public override void stop() {
    var op = (DejaDup.Operation)current_op_ref.get();
    op.stop();
  }
  public override void resume() {
    var op = (DejaDup.Operation)current_op_ref.get();
    op.resume();
  }

  public override void set_mount_op(MountOperation mount_op) {
    var op = (DejaDup.Operation)current_op_ref.get();
    op.set_mount_op(mount_op);
  }

  public override async void set_passphrase(string? passphrase, bool save) {
    var op = (DejaDup.Operation)current_op_ref.get();
    yield op.set_passphrase(passphrase, save);
  }

  public override async void set_backend_password(string? password, bool save) {
    var op = (DejaDup.Operation)current_op_ref.get();
    yield op.set_backend_password(password, save);
  }

  public override bool set_oauth_token(string url) {
    var op = (DejaDup.Operation)current_op_ref.get();
    return op.set_oauth_token(url);
  }

  // Private API

  construct {
    Application.get_default().bind_busy_property(this, "active");
    done.connect_after(disconnect_from_op);
  }

  private void connect_to_op()
  {
    var op = (DejaDup.Operation)current_op_ref.get();

    op.started.connect(() => {started();});
    op.done.connect((s, c, d) => {done(s, c, d);});
    op.raise_error.connect((e, d) => {raise_error(e, d);});
    op.action_desc_changed.connect((a) => {action_desc_changed(a);});
    op.action_file_changed.connect((f, a) => {action_file_changed(f, a);});
    op.is_full.connect((f) => {is_full(f);});
    op.progress.connect((p) => {progress(p);});
    op.open_url.connect((u) => {open_url(u);});
    op.mount_op_required.connect(() => {mount_op_required();});
    op.passphrase_required.connect((r, f) => {passphrase_required(r, f);});
    op.backend_password_required.connect((d, l, e) => {backend_password_required(d, l, e);});
    op.question.connect((s, h, d, a, safe) => {question(s, h, d, a, safe);});

    op.bind_property(
      "use-cached-password", this, "use-cached-password",
      BindingFlags.SYNC_CREATE
    );
    op.bind_property(
      "needs-password", this, "needs-password",
      BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL
    );
    op.bind_property("backend", this, "backend", BindingFlags.SYNC_CREATE);
    op.bind_property(
      "use-progress", this, "use-progress",
      BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL
    );
    op.bind_property("mode", this, "mode", BindingFlags.SYNC_CREATE);
  }

  private void disconnect_from_op()
  {
    var op = current_op_ref.get();

    SignalHandler.disconnect_matched(
      op, SignalMatchType.DATA, 0, 0, null, null, this
    );

    current_op_ref.set(null);
    notify_property("active");
  }

  WeakRef current_op_ref;
  private OperationWatcher()
  {
    Object();
  }
}
