///|
/// Quality descriptor shared by status and measurement objects.
pub struct QualityDescriptor {
  overflow : Bool
  blocked : Bool
  substituted : Bool
  not_topical : Bool
  invalid : Bool
} derive(Eq, Debug)

///|
pub fn QualityDescriptor::clear() -> QualityDescriptor {
  {
    overflow: false,
    blocked: false,
    substituted: false,
    not_topical: false,
    invalid: false,
  }
}

///|
/// Decode the low byte of a QDS value.
pub fn QualityDescriptor::from_byte(
  value : Int,
) -> Result[QualityDescriptor, String] {
  if value < 0 || value > 255 {
    Err("quality descriptor must fit one byte")
  } else {
    Ok({
      overflow: (value & 1) != 0,
      blocked: (value & 0x10) != 0,
      substituted: (value & 0x20) != 0,
      not_topical: (value & 0x40) != 0,
      invalid: (value & 0x80) != 0,
    })
  }
}

///|
pub fn QualityDescriptor::to_byte(self : QualityDescriptor) -> Int {
  (if self.overflow { 1 } else { 0 }) |
  (if self.blocked { 0x10 } else { 0 }) |
  (if self.substituted { 0x20 } else { 0 }) |
  (if self.not_topical { 0x40 } else { 0 }) |
  (if self.invalid { 0x80 } else { 0 })
}

///|
pub fn QualityDescriptor::with_overflow(
  self : QualityDescriptor,
  value : Bool,
) -> QualityDescriptor {
  {
    overflow: value,
    blocked: self.blocked,
    substituted: self.substituted,
    not_topical: self.not_topical,
    invalid: self.invalid,
  }
}

///|
pub fn QualityDescriptor::with_blocked(
  self : QualityDescriptor,
  value : Bool,
) -> QualityDescriptor {
  {
    overflow: self.overflow,
    blocked: value,
    substituted: self.substituted,
    not_topical: self.not_topical,
    invalid: self.invalid,
  }
}

///|
pub fn QualityDescriptor::with_substituted(
  self : QualityDescriptor,
  value : Bool,
) -> QualityDescriptor {
  {
    overflow: self.overflow,
    blocked: self.blocked,
    substituted: value,
    not_topical: self.not_topical,
    invalid: self.invalid,
  }
}

///|
pub fn QualityDescriptor::with_not_topical(
  self : QualityDescriptor,
  value : Bool,
) -> QualityDescriptor {
  {
    overflow: self.overflow,
    blocked: self.blocked,
    substituted: self.substituted,
    not_topical: value,
    invalid: self.invalid,
  }
}

///|
pub fn QualityDescriptor::with_invalid(
  self : QualityDescriptor,
  value : Bool,
) -> QualityDescriptor {
  {
    overflow: self.overflow,
    blocked: self.blocked,
    substituted: self.substituted,
    not_topical: self.not_topical,
    invalid: value,
  }
}

///|
pub fn QualityDescriptor::is_usable(self : QualityDescriptor) -> Bool {
  !self.invalid && !self.blocked
}

///|
pub fn QualityDescriptor::has_status_change(self : QualityDescriptor) -> Bool {
  self.substituted || self.not_topical || self.invalid
}

///|
pub fn QualityDescriptor::flags(self : QualityDescriptor) -> Array[String] {
  let flags : Array[String] = []
  if self.overflow {
    flags.push("overflow")
  }
  if self.blocked {
    flags.push("blocked")
  }
  if self.substituted {
    flags.push("substituted")
  }
  if self.not_topical {
    flags.push("not-topical")
  }
  if self.invalid {
    flags.push("invalid")
  }
  flags
}

///|
/// Quality descriptor for single and double point status values.
pub struct StatusQuality {
  blocked : Bool
  substituted : Bool
  not_topical : Bool
  invalid : Bool
} derive(Eq, Debug)

///|
pub fn StatusQuality::clear() -> StatusQuality {
  { blocked: false, substituted: false, not_topical: false, invalid: false }
}

///|
pub fn StatusQuality::from_byte(value : Int) -> Result[StatusQuality, String] {
  if value < 0 || value > 255 {
    Err("status quality must fit one byte")
  } else {
    Ok({
      blocked: (value & 0x10) != 0,
      substituted: (value & 0x20) != 0,
      not_topical: (value & 0x40) != 0,
      invalid: (value & 0x80) != 0,
    })
  }
}

///|
pub fn StatusQuality::to_byte(self : StatusQuality) -> Int {
  (if self.blocked { 0x10 } else { 0 }) |
  (if self.substituted { 0x20 } else { 0 }) |
  (if self.not_topical { 0x40 } else { 0 }) |
  (if self.invalid { 0x80 } else { 0 })
}

///|
pub fn StatusQuality::as_measurement(self : StatusQuality) -> QualityDescriptor {
  {
    overflow: false,
    blocked: self.blocked,
    substituted: self.substituted,
    not_topical: self.not_topical,
    invalid: self.invalid,
  }
}

///|
pub fn StatusQuality::is_usable(self : StatusQuality) -> Bool {
  !self.invalid && !self.blocked
}

///|
/// Single point information value, including its QDS.
pub struct SinglePointValue {
  state : Bool
  quality : StatusQuality
} derive(Eq, Debug)

///|
pub fn SinglePointValue::new(
  state : Bool,
  quality? : StatusQuality = StatusQuality::clear(),
) -> SinglePointValue {
  { state, quality }
}

///|
pub fn SinglePointValue::state(self : SinglePointValue) -> Bool {
  self.state
}

///|
pub fn SinglePointValue::quality(self : SinglePointValue) -> StatusQuality {
  self.quality
}

///|
pub fn SinglePointValue::to_byte(self : SinglePointValue) -> Int {
  (if self.state { 1 } else { 0 }) | self.quality.to_byte()
}

///|
pub fn SinglePointValue::from_byte(
  value : Int,
) -> Result[SinglePointValue, String] {
  if value < 0 || value > 255 {
    Err("single point value must fit one byte")
  } else {
    match StatusQuality::from_byte(value) {
      Ok(quality) => Ok({ state: (value & 1) != 0, quality })
      Err(error) => Err(error)
    }
  }
}

///|
/// Double point information value. IEC 104 reserves state value 0 and 3.
pub struct DoublePointValue {
  state : Int
  quality : StatusQuality
} derive(Eq, Debug)

///|
pub fn DoublePointValue::new(
  state : Int,
  quality? : StatusQuality = StatusQuality::clear(),
) -> Result[DoublePointValue, String] {
  if state < 0 || state > 3 {
    Err("double point state must fit two bits")
  } else {
    Ok({ state, quality })
  }
}

///|
pub fn DoublePointValue::state(self : DoublePointValue) -> Int {
  self.state
}

///|
pub fn DoublePointValue::quality(self : DoublePointValue) -> StatusQuality {
  self.quality
}

///|
pub fn DoublePointValue::to_byte(self : DoublePointValue) -> Int {
  (self.state & 3) | self.quality.to_byte()
}

///|
pub fn DoublePointValue::from_byte(
  value : Int,
) -> Result[DoublePointValue, String] {
  if value < 0 || value > 255 {
    Err("double point value must fit one byte")
  } else {
    match StatusQuality::from_byte(value) {
      Ok(quality) => Ok({ state: value & 3, quality })
      Err(error) => Err(error)
    }
  }
}

///|
/// Step position value with a signed seven-bit position and transient flag.
pub struct StepPositionValue {
  position : Int
  transient : Bool
  quality : StatusQuality
} derive(Eq, Debug)

///|
pub fn StepPositionValue::new(
  position : Int,
  transient? : Bool = false,
  quality? : StatusQuality = StatusQuality::clear(),
) -> Result[StepPositionValue, String] {
  if position < -64 || position > 63 {
    Err("step position must fit signed seven bits")
  } else {
    Ok({ position, transient, quality })
  }
}

///|
pub fn StepPositionValue::position(self : StepPositionValue) -> Int {
  self.position
}

///|
pub fn StepPositionValue::quality(self : StepPositionValue) -> StatusQuality {
  self.quality
}

///|
pub fn StepPositionValue::to_array(self : StepPositionValue) -> Array[Byte] {
  let encoded = if self.position < 0 {
    self.position + 128
  } else {
    self.position
  }
  [
    encoded.to_byte(),
    ((if self.transient { 1 } else { 0 }) | self.quality.to_byte()).to_byte(),
  ]
}

///|
/// Normalized signed 16-bit measurement.
pub struct NormalizedValue {
  value : Int
  quality : QualityDescriptor
} derive(Eq, Debug)

///|
pub fn NormalizedValue::new(
  value : Int,
  quality? : QualityDescriptor = QualityDescriptor::clear(),
) -> Result[NormalizedValue, String] {
  if value < -32768 || value > 32767 {
    Err("normalized value must fit signed 16 bits")
  } else {
    Ok({ value, quality })
  }
}

///|
pub fn NormalizedValue::value(self : NormalizedValue) -> Int {
  self.value
}

///|
pub fn NormalizedValue::quality(self : NormalizedValue) -> QualityDescriptor {
  self.quality
}

///|
pub fn NormalizedValue::raw_unsigned(self : NormalizedValue) -> Int {
  if self.value < 0 {
    self.value + 65536
  } else {
    self.value
  }
}

///|
pub fn NormalizedValue::to_array(self : NormalizedValue) -> Array[Byte] {
  [
    (self.raw_unsigned() & 0xff).to_byte(),
    ((self.raw_unsigned() >> 8) & 0xff).to_byte(),
    self.quality.to_byte().to_byte(),
  ]
}

///|
/// Signed 16-bit scaled measurement.
pub struct ScaledValue {
  value : Int
  quality : QualityDescriptor
} derive(Eq, Debug)

///|
pub fn ScaledValue::new(
  value : Int,
  quality? : QualityDescriptor = QualityDescriptor::clear(),
) -> Result[ScaledValue, String] {
  if value < -32768 || value > 32767 {
    Err("scaled value must fit signed 16 bits")
  } else {
    Ok({ value, quality })
  }
}

///|
pub fn ScaledValue::value(self : ScaledValue) -> Int {
  self.value
}

///|
pub fn ScaledValue::quality(self : ScaledValue) -> QualityDescriptor {
  self.quality
}

///|
pub fn ScaledValue::to_array(self : ScaledValue) -> Array[Byte] {
  let raw = if self.value < 0 { self.value + 65536 } else { self.value }
  [
    (raw & 0xff).to_byte(),
    ((raw >> 8) & 0xff).to_byte(),
    self.quality.to_byte().to_byte(),
  ]
}

///|
/// IEEE-754 short floating point measurement.
pub struct ShortFloatValue {
  value : Float
  quality : QualityDescriptor
} derive(Eq, Debug)

///|
pub fn ShortFloatValue::new(
  value : Float,
  quality? : QualityDescriptor = QualityDescriptor::clear(),
) -> ShortFloatValue {
  { value, quality }
}

///|
pub fn ShortFloatValue::value(self : ShortFloatValue) -> Float {
  self.value
}

///|
pub fn ShortFloatValue::quality(self : ShortFloatValue) -> QualityDescriptor {
  self.quality
}

///|
/// 32-bit binary counter value with sequence and overflow flags.
pub struct BinaryCounterValue {
  value : UInt
  sequence : Int
  carry : Bool
  adjusted : Bool
  invalid : Bool
} derive(Eq, Debug)

///|
pub fn BinaryCounterValue::new(
  value : UInt,
  sequence? : Int = 0,
  carry? : Bool = false,
  adjusted? : Bool = false,
  invalid? : Bool = false,
) -> Result[BinaryCounterValue, String] {
  if sequence < 0 || sequence > 31 {
    Err("binary counter sequence must fit five bits")
  } else {
    Ok({ value, sequence, carry, adjusted, invalid })
  }
}

///|
pub fn BinaryCounterValue::value(self : BinaryCounterValue) -> UInt {
  self.value
}

///|
pub fn BinaryCounterValue::sequence(self : BinaryCounterValue) -> Int {
  self.sequence
}

///|
pub fn BinaryCounterValue::flags(self : BinaryCounterValue) -> Int {
  self.sequence |
  (if self.carry { 0x20 } else { 0 }) |
  (if self.adjusted { 0x40 } else { 0 }) |
  (if self.invalid { 0x80 } else { 0 })
}

///|
/// Quality-preserving measurement classification used by point stores.
pub enum MeasurementState {
  Good
  Blocked
  Substituted
  NotTopical
  Invalid
} derive(Eq, Debug)

///|
pub fn measurement_state(quality : QualityDescriptor) -> MeasurementState {
  if quality.invalid {
    Invalid
  } else if quality.blocked {
    Blocked
  } else if quality.substituted {
    Substituted
  } else if quality.not_topical {
    NotTopical
  } else {
    Good
  }
}

///|
pub fn measurement_state_examples() -> Array[MeasurementState] {
  [Good, Blocked, Substituted, NotTopical, Invalid]
}

///|
/// Build a quality descriptor from a measurement state.
pub fn quality_for_state(state : MeasurementState) -> QualityDescriptor {
  match state {
    Good => QualityDescriptor::clear()
    Blocked => QualityDescriptor::clear().with_blocked(true)
    Substituted => QualityDescriptor::clear().with_substituted(true)
    NotTopical => QualityDescriptor::clear().with_not_topical(true)
    Invalid => QualityDescriptor::clear().with_invalid(true)
  }
}

///|
/// Return the signed value represented by a two-byte little-endian field.
pub fn decode_signed16(low : Int, high : Int) -> Result[Int, String] {
  if low < 0 || low > 255 || high < 0 || high > 255 {
    Err("signed value octets must fit one byte")
  } else {
    let raw = low | (high << 8)
    Ok(if raw >= 32768 { raw - 65536 } else { raw })
  }
}

///|
/// Encode a signed value into a little-endian two-byte field.
pub fn encode_signed16(value : Int) -> Result[Array[Byte], String] {
  if value < -32768 || value > 32767 {
    Err("signed value must fit 16 bits")
  } else {
    let raw = if value < 0 { value + 65536 } else { value }
    Ok([(raw & 0xff).to_byte(), ((raw >> 8) & 0xff).to_byte()])
  }
}

///|
/// Clamp a quality byte to the protocol's low eight bits.
pub fn normalize_quality(value : Int) -> Int {
  value & 0xff
}