// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// Device metadata and description types.
///
/// Ported from upstream `cpal/src/device_description.rs`.

///|
pub enum DeviceType {
  Speaker
  Microphone
  Headphones
  Headset
  Earpiece
  Handset
  HearingAid
  Dock
  Tuner
  Virtual
  Unknown
} derive(Debug, Eq)

///|
pub fn DeviceType::speaker() -> DeviceType {
  Speaker
}

///|
pub fn DeviceType::microphone() -> DeviceType {
  Microphone
}

///|
pub fn DeviceType::headphones() -> DeviceType {
  Headphones
}

///|
pub fn DeviceType::headset() -> DeviceType {
  Headset
}

///|
pub fn DeviceType::earpiece() -> DeviceType {
  Earpiece
}

///|
pub fn DeviceType::handset() -> DeviceType {
  Handset
}

///|
pub fn DeviceType::hearing_aid() -> DeviceType {
  HearingAid
}

///|
pub fn DeviceType::dock() -> DeviceType {
  Dock
}

///|
pub fn DeviceType::tuner() -> DeviceType {
  Tuner
}

///|
pub fn DeviceType::virtual_() -> DeviceType {
  Virtual
}

///|
pub fn DeviceType::unknown() -> DeviceType {
  Unknown
}

///|
pub enum InterfaceType {
  BuiltIn
  Usb
  Bluetooth
  Pci
  FireWire
  Thunderbolt
  Hdmi
  Line
  Spdif
  Network
  Virtual
  DisplayPort
  Aggregate
  Unknown
} derive(Debug, Eq)

///|
pub fn InterfaceType::built_in() -> InterfaceType {
  BuiltIn
}

///|
pub fn InterfaceType::usb() -> InterfaceType {
  Usb
}

///|
pub fn InterfaceType::bluetooth() -> InterfaceType {
  Bluetooth
}

///|
pub fn InterfaceType::pci() -> InterfaceType {
  Pci
}

///|
pub fn InterfaceType::fire_wire() -> InterfaceType {
  FireWire
}

///|
pub fn InterfaceType::thunderbolt() -> InterfaceType {
  Thunderbolt
}

///|
pub fn InterfaceType::hdmi() -> InterfaceType {
  Hdmi
}

///|
pub fn InterfaceType::line() -> InterfaceType {
  Line
}

///|
pub fn InterfaceType::spdif() -> InterfaceType {
  Spdif
}

///|
pub fn InterfaceType::network() -> InterfaceType {
  Network
}

///|
pub fn InterfaceType::virtual_() -> InterfaceType {
  Virtual
}

///|
pub fn InterfaceType::display_port() -> InterfaceType {
  DisplayPort
}

///|
pub fn InterfaceType::aggregate() -> InterfaceType {
  Aggregate
}

///|
pub fn InterfaceType::unknown() -> InterfaceType {
  Unknown
}

///|
pub enum DeviceDirection {
  Input
  Output
  Duplex
  Unknown
} derive(Debug, Eq)

///|
pub fn DeviceDirection::input() -> DeviceDirection {
  Input
}

///|
pub fn DeviceDirection::output() -> DeviceDirection {
  Output
}

///|
pub fn DeviceDirection::duplex() -> DeviceDirection {
  Duplex
}

///|
pub fn DeviceDirection::unknown() -> DeviceDirection {
  Unknown
}

///|
pub struct DeviceDescription {
  name : String
  manufacturer : String?
  driver : String?
  device_type : DeviceType
  interface_type : InterfaceType
  direction : DeviceDirection
  address : String?
  extended : Array[String]
} derive(Debug, Eq)

///|
pub fn DeviceDescription::DeviceDescription(
  name : String,
  manufacturer? : String? = None,
  driver? : String? = None,
  device_type? : DeviceType = Unknown,
  interface_type? : InterfaceType = Unknown,
  direction? : DeviceDirection = Unknown,
  address? : String? = None,
  extended? : Array[String] = [],
) -> DeviceDescription {
  {
    name,
    manufacturer,
    driver,
    device_type,
    interface_type,
    direction,
    address,
    extended,
  }
}

///|
pub fn DeviceDescription::new(
  name : String,
  manufacturer? : String? = None,
  driver? : String? = None,
  device_type? : DeviceType = Unknown,
  interface_type? : InterfaceType = Unknown,
  direction? : DeviceDirection = Unknown,
  address? : String? = None,
  extended? : Array[String] = [],
) -> DeviceDescription {
  DeviceDescription(
    name,
    manufacturer~,
    driver~,
    device_type~,
    interface_type~,
    direction~,
    address~,
    extended~,
  )
}

///|
pub fn DeviceDescription::name(self : DeviceDescription) -> String {
  self.name
}

///|
pub fn DeviceDescription::manufacturer(self : DeviceDescription) -> String? {
  self.manufacturer
}

///|
pub fn DeviceDescription::driver(self : DeviceDescription) -> String? {
  self.driver
}

///|
pub fn DeviceDescription::device_type(self : DeviceDescription) -> DeviceType {
  self.device_type
}

///|
pub fn DeviceDescription::interface_type(
  self : DeviceDescription,
) -> InterfaceType {
  self.interface_type
}

///|
pub fn DeviceDescription::direction(
  self : DeviceDescription,
) -> DeviceDirection {
  self.direction
}

///|
pub fn DeviceDescription::supports_input(self : DeviceDescription) -> Bool {
  match self.direction {
    Input | Duplex => true
    _ => false
  }
}

///|
pub fn DeviceDescription::supports_output(self : DeviceDescription) -> Bool {
  match self.direction {
    Output | Duplex => true
    _ => false
  }
}

///|
pub fn DeviceDescription::address(self : DeviceDescription) -> String? {
  self.address
}

///|
pub fn DeviceDescription::extended(self : DeviceDescription) -> Array[String] {
  self.extended
}

///|
pub fn DeviceType::to_string(self : DeviceType) -> String {
  match self {
    Speaker => "Speaker"
    Microphone => "Microphone"
    Headphones => "Headphones"
    Headset => "Headset"
    Earpiece => "Earpiece"
    Handset => "Handset"
    HearingAid => "Hearing Aid"
    Dock => "Dock"
    Tuner => "Tuner"
    Virtual => "Virtual"
    Unknown => "Unknown"
  }
}

///|
pub fn InterfaceType::to_string(self : InterfaceType) -> String {
  match self {
    BuiltIn => "Built-in"
    Usb => "USB"
    Bluetooth => "Bluetooth"
    Pci => "PCI"
    FireWire => "FireWire"
    Thunderbolt => "Thunderbolt"
    Hdmi => "HDMI"
    Line => "Line"
    Spdif => "S/PDIF"
    Network => "Network"
    Virtual => "Virtual"
    DisplayPort => "DisplayPort"
    Aggregate => "Aggregate"
    Unknown => "Unknown"
  }
}

///|
pub fn DeviceDirection::to_string(self : DeviceDirection) -> String {
  match self {
    Input => "Input"
    Output => "Output"
    Duplex => "Duplex"
    Unknown => "Unknown"
  }
}

///|
pub fn DeviceDescription::to_string(self : DeviceDescription) -> String {
  let sb = StringBuilder::new()
  sb.write_string(self.name)
  match self.manufacturer {
    None => ()
    Some(m) => sb.write_string(" (\{m})")
  }
  if self.device_type != Unknown {
    sb.write_string(" [\{self.device_type.to_string()}]")
  }
  if self.interface_type != Unknown {
    sb.write_string(" via \{self.interface_type.to_string()}")
  }
  sb.to_string()
}

///|
/// Builder for constructing a `DeviceDescription`.
///
/// This is primarily intended for backend implementations to gradually build up
/// descriptions with whatever metadata is available.
pub struct DeviceDescriptionBuilder {
  name : String
  manufacturer : String?
  driver : String?
  device_type : DeviceType
  interface_type : InterfaceType
  direction : DeviceDirection
  address : String?
  extended : Array[String]
} derive(Debug, Eq)

///|
pub fn DeviceDescriptionBuilder::DeviceDescriptionBuilder(
  name : String,
) -> DeviceDescriptionBuilder {
  {
    name,
    manufacturer: None,
    driver: None,
    device_type: Unknown,
    interface_type: Unknown,
    direction: Unknown,
    address: None,
    extended: [],
  }
}

///|
pub fn DeviceDescriptionBuilder::new(name : String) -> DeviceDescriptionBuilder {
  DeviceDescriptionBuilder(name)
}

///|
pub fn DeviceDescriptionBuilder::manufacturer(
  self : DeviceDescriptionBuilder,
  manufacturer : String,
) -> DeviceDescriptionBuilder {
  {
    name: self.name,
    manufacturer: Some(manufacturer),
    driver: self.driver,
    device_type: self.device_type,
    interface_type: self.interface_type,
    direction: self.direction,
    address: self.address,
    extended: self.extended,
  }
}

///|
pub fn DeviceDescriptionBuilder::driver(
  self : DeviceDescriptionBuilder,
  driver : String,
) -> DeviceDescriptionBuilder {
  {
    name: self.name,
    manufacturer: self.manufacturer,
    driver: Some(driver),
    device_type: self.device_type,
    interface_type: self.interface_type,
    direction: self.direction,
    address: self.address,
    extended: self.extended,
  }
}

///|
pub fn DeviceDescriptionBuilder::device_type(
  self : DeviceDescriptionBuilder,
  device_type : DeviceType,
) -> DeviceDescriptionBuilder {
  {
    name: self.name,
    manufacturer: self.manufacturer,
    driver: self.driver,
    device_type,
    interface_type: self.interface_type,
    direction: self.direction,
    address: self.address,
    extended: self.extended,
  }
}

///|
pub fn DeviceDescriptionBuilder::interface_type(
  self : DeviceDescriptionBuilder,
  interface_type : InterfaceType,
) -> DeviceDescriptionBuilder {
  {
    name: self.name,
    manufacturer: self.manufacturer,
    driver: self.driver,
    device_type: self.device_type,
    interface_type,
    direction: self.direction,
    address: self.address,
    extended: self.extended,
  }
}

///|
pub fn DeviceDescriptionBuilder::direction(
  self : DeviceDescriptionBuilder,
  direction : DeviceDirection,
) -> DeviceDescriptionBuilder {
  {
    name: self.name,
    manufacturer: self.manufacturer,
    driver: self.driver,
    device_type: self.device_type,
    interface_type: self.interface_type,
    direction,
    address: self.address,
    extended: self.extended,
  }
}

///|
pub fn DeviceDescriptionBuilder::address(
  self : DeviceDescriptionBuilder,
  address : String,
) -> DeviceDescriptionBuilder {
  {
    name: self.name,
    manufacturer: self.manufacturer,
    driver: self.driver,
    device_type: self.device_type,
    interface_type: self.interface_type,
    direction: self.direction,
    address: Some(address),
    extended: self.extended,
  }
}

///|
pub fn DeviceDescriptionBuilder::extended(
  self : DeviceDescriptionBuilder,
  lines : Array[String],
) -> DeviceDescriptionBuilder {
  {
    name: self.name,
    manufacturer: self.manufacturer,
    driver: self.driver,
    device_type: self.device_type,
    interface_type: self.interface_type,
    direction: self.direction,
    address: self.address,
    extended: lines,
  }
}

///|
pub fn DeviceDescriptionBuilder::add_extended_line(
  self : DeviceDescriptionBuilder,
  line : String,
) -> DeviceDescriptionBuilder {
  let xs = self.extended.copy()
  xs.push(line)
  {
    name: self.name,
    manufacturer: self.manufacturer,
    driver: self.driver,
    device_type: self.device_type,
    interface_type: self.interface_type,
    direction: self.direction,
    address: self.address,
    extended: xs,
  }
}

///|
pub fn DeviceDescriptionBuilder::build(
  self : DeviceDescriptionBuilder,
) -> DeviceDescription {
  DeviceDescription(
    self.name,
    manufacturer=self.manufacturer,
    driver=self.driver,
    device_type=self.device_type,
    interface_type=self.interface_type,
    direction=self.direction,
    address=self.address,
    extended=self.extended,
  )
}

///|
pub fn direction_from_caps(
  has_input : Bool,
  has_output : Bool,
) -> DeviceDirection {
  match (has_input, has_output) {
    (true, true) => Duplex
    (true, false) => Input
    (false, true) => Output
    (false, false) => Unknown
  }
}

///|
pub fn direction_from_counts(
  input_channels : ChannelCount?,
  output_channels : ChannelCount?,
) -> DeviceDirection {
  let has_input = match input_channels {
    None => false
    Some(n) => n > 0
  }
  let has_output = match output_channels {
    None => false
    Some(n) => n > 0
  }
  direction_from_caps(has_input, has_output)
}