///|
/// OPC UA DataValue with optional value, status, timestamps, and picoseconds.
pub(all) struct DataValue {
  value : VariantValue?
  status : StatusCode?
  source_timestamp : UaDateTime?
  server_timestamp : UaDateTime?
  source_picoseconds : UInt16?
  server_picoseconds : UInt16?
}

///|
/// Construct an empty DataValue.
pub fn DataValue::empty() -> DataValue {
  {
    value: None,
    status: None,
    source_timestamp: None,
    server_timestamp: None,
    source_picoseconds: None,
    server_picoseconds: None,
  }
}

///|
/// Construct a DataValue carrying only a Variant value.
pub fn DataValue::of(value : VariantValue) -> DataValue {
  {
    value: Some(value),
    status: None,
    source_timestamp: None,
    server_timestamp: None,
    source_picoseconds: None,
    server_picoseconds: None,
  }
}

///|
/// Whether the DataValue has no optional fields.
pub fn DataValue::is_empty(self : DataValue) -> Bool {
  match self.value {
    Some(_) => false
    None =>
      match self.status {
        Some(_) => false
        None =>
          match self.source_timestamp {
            Some(_) => false
            None =>
              match self.server_timestamp {
                Some(_) => false
                None =>
                  match self.source_picoseconds {
                    Some(_) => false
                    None =>
                      match self.server_picoseconds {
                        Some(_) => false
                        None => true
                      }
                  }
              }
          }
      }
  }
}

///|
fn validate_data_value(value : DataValue) -> Unit raise UaError {
  match value.source_picoseconds {
    Some(ps) => {
      if ps > 9999U.to_uint16() {
        raise InvalidValue("SourcePicoseconds must be between 0 and 9999")
      }
      match value.source_timestamp {
        None => raise InvalidValue("SourcePicoseconds requires SourceTimestamp")
        Some(_) => ()
      }
    }
    None => ()
  }
  match value.server_picoseconds {
    Some(ps) => {
      if ps > 9999U.to_uint16() {
        raise InvalidValue("ServerPicoseconds must be between 0 and 9999")
      }
      match value.server_timestamp {
        None => raise InvalidValue("ServerPicoseconds requires ServerTimestamp")
        Some(_) => ()
      }
    }
    None => ()
  }
}

///|
fn data_value_mask(value : DataValue) -> UInt32 {
  let mask = 0U
  let mut mask = mask
  match value.value {
    Some(_) => mask = mask | 0x01U
    None => ()
  }
  match value.status {
    Some(_) => mask = mask | 0x02U
    None => ()
  }
  match value.source_timestamp {
    Some(_) => mask = mask | 0x04U
    None => ()
  }
  match value.server_timestamp {
    Some(_) => mask = mask | 0x08U
    None => ()
  }
  match value.source_picoseconds {
    Some(_) => mask = mask | 0x10U
    None => ()
  }
  match value.server_picoseconds {
    Some(_) => mask = mask | 0x20U
    None => ()
  }
  mask
}

///|
/// Encode a DataValue and its optional-field mask.
pub fn UaWriter::write_data_value(
  self : UaWriter,
  value : DataValue,
) -> Unit raise UaError {
  validate_data_value(value)
  self.write_u8(data_value_mask(value))
  match value.value {
    Some(item) => self.write_variant(item)
    None => ()
  }
  match value.status {
    Some(item) => self.write_status_code(item)
    None => ()
  }
  match value.source_timestamp {
    Some(item) => self.write_datetime(item)
    None => ()
  }
  match value.server_timestamp {
    Some(item) => self.write_datetime(item)
    None => ()
  }
  match value.source_picoseconds {
    Some(item) => self.write_uint16(item)
    None => ()
  }
  match value.server_picoseconds {
    Some(item) => self.write_uint16(item)
    None => ()
  }
}

///|
/// Decode a DataValue and validate its optional-field mask.
pub fn UaReader::read_data_value(self : UaReader) -> DataValue raise UaError {
  let mask = self.read_u8()
  if (mask & 0xC0U) != 0U {
    raise InvalidMask("DataValue", mask)
  }
  let value = if (mask & 0x01U) != 0U {
    Some(self.read_variant())
  } else {
    None
  }
  let status = if (mask & 0x02U) != 0U {
    Some(self.read_status_code())
  } else {
    None
  }
  let source_timestamp = if (mask & 0x04U) != 0U {
    Some(self.read_datetime())
  } else {
    None
  }
  let server_timestamp = if (mask & 0x08U) != 0U {
    Some(self.read_datetime())
  } else {
    None
  }
  let source_picoseconds = if (mask & 0x10U) != 0U {
    Some(self.read_uint16())
  } else {
    None
  }
  let server_picoseconds = if (mask & 0x20U) != 0U {
    Some(self.read_uint16())
  } else {
    None
  }
  let decoded = {
    value,
    status,
    source_timestamp,
    server_timestamp,
    source_picoseconds,
    server_picoseconds,
  }
  validate_data_value(decoded)
  decoded
}