///|
fn Generator::fixed_view(
  self : Generator,
  name : String,
  message : MessageDef,
) -> Unit raise SbeError {
  let numeric : Array[(FieldDef, TypeEncoding)] = []
  for field in message.block.fields {
    if field.presence == Constant {
      continue
    }
    match self.schema.type_def(field.type_name) {
      Encoded(encoding) if encoding.primitive != Char =>
        numeric.push((field, encoding))
      _ => ()
    }
  }
  if numeric.is_empty() {
    return
  }
  let view = name + "View"
  ignore(self.claim(view, "view:\{message.id}"))
  let order = match self.schema.byte_order {
    LittleEndian => "@sbe.LittleEndian"
    BigEndian => "@sbe.BigEndian"
  }
  let out = self.output
  out.write_string(
    "///|\n/// Direct reads of fixed numeric fields from the original bytes.\npub struct \{view} {\n  block : @sbe.FixedBlock\n  acting_version : Int\n}\n\n",
  )
  out.write_string(
    "///|\npub fn \{view}::version(self : \{view}) -> Int { self.acting_version }\n\n",
  )
  let layout = self.schema.header_layout
  out.write_string(
    "///|\n/// Checks message identity and fixed block bounds. Use decode for full validation.\npub fn \{name}::view(bytes : Bytes) -> \{view} raise @sbe.SbeError {\n  if bytes.length() > 16777216 { raise @sbe.value_error(\"message\", \"message exceeds 16 MiB\") }\n  let header = @sbe.FixedBlock::new(bytes, 0, \{layout.size}, \{order})\n",
  )
  for key in ["blockLength", "templateId", "schemaId", "version"] {
    let entry = layout.entry(key)
    let variable = field_identifier(key)
    out.write_string(
      "  let \{variable} = header.uint_at(\{entry.offset}, \{entry.primitive.width()})\n",
    )
    let lower = if entry.min_value > 0 {
      "\{variable} < \{entry.min_value}UL || "
    } else {
      ""
    }
    out.write_string(
      "  if \{lower}\{variable} > \{entry.max_value}UL { raise @sbe.value_error(\{code_quote(key)}, \"out-of-range framing member\") }\n",
    )
  }
  out.write_string(
    "  if f_template_id != \{message.id}UL || f_schema_id != \{self.schema.id}UL || f_version.to_int() < \{message.since_version} {\n    raise @sbe.value_error(\"header\", \"unexpected template, schema or acting version\")\n  }\n  { block: @sbe.FixedBlock::new(bytes, \{layout.size}, f_block_length.to_int(), \{order}), acting_version: f_version.to_int() }\n}\n\n",
  )
  for item in numeric {
    self.numeric_accessor(view, item.0, item.1)
  }
}

///|
fn numeric_is_null(encoding : TypeEncoding) -> String raise SbeError {
  match encoding_null(encoding) {
    Unsigned(value) => "value == \{value}UL"
    Signed(value) => "value == \{value}L"
    Floating(value) =>
      if value.is_nan() {
        "value.is_nan()"
      } else if value.is_inf() {
        if value < 0.0 {
          "value.is_inf() && value < 0.0"
        } else {
          "value.is_inf() && value > 0.0"
        }
      } else {
        "value == \{value}"
      }
    _ => raise InvalidSchema("numeric accessor expected a numeric null", 0)
  }
}

///|
fn Generator::numeric_accessor(
  self : Generator,
  view : String,
  field : FieldDef,
  encoding : TypeEncoding,
) -> Unit raise SbeError {
  let name = field_identifier(field.name)
  let primitive = encoding.primitive
  let width = primitive.width()
  let (type_name, reader) = if primitive.is_signed() {
    ("Int64", "int_at")
  } else if primitive.is_float() {
    ("Double", "float_at")
  } else {
    ("UInt64", "uint_at")
  }
  let optional = field.presence == Optional || field.since_version > 0
  let result = if optional { type_name + "?" } else { type_name }
  let array = encoding.length != 1
  let argument = if array { ", index : Int" } else { "" }
  let out = self.output
  out.write_string(
    "///|\npub fn \{view}::\{name}(self : \{view}\{argument}) -> \{result} raise @sbe.SbeError {\n",
  )
  if array {
    out.write_string(
      "  if index < 0 || index >= \{encoding.length} { raise @sbe.value_error(\{code_quote(field.name)}, \"array index out of range\") }\n",
    )
  }
  if field.since_version > 0 {
    out.write_string(
      "  if self.acting_version < \{field.since_version} { return None }\n",
    )
  }
  let offset = if array {
    "\{field.offset} + index * \{width}"
  } else {
    "\{field.offset}"
  }
  out.write_string("  let value = self.block.\{reader}(\{offset}, \{width})\n")
  if field.presence == Optional {
    out.write_string("  if \{numeric_is_null(encoding)} { return None }\n")
  }
  out.write_string(
    if optional {
      "  Some(value)\n}\n\n"
    } else {
      "  value\n}\n\n"
    },
  )
}