-- WirePlumber
--
-- Copyright © 2024 Collabora Ltd.
--
-- SPDX-License-Identifier: MIT
--
-- Finds the user preferred profile for a device, based on the priorities
-- defined in the "device.profile.priority.rules" section of the configuration.

cutils = require ("common-utils")
log = Log.open_topic ("s-device")

config = {}
config.rules = Conf.get_section_as_json ("device.profile.priority.rules", Json.Array {})

function getRulesProfilePriorities (device)
  local props = JsonUtils.match_rules_update_properties (config.rules,
    device.properties)
  local p_array = props["priorities"]
  if not p_array then
    return nil
  end

  local p_json = Json.Raw (p_array)
  return p_json:parse ()
end

function getPreferredBluetoothProfilePriorities (device)
  if device.properties["device.api"] ~= "bluez5" then
    return nil
  end

  local preference = Settings.get_string ("bluetooth.profile-preference")
  if preference == "latency" then
    log:info (device, "using best latency profile")
    return { "a2dp-auto-prefer-latency" }
  elseif preference == "quality" then
    log:info (device, "using best quality profile")
    return { "a2dp-auto-prefer-quality" }
  end

  log:warning (device, "invalid preference value '" .. preference ..
      "'. Defaulting to best quality profile")
  return { "a2dp-auto-prefer-quality" }
end

SimpleEventHook {
  name = "device/find-preferred-profile",
  after = "device/find-stored-profile",
  before = "device/find-best-profile",
  interests = {
    EventInterest {
      Constraint { "event.type", "=", "select-profile" },
    },
  },
  execute = function (event)
    local selected_profile = event:get_data ("selected-profile")

    -- skip hook if the profile is already selected for this device.
    if selected_profile then
      return
    end

    local device = event:get_subject ()
    local device_name = device.properties["device.name"] or ""

    -- Use device priority rules if any. Otherwise, get the preferred quality or
    -- latency priorities if BT device.
    local priorities = getRulesProfilePriorities (device)
    if priorities == nil then
      priorities = getPreferredBluetoothProfilePriorities (device)
    end
    if priorities == nil then
      log:info (device, string.format (
          "Preferred profile priorities not available for device '%s'",
          device_name))
      return
    end

    -- Find the preferred profile
    for _, priority_profile in ipairs(priorities) do
      for p in device:iterate_params("EnumProfile") do
        local device_profile = cutils.parseParam(p, "EnumProfile")
        if device_profile and device_profile.name == priority_profile and
            device_profile.available ~= "no" then
          selected_profile = device_profile
          goto profile_set
        end
      end
    end

::profile_set::
    if selected_profile then
      log:info (device, string.format (
        "Found preferred profile '%s' (%d) for device '%s'",
        selected_profile.name, selected_profile.index, device_name))
      event:set_data ("selected-profile", selected_profile)
    else
      log:info (device, string.format (
          "Could not find preferred profile for device '%s'", device_name))
    end

  end
}:register()
