-- WirePlumber
--
-- Copyright © 2021-2022 Collabora Ltd.
--    @author George Kiagiadakis <george.kiagiadakis@collabora.com>
--
-- Based on default-routes.c from pipewire-media-session
-- Copyright © 2020 Wim Taymans
--
-- SPDX-License-Identifier: MIT
--
-- find the best route for a given device_id, based on availability and priority

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

SimpleEventHook {
  name = "device/find-best-routes",
  after = "device/find-stored-routes",
  interests = {
    EventInterest {
      Constraint { "event.type", "=", "select-routes" },
      Constraint { "profile.active-device-ids", "is-present" },
    },
  },
  execute = function (event)
    local device = event:get_subject ()
    local event_properties = event:get_properties ()
    local active_ids = event_properties ["profile.active-device-ids"]
    local selected_routes = event:get_data ("selected-routes") or Properties()

    local dev_info = devinfo:get_device_info (device)
    assert (dev_info)

    -- active IDs are exchanged in JSON format
    active_ids = Json.Raw (active_ids):parse ()

    for _, device_id in ipairs (active_ids) do
      -- if a previous hook already selected a route for this device_id, skip it
      if selected_routes [tostring (device_id)] then
        goto next_device_id
      end

      local best_avail = nil
      local best_unk = nil
      for _, ri in pairs (dev_info.route_infos) do
        if cutils.arrayContains (ri.devices, device_id) and
              (ri.profiles == nil or cutils.arrayContains (ri.profiles, dev_info.active_profile)) then
          if ri.available == "yes" or ri.available == "unknown" then
            if ri.direction == "Output" and ri.available ~= ri.prev_available then
              best_avail = ri
              ri.save = true
              break
            elseif ri.available == "yes" then
              if (best_avail == nil or ri.priority > best_avail.priority) then
                best_avail = ri
              end
            elseif best_unk == nil or ri.priority > best_unk.priority then
              best_unk = ri
            end
          end
        end
      end

      local route = best_avail or best_unk
      if route then
        selected_routes [tostring (device_id)] =
            Json.Object { index = route.index }:to_string ()
      end

      ::next_device_id::
    end

    -- save the selected routes for the apply-routes hook
    event:set_data ("selected-routes", selected_routes)
  end
}:register ()
