-- As explained on: https://bennett.dev/auto-link-pipewire-ports-wireplumber/
--
-- This script automatically connects the audio device specified to liquidsoap.
-- Thanks to Bennett Hardwick <me@bennetthardwick.com> 

-- Link two ports together
function link_port(output_port, input_port)
  if not input_port or not output_port then
    return false
  end

  local link_args = {
    ["link.input.node"] = input_port.properties["node.id"],
    ["link.input.port"] = input_port.properties["object.id"],

    ["link.output.node"] = output_port.properties["node.id"],
    ["link.output.port"] = output_port.properties["object.id"],
    
    -- The node never got created if it didn't have this field set to something
    ["object.id"] = nil,

    -- I was running into issues when I didn't have this set
    ["object.linger"] = true,

    ["node.description"] = "AURA Link created by auto_connect_ports"
  }

  local link = Link("link-factory", link_args)
  link:activate(1)

  return true
end

function delete_link(link_om, output_port, input_port)
  print("Trying to delete")

  if not input_port or not output_port then
    print("No ports")
    return false
  end

  local link = link_om:lookup {
    Constraint {
      "link.input.node", "equals", input_port.properties["node.id"]
    },
    Constraint {
      "link.input.port", "equals", input_port.properties["object.id"],
    },
    Constraint {
      "link.output.node", "equals", output_port.properties["node.id"],
    },
    Constraint {
      "link.output.port", "equals", output_port.properties["object.id"],
    }
  }

  if not link then

    print("No link!")

    return
  end

  print("Deleting link!")

  link:request_destroy()
end

-- Automatically link ports together by their specific audio channels.
--
-- ┌──────────────────┐         ┌───────────────────┐
-- │                  │         │                   │
-- │               FL ├────────►│ AUX0              │
-- │      OUTPUT      │         │                   │
-- │               FR ├────────►│ AUX1  INPUT       │
-- │                  │         │                   │
-- └──────────────────┘         │ AUX2              │
--                              │                   │
--                              └───────────────────┘
-- 
-- -- Call this method inside a script in global scope
--
-- auto_connect_ports {
--
--   -- A constraint for all the required ports of the output device 
--   output = Constraint { "node.name"}
--
--   -- A constraint for all the required ports of the input device 
--   input = Constraint { .. }
--
--   -- A mapping of output audio channels to input audio channels
--
--   connections = {
--     ["FL"] = "AUX0"
--     ["FR"] = "AUX1"
--   }
--
-- }
--
function auto_connect_ports(args)
  local output_om = ObjectManager {
    Interest {
      type = "port",
      args["output"],
      Constraint { "port.direction", "equals", "out" }
    }
  }

  local input_om = ObjectManager {
    Interest {
      type = "port",
      args["input"],
      Constraint { "port.direction", "equals", "in" }
    }
  }

  local all_links = ObjectManager {
    Interest {
      type = "link",
    }
  }

  local unless = nil

  if args["unless"] then
    unless = ObjectManager {
      Interest {
        type = "port",
        args["unless"],
        Constraint { "port.direction", "equals", "in" }
      }
    }

  end

  -- handle the connect argument
  function _connect()
    local delete_links = unless and unless:get_n_objects() > 0

    for output_name, input_name in pairs(args.connect) do
      local output = output_om:lookup { Constraint { "port.name", "matches", string.format("*%s", output_name) }}
      local input =  input_om:lookup { Constraint { "port.name", "matches", string.format("*%s", input_name) }}

      if delete_links then
        delete_link(all_links, output, input)
      else
        link_port(output, input)
      end
    end
  end

  output_om:connect("object-added", _connect)
  input_om:connect("object-added", _connect)
  all_links:connect("object-added", _connect)

  output_om:activate()
  input_om:activate()
  all_links:activate()

  if unless then
    unless:connect("object-added", _connect)
    unless:connect("object-removed", _connect)
    unless:activate()
  end
end

-- 
-- Fill in your port.alias and port.name
--

-- Auto connect Yamaha sink to the first two channels of the Yamaha
local audio_source_input = os.getenv("AURA_ENGINE_INPUT_DEVICE")
local audio_input_channel_left = os.getenv("AURA_ENGINE_INPUT_CHANNEL_LEFT")
local audio_input_channel_right = os.getenv("AURA_ENGINE_INPUT_CHANNEL_RIGHT")
local audio_source_output = os.getenv("AURA_ENGINE_OUTPUT_DEVICE")
local audio_output_channel_left = os.getenv("AURA_ENGINE_OUTPUT_CHANNEL_LEFT")
local audio_output_channel_right = os.getenv("AURA_ENGINE_OUTPUT_CHANNEL_RIGHT")

-- print(string.format("%s_*", audio_source_input))
auto_connect_ports {
  output = Constraint { "port.alias", "matches", audio_source_input .. "_*" },
  input = Constraint { "port.alias", "matches", "in_line_0:*" },
  connect = {
    [audio_input_channel_left] = "in_0",
    [audio_input_channel_right] = "in_1"
  }
}
auto_connect_ports {
  output = Constraint { "port.alias", "matches", "lineout_0:*" },
  input = Constraint { "port.alias", "matches", audio_source_output .. "_*" },
  connect = {
    ["out_0"] = audio_output_channel_left,
    ["out_1"] = audio_output_channel_right
  }
}