use std::fmt;

/// All distinct unit types used in KNX datapoint types.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Unit {
    // Electrical
    Ampere,
    Milliampere,
    Millivolt,
    Volt,
    VoltPerKelvin,
    VoltPerMeter,
    VoltAmpere,
    VoltAmpereHour,
    VoltAmpereReactiveHour,
    Watt,
    WattPerSquareMeter,
    WattPerMeterKelvin,
    WattHour,
    Kilowatt,
    KilowattHour,
    KilovoltAmpereHour,
    KilovoltAmpereReactiveHour,
    MegawattHour,
    Ohm,
    OhmMeter,
    Siemens,
    SiemensPerMeter,
    Weber,
    Tesla,
    Henry,
    Farad,

    // Thermal / Temperature
    DegreeCelsius,
    DegreeFahrenheit,
    Kelvin,
    KelvinPerPercent,
    KelvinPerHour,

    // Mechanical / Force / Pressure
    Newton,
    NewtonPerMeter,
    NewtonPerSecond,
    NewtonMeter,
    Pascal,
    Joule,
    JoulePerKelvin,
    JoulePerMol,
    JouleSecond,

    // Motion / Speed
    MeterPerSecond,
    MeterPerSecondSquared,
    KilometerPerHour,
    RadianPerSecond,
    RadianPerSecondSquared,

    // Length / Area / Volume
    Meter,
    Millimeter,
    SquareMeter,
    SquareMeterPerNewton,
    CubicMeter,
    CubicMeterPerHour,
    CubicMeterPerSecond,
    Liter,
    LiterPerHour,
    LiterPerSquareMeter,
    LiterPerSecond,

    // Mass / Density
    Kilogram,
    KilogramPerCubicMeter,
    KilogramPerSecond,
    GramPerCubicMeter,
    MicrogramPerCubicMeter,

    // Light
    Lux,
    Lumen,
    LumenSecond,
    Candela,
    CandelaPerSquareMeter,

    // Time
    Second,
    Millisecond,
    Minute,
    Hour,

    // Frequency
    Hertz,
    InverseSecond,

    // Angular
    Degree,
    Radian,
    Steradian,

    // Magnetic
    AmperePerMeter,
    AmperePerSquareMeter,
    AmpereSquareMeter,

    // Electric field / Charge
    Coulomb,
    CoulombPerSquareMeter,
    CoulombPerCubicMeter,
    CoulombMeter,

    // Chemical / Environmental
    Mol,
    PartsPerMillion,
    Percent,

    // Misc / Dimensionless
    SpeedOfLight,
    CounterPulses,
    Pulses,
}

impl Unit {
    #[must_use]
    pub fn symbol(&self) -> &'static str {
        match self {
            Self::Percent => "%",
            Self::Ampere => "A",
            Self::AmperePerMeter => "A/m",
            Self::AmperePerSquareMeter => "A/m²",
            Self::AmpereSquareMeter => "A m²",
            Self::Coulomb => "C",
            Self::CoulombPerSquareMeter => "C/m²",
            Self::CoulombPerCubicMeter => "C/m³",
            Self::CoulombMeter => "C m",
            Self::Farad => "F",
            Self::Henry => "H",
            Self::Hertz => "Hz",
            Self::Joule => "J",
            Self::JoulePerKelvin => "J/K",
            Self::JoulePerMol => "J/mol",
            Self::JouleSecond => "J s",
            Self::Kelvin => "K",
            Self::KelvinPerPercent => "K/%",
            Self::KelvinPerHour => "K/h",
            Self::Liter => "L",
            Self::LiterPerHour => "L/h",
            Self::LiterPerSquareMeter => "L/m²",
            Self::LiterPerSecond => "L/s",
            Self::MegawattHour => "MWh",
            Self::Newton => "N",
            Self::NewtonPerMeter => "N/m",
            Self::NewtonPerSecond => "N/s",
            Self::NewtonMeter => "Nm",
            Self::Pascal => "Pa",
            Self::Siemens => "S",
            Self::SiemensPerMeter => "S/m",
            Self::Tesla => "T",
            Self::Volt => "V",
            Self::VoltPerKelvin => "V/K",
            Self::VoltPerMeter => "V/m",
            Self::VoltAmpere => "VA",
            Self::VoltAmpereHour => "VAh",
            Self::VoltAmpereReactiveHour => "VARh",
            Self::Watt => "W",
            Self::WattPerSquareMeter => "W/m²",
            Self::WattPerMeterKelvin => "W/mK",
            Self::Weber => "Wb",
            Self::WattHour => "Wh",
            Self::SpeedOfLight => "c",
            Self::Candela => "cd",
            Self::CandelaPerSquareMeter => "cd/m²",
            Self::CounterPulses => "counter pulses",
            Self::GramPerCubicMeter => "g/m³",
            Self::Hour => "h",
            Self::KilovoltAmpereHour => "kVAh",
            Self::KilovoltAmpereReactiveHour => "kVARh",
            Self::Kilowatt => "kW",
            Self::KilowattHour => "kWh",
            Self::Kilogram => "kg",
            Self::KilogramPerCubicMeter => "kg/m³",
            Self::KilogramPerSecond => "kg/s",
            Self::KilometerPerHour => "km/h",
            Self::Lumen => "lm",
            Self::LumenSecond => "lm s",
            Self::Lux => "lx",
            Self::Meter => "m",
            Self::MeterPerSecond => "m/s",
            Self::MeterPerSecondSquared => "m/s²",
            Self::Milliampere => "mA",
            Self::Millivolt => "mV",
            Self::Minute => "min",
            Self::Millimeter => "mm",
            Self::Mol => "mol",
            Self::Millisecond => "ms",
            Self::SquareMeter => "m²",
            Self::SquareMeterPerNewton => "m²/N",
            Self::CubicMeter => "m³",
            Self::CubicMeterPerHour => "m³/h",
            Self::CubicMeterPerSecond => "m³/s",
            Self::PartsPerMillion => "ppm",
            Self::Pulses => "pulses",
            Self::Radian => "rad",
            Self::RadianPerSecond => "rad/s",
            Self::RadianPerSecondSquared => "rad/s²",
            Self::Second => "s",
            Self::Steradian => "sr",
            Self::InverseSecond => "s⁻¹",
            Self::Degree => "°",
            Self::DegreeCelsius => "°C",
            Self::DegreeFahrenheit => "°F",
            Self::Ohm => "Ω",
            Self::OhmMeter => "Ωm",
            Self::MicrogramPerCubicMeter => "μg/m³",
        }
    }

    #[must_use]
    pub fn id(&self) -> &'static str {
        match self {
            Self::Percent => "percent",
            Self::Ampere => "ampere",
            Self::AmperePerMeter => "ampere_per_meter",
            Self::AmperePerSquareMeter => "ampere_per_square_meter",
            Self::AmpereSquareMeter => "ampere_square_meter",
            Self::Coulomb => "coulomb",
            Self::CoulombPerSquareMeter => "coulomb_per_square_meter",
            Self::CoulombPerCubicMeter => "coulomb_per_cubic_meter",
            Self::CoulombMeter => "coulomb_meter",
            Self::Farad => "farad",
            Self::Henry => "henry",
            Self::Hertz => "hertz",
            Self::Joule => "joule",
            Self::JoulePerKelvin => "joule_per_kelvin",
            Self::JoulePerMol => "joule_per_mol",
            Self::JouleSecond => "joule_second",
            Self::Kelvin => "kelvin",
            Self::KelvinPerPercent => "kelvin_per_percent",
            Self::KelvinPerHour => "kelvin_per_hour",
            Self::Liter => "liter",
            Self::LiterPerHour => "liter_per_hour",
            Self::LiterPerSquareMeter => "liter_per_square_meter",
            Self::LiterPerSecond => "liter_per_second",
            Self::MegawattHour => "megawatt_hour",
            Self::Newton => "newton",
            Self::NewtonPerMeter => "newton_per_meter",
            Self::NewtonPerSecond => "newton_per_second",
            Self::NewtonMeter => "newton_meter",
            Self::Pascal => "pascal",
            Self::Siemens => "siemens",
            Self::SiemensPerMeter => "siemens_per_meter",
            Self::Tesla => "tesla",
            Self::Volt => "volt",
            Self::VoltPerKelvin => "volt_per_kelvin",
            Self::VoltPerMeter => "volt_per_meter",
            Self::VoltAmpere => "volt_ampere",
            Self::VoltAmpereHour => "volt_ampere_hour",
            Self::VoltAmpereReactiveHour => "volt_ampere_reactive_hour",
            Self::Watt => "watt",
            Self::WattPerSquareMeter => "watt_per_square_meter",
            Self::WattPerMeterKelvin => "watt_per_meter_kelvin",
            Self::Weber => "weber",
            Self::WattHour => "watt_hour",
            Self::SpeedOfLight => "speed_of_light",
            Self::Candela => "candela",
            Self::CandelaPerSquareMeter => "candela_per_square_meter",
            Self::CounterPulses => "counter_pulses",
            Self::GramPerCubicMeter => "gram_per_cubic_meter",
            Self::Hour => "hour",
            Self::KilovoltAmpereHour => "kilovolt_ampere_hour",
            Self::KilovoltAmpereReactiveHour => "kilovolt_ampere_reactive_hour",
            Self::Kilowatt => "kilowatt",
            Self::KilowattHour => "kilowatt_hour",
            Self::Kilogram => "kilogram",
            Self::KilogramPerCubicMeter => "kilogram_per_cubic_meter",
            Self::KilogramPerSecond => "kilogram_per_second",
            Self::KilometerPerHour => "kilometer_per_hour",
            Self::Lumen => "lumen",
            Self::LumenSecond => "lumen_second",
            Self::Lux => "lux",
            Self::Meter => "meter",
            Self::MeterPerSecond => "meter_per_second",
            Self::MeterPerSecondSquared => "meter_per_second_squared",
            Self::Milliampere => "milliampere",
            Self::Millivolt => "millivolt",
            Self::Minute => "minute",
            Self::Millimeter => "millimeter",
            Self::Mol => "mol",
            Self::Millisecond => "millisecond",
            Self::SquareMeter => "square_meter",
            Self::SquareMeterPerNewton => "square_meter_per_newton",
            Self::CubicMeter => "cubic_meter",
            Self::CubicMeterPerHour => "cubic_meter_per_hour",
            Self::CubicMeterPerSecond => "cubic_meter_per_second",
            Self::PartsPerMillion => "parts_per_million",
            Self::Pulses => "pulses",
            Self::Radian => "radian",
            Self::RadianPerSecond => "radian_per_second",
            Self::RadianPerSecondSquared => "radian_per_second_squared",
            Self::Second => "second",
            Self::Steradian => "steradian",
            Self::InverseSecond => "inverse_second",
            Self::Degree => "degree",
            Self::DegreeCelsius => "degree_celsius",
            Self::DegreeFahrenheit => "degree_fahrenheit",
            Self::Ohm => "ohm",
            Self::OhmMeter => "ohm_meter",
            Self::MicrogramPerCubicMeter => "microgram_per_cubic_meter",
        }
    }

    #[must_use]
    pub fn from_symbol(s: &str) -> Option<Self> {
        match s {
            "%" => Some(Self::Percent),
            "A" => Some(Self::Ampere),
            "A/m" => Some(Self::AmperePerMeter),
            "A/m²" => Some(Self::AmperePerSquareMeter),
            "A m²" => Some(Self::AmpereSquareMeter),
            "C" => Some(Self::Coulomb),
            "C/m²" => Some(Self::CoulombPerSquareMeter),
            "C/m³" => Some(Self::CoulombPerCubicMeter),
            "C m" => Some(Self::CoulombMeter),
            "F" => Some(Self::Farad),
            "H" => Some(Self::Henry),
            "Hz" => Some(Self::Hertz),
            "J" => Some(Self::Joule),
            "J/K" => Some(Self::JoulePerKelvin),
            "J/mol" => Some(Self::JoulePerMol),
            "J s" => Some(Self::JouleSecond),
            "K" => Some(Self::Kelvin),
            "K/%" => Some(Self::KelvinPerPercent),
            "K/h" => Some(Self::KelvinPerHour),
            "L" => Some(Self::Liter),
            "L/h" => Some(Self::LiterPerHour),
            "L/m²" => Some(Self::LiterPerSquareMeter),
            "L/s" => Some(Self::LiterPerSecond),
            "MWh" => Some(Self::MegawattHour),
            "N" => Some(Self::Newton),
            "N/m" => Some(Self::NewtonPerMeter),
            "N/s" => Some(Self::NewtonPerSecond),
            "Nm" => Some(Self::NewtonMeter),
            "Pa" => Some(Self::Pascal),
            "S" => Some(Self::Siemens),
            "S/m" => Some(Self::SiemensPerMeter),
            "T" => Some(Self::Tesla),
            "V" => Some(Self::Volt),
            "V/K" => Some(Self::VoltPerKelvin),
            "V/m" => Some(Self::VoltPerMeter),
            "VA" => Some(Self::VoltAmpere),
            "VAh" => Some(Self::VoltAmpereHour),
            "VARh" => Some(Self::VoltAmpereReactiveHour),
            "W" => Some(Self::Watt),
            "W/m²" => Some(Self::WattPerSquareMeter),
            "W/mK" => Some(Self::WattPerMeterKelvin),
            "Wb" => Some(Self::Weber),
            "Wh" => Some(Self::WattHour),
            "c" => Some(Self::SpeedOfLight),
            "cd" => Some(Self::Candela),
            "cd/m²" => Some(Self::CandelaPerSquareMeter),
            "counter pulses" => Some(Self::CounterPulses),
            "g/m³" => Some(Self::GramPerCubicMeter),
            "h" => Some(Self::Hour),
            "kVAh" => Some(Self::KilovoltAmpereHour),
            "kVARh" => Some(Self::KilovoltAmpereReactiveHour),
            "kW" => Some(Self::Kilowatt),
            "kWh" => Some(Self::KilowattHour),
            "kg" => Some(Self::Kilogram),
            "kg/m³" => Some(Self::KilogramPerCubicMeter),
            "kg/s" => Some(Self::KilogramPerSecond),
            "km/h" => Some(Self::KilometerPerHour),
            "lm" => Some(Self::Lumen),
            "lm s" => Some(Self::LumenSecond),
            "lx" => Some(Self::Lux),
            "m" => Some(Self::Meter),
            "m/s" => Some(Self::MeterPerSecond),
            "m/s²" => Some(Self::MeterPerSecondSquared),
            "mA" => Some(Self::Milliampere),
            "mV" => Some(Self::Millivolt),
            "min" => Some(Self::Minute),
            "mm" => Some(Self::Millimeter),
            "mol" => Some(Self::Mol),
            "ms" => Some(Self::Millisecond),
            "m²" => Some(Self::SquareMeter),
            "m²/N" => Some(Self::SquareMeterPerNewton),
            "m³" => Some(Self::CubicMeter),
            "m³/h" => Some(Self::CubicMeterPerHour),
            "m³/s" => Some(Self::CubicMeterPerSecond),
            "ppm" => Some(Self::PartsPerMillion),
            "pulses" => Some(Self::Pulses),
            "rad" => Some(Self::Radian),
            "rad/s" => Some(Self::RadianPerSecond),
            "rad/s²" => Some(Self::RadianPerSecondSquared),
            "s" => Some(Self::Second),
            "sr" => Some(Self::Steradian),
            "s⁻¹" => Some(Self::InverseSecond),
            "°" => Some(Self::Degree),
            "°C" => Some(Self::DegreeCelsius),
            "°F" => Some(Self::DegreeFahrenheit),
            "Ω" => Some(Self::Ohm),
            "Ωm" => Some(Self::OhmMeter),
            "μg/m³" => Some(Self::MicrogramPerCubicMeter),
            _ => None,
        }
    }
}

impl fmt::Display for Unit {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.symbol())
    }
}
