/* WirePlumber
 *
 * Copyright © 2023 Collabora Ltd.
 *    @author George Kiagiadakis <george.kiagiadakis@collabora.com>
 *
 * SPDX-License-Identifier: MIT
 */

#include "../common/test-log.h"

static void
test_match_rules_update_properties (void)
{
  static const gchar * const rules_json_string =
      "["
      "  {"
      "    matches = ["
      "      {"
      "        device.name = \"~alsa_card.*\""
      "      }"
      "    ]"
      "    actions = {"
      "      update-props = {"
      "        api.alsa.use-acp = true"
      "        api.acp.auto-port = false"
      "      }"
      "    }"
      "  }"
      "  {"
      "    matches = ["
      "      {"
      "        node.name = \"alsa_output.0.my-alsa-device\""
      "      }"
      "    ]"
      "    actions = {"
      "      update-props = {"
      "        audio.rate = 96000"
      "        node.description = \"My ALSA Node\""
      "        media.class = null"
      "      }"
      "    }"
      "  }"
      "]";

  g_autoptr (WpSpaJson) rules = wp_spa_json_new_wrap_stringn (rules_json_string,
      strlen (rules_json_string));
  g_assert_nonnull (rules);

  /* Unmatched */
  {
    g_autoptr (WpProperties) match_props = NULL;

    match_props = wp_properties_new (
        "device.name", "unmatched-device-name",
        NULL);

    g_assert_cmpint (wp_json_utils_match_rules_update_properties (rules, match_props), ==, 0);
  }

  /* Match regex with props filled */
  {
    g_autoptr (WpProperties) match_props = NULL;
    const gchar *str = NULL;

    match_props = wp_properties_new (
        "device.name", "alsa_card_0.my-alsa-device",
        NULL);
    g_assert_nonnull (match_props);

    str = wp_properties_get (match_props, "device.name");
    g_assert_cmpstr (str, ==, "alsa_card_0.my-alsa-device");
    str = wp_properties_get (match_props, "api.alsa.use-acp");
    g_assert_null (str);
    str = wp_properties_get (match_props, "api.acp.auto-port");
    g_assert_null (str);

    g_assert_cmpint (wp_json_utils_match_rules_update_properties (rules, match_props), ==, 2);

    str = wp_properties_get (match_props, "device.name");
    g_assert_cmpstr (str, ==, "alsa_card_0.my-alsa-device");
    str = wp_properties_get (match_props, "api.alsa.use-acp");
    g_assert_cmpstr (str, ==, "true");
    str = wp_properties_get (match_props, "api.acp.auto-port");
    g_assert_cmpstr (str, ==, "false");
  }

  /* Match equal with props filled */
  {
    g_autoptr (WpProperties) match_props = NULL;
    const gchar *str = NULL;

    match_props = wp_properties_new (
        "node.name", "alsa_output.0.my-alsa-device",
        NULL);
    g_assert_nonnull (match_props);

    str = wp_properties_get (match_props, "node.name");
    g_assert_cmpstr (str, ==, "alsa_output.0.my-alsa-device");
    str = wp_properties_get (match_props, "audio.rate");
    g_assert_null (str);
    str = wp_properties_get (match_props, "node.description");
    g_assert_null (str);
    str = wp_properties_get (match_props, "media.class");
    g_assert_null (str);

    g_assert_cmpint (wp_json_utils_match_rules_update_properties (rules, match_props), ==, 2);

    str = wp_properties_get (match_props, "node.name");
    g_assert_cmpstr (str, ==, "alsa_output.0.my-alsa-device");
    str = wp_properties_get (match_props, "audio.rate");
    g_assert_cmpstr (str, ==, "96000");
    str = wp_properties_get (match_props, "node.description");
    g_assert_cmpstr (str, ==, "My ALSA Node");
    str = wp_properties_get (match_props, "media.class");
    g_assert_null (str);
  }

  /* Match equal with 1 prop updated */
  {
    g_autoptr (WpProperties) match_props = NULL;
    const gchar *str = NULL;

    match_props = wp_properties_new (
        "node.name", "alsa_output.0.my-alsa-device",
        "audio.rate", "96000",
        "node.description", "Test",
        NULL);
    g_assert_nonnull (match_props);

    str = wp_properties_get (match_props, "node.name");
    g_assert_cmpstr (str, ==, "alsa_output.0.my-alsa-device");
    str = wp_properties_get (match_props, "audio.rate");
    g_assert_cmpstr (str, ==, "96000");
    str = wp_properties_get (match_props, "node.description");
    g_assert_cmpstr (str, ==, "Test");
    str = wp_properties_get (match_props, "media.class");
    g_assert_null (str);

    g_assert_cmpint (wp_json_utils_match_rules_update_properties (rules, match_props), ==, 1);

    str = wp_properties_get (match_props, "node.name");
    g_assert_cmpstr (str, ==, "alsa_output.0.my-alsa-device");
    str = wp_properties_get (match_props, "audio.rate");
    g_assert_cmpstr (str, ==, "96000");
    str = wp_properties_get (match_props, "node.description");
    g_assert_cmpstr (str, ==, "My ALSA Node");
    str = wp_properties_get (match_props, "media.class");
    g_assert_null (str);
  }

  /* Match equal with prop deleted */
  {
    g_autoptr (WpProperties) match_props = NULL;
    const gchar *str = NULL;

    match_props = wp_properties_new (
        "node.name", "alsa_output.0.my-alsa-device",
        "media.class", "Audio/Sink",
        "audio.rate", "48000",
        "node.description", "Test",
        NULL);
    g_assert_nonnull (match_props);

    str = wp_properties_get (match_props, "node.name");
    g_assert_cmpstr (str, ==, "alsa_output.0.my-alsa-device");
    str = wp_properties_get (match_props, "audio.rate");
    g_assert_cmpstr (str, ==, "48000");
    str = wp_properties_get (match_props, "node.description");
    g_assert_cmpstr (str, ==, "Test");
    str = wp_properties_get (match_props, "media.class");
    g_assert_cmpstr (str, ==, "Audio/Sink");

    g_assert_cmpint (wp_json_utils_match_rules_update_properties (rules, match_props), ==, 3);

    str = wp_properties_get (match_props, "node.name");
    g_assert_cmpstr (str, ==, "alsa_output.0.my-alsa-device");
    str = wp_properties_get (match_props, "audio.rate");
    g_assert_cmpstr (str, ==, "96000");
    str = wp_properties_get (match_props, "node.description");
    g_assert_cmpstr (str, ==, "My ALSA Node");
    str = wp_properties_get (match_props, "media.class");
    g_assert_null (str);
  }
}

static gboolean
match_rules_cb (gpointer data, const gchar * action, WpSpaJson * value,
    GError ** error)
{
  WpProperties *match_props = data;

  if (g_str_equal (action, "update-props")) {
    wp_properties_update_from_json (match_props, value);
  }
  else if (g_str_equal (action, "set-answer")) {
    g_autofree gchar *str = wp_spa_json_to_string (value);
    wp_properties_set (match_props, "answer.universe", str);
  }
  else if (g_str_equal (action, "generate-error")) {
    g_autofree gchar *str = wp_spa_json_parse_string (value);
    g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
        "error: %s", str);
    return FALSE;
  }
  else if (g_str_equal (action, "set-description")) {
    g_autofree gchar *str = wp_spa_json_parse_string (value);
    wp_properties_set (match_props, "device.description", str);
  }

  return TRUE;
}

static void
test_match_rules (void)
{
  static const gchar * const rules_json_string =
      "["
      "  {"
      "    matches = ["
      "      {"
      "        device.name = \"~alsa_card.*\""
      "      }"
      "    ]"
      "    actions = {"
      "      update-props = {"
      "        device.name = alsa_card.1"
      "        api.acp.auto-port = false"
      "      }"
      "      set-answer = 42"
      "    }"
      "  }"
      "  {"
      "    matches = ["
      "      {"
      "        test.error = true"
      "      }"
      "    ]"
      "    actions = {"
      "      generate-error = \"test.error is true\""
      "    }"
      "  }"
      "  {"
      "    matches = ["
      "      {"
      "        device.name = \"alsa_card.1\""
      "      }"
      "    ]"
      "    actions = {"
      "      set-description = \"My ALSA Device\""
      "    }"
      "  }"
      "]";

  g_autoptr (WpSpaJson) rules = wp_spa_json_new_wrap_stringn (rules_json_string,
      strlen (rules_json_string));
  g_assert_nonnull (rules);

  /* no error */
  {
    g_autoptr (GError) error = NULL;
    g_autoptr (WpProperties) match_props = NULL;
    const gchar *str = NULL;

    match_props = wp_properties_new (
        "device.name", "alsa_card.0",
        "test.error", "false",
        NULL);

    str = wp_properties_get (match_props, "device.name");
    g_assert_cmpstr (str, ==, "alsa_card.0");
    str = wp_properties_get (match_props, "api.acp.auto-port");
    g_assert_null (str);
    str = wp_properties_get (match_props, "answer.universe");
    g_assert_null (str);
    str = wp_properties_get (match_props, "test.error");
    g_assert_cmpstr (str, ==, "false");
    str = wp_properties_get (match_props, "device.description");
    g_assert_null (str);

    g_assert_true (wp_json_utils_match_rules (rules, match_props, match_rules_cb,
        match_props, &error));
    g_assert_no_error (error);

    str = wp_properties_get (match_props, "device.name");
    g_assert_cmpstr (str, ==, "alsa_card.1");
    str = wp_properties_get (match_props, "api.acp.auto-port");
    g_assert_cmpstr (str, ==, "false");
    str = wp_properties_get (match_props, "answer.universe");
    g_assert_cmpstr (str, ==, "42");
    str = wp_properties_get (match_props, "test.error");
    g_assert_cmpstr (str, ==, "false");
    str = wp_properties_get (match_props, "device.description");
    g_assert_cmpstr (str, ==, "My ALSA Device");
  }

  /* with error */
  {
    g_autoptr (GError) error = NULL;
    g_autoptr (WpProperties) match_props = NULL;
    const gchar *str = NULL;

    match_props = wp_properties_new (
        "device.name", "alsa_card.256",
        "test.error", "true",
        NULL);

    str = wp_properties_get (match_props, "device.name");
    g_assert_cmpstr (str, ==, "alsa_card.256");
    str = wp_properties_get (match_props, "api.acp.auto-port");
    g_assert_null (str);
    str = wp_properties_get (match_props, "answer.universe");
    g_assert_null (str);
    str = wp_properties_get (match_props, "test.error");
    g_assert_cmpstr (str, ==, "true");
    str = wp_properties_get (match_props, "device.description");
    g_assert_null (str);

    g_assert_false (wp_json_utils_match_rules (rules, match_props, match_rules_cb,
        match_props, &error));
    g_assert_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED);
    g_assert_cmpstr (error->message, ==, "error: test.error is true");

    str = wp_properties_get (match_props, "device.name");
    g_assert_cmpstr (str, ==, "alsa_card.1");
    str = wp_properties_get (match_props, "api.acp.auto-port");
    g_assert_cmpstr (str, ==, "false");
    str = wp_properties_get (match_props, "answer.universe");
    g_assert_cmpstr (str, ==, "42");
    str = wp_properties_get (match_props, "test.error");
    g_assert_cmpstr (str, ==, "true");
    str = wp_properties_get (match_props, "device.description");
    g_assert_null (str);
  }
}

gint
main (gint argc, gchar *argv[])
{
  g_test_init (&argc, &argv, NULL);
  g_log_set_writer_func (wp_log_writer_default, NULL, NULL);

  g_test_add_func ("/wp/json-utils/match_rules_update_props",
      test_match_rules_update_properties);
  g_test_add_func ("/wp/json-utils/match_rules", test_match_rules);

  return g_test_run ();
}
