   end
 
   defp field_name(field) when is_record(field, :named_field), do: named_field(field, :name)
-
-  defp infer_type_from_term(term) when is_binary(term),  do: :string
-  defp infer_type_from_term(term) when is_integer(term), do: :sint32
-  defp infer_type_from_term(term) when is_float(term),   do: :double
-  defp infer_type_from_term(term) when is_boolean(term), do: :boolean
-  defp infer_type_from_term(term) when is_list(term),    do: :embedded_list
-  defp infer_type_from_term(term) when is_map(term),     do: :embedded_map
 
   # http://orientdb.com/docs/last/Types.html
   @types [

    {{class_name, fields}, rest}
  end

  defp decode_header_fields(data, acc \\ []) do
    {i, rest} = :small_ints.decode_zigzag_varint(data)

    # If `i` is positive, that means the next field definition is a "named
    # field" and `i` is the length of the field's name. If it's negative, it
    # represents the property id of a property. If it's 0, it signals the end of
    # the header segment.

    cond do
      i == 0 ->
        # Remember to return `rest` and not `data` since we want to ditch the 0
        # byte that signals the end of the header.
        {Enum.reverse(acc), rest}
      i < 0 ->
        {data_ptr, rest} = decode_data_ptr(rest)
        property = property(id: decode_property_id(i), data_ptr: data_ptr)
        decode_header_fields(rest, [property|acc])
      i > 0 ->
        {field_name, rest}            = decode_type(data, :string)
        {data_ptr, rest}              = decode_data_ptr(rest)
        <<data_type, rest :: binary>> = rest
        field = named_field(name: field_name, data_type: int_to_type(data_type), data_ptr: data_ptr)

        decode_header_fields(rest, [field|acc])
    end
  end

  defp decode_fields(data, field_definitions) do
    {fields_and_values, rest} = Enum.map_reduce field_definitions, data, fn
      field, acc when is_record(field, :named_field) ->
        named_field(data_type: type, data_ptr: ptr) = field

        if ptr == 0 do
          {{field, nil}, acc}
        else
          {value, rest} = decode_type(acc, type)
          {{field, value}, rest}
        end
    end

    {build_fields(fields_and_values), rest}
  end

  defp build_fields(fields_and_values) do
    for {field, value} <- fields_and_values, into: %{} do
      {named_field(field, :name), value}
    end
  end


  end

  defp encode_fields(fields, offset) do
    fields = Enum.map(fields, &encode_field_value/1)
    offset = offset + header_offset(fields)

    {fields, values, _} = Enum.reduce fields, {[], [], offset}, fn(%Field{} = field, {fs, vs, index}) ->
      encoded_field = %{field | pointer_to_data: index} |> encode_field_for_header
      index         = index + IO.iodata_length(field.encoded_value)

      {[encoded_field|fs], [field.encoded_value|vs], index}
    end

    [Enum.reverse(fields), 0, Enum.reverse(values)]
  end

  # Returns the length of the header based on the list of fields.
  defp header_offset(fields) do
    # The last +1 is for the `0` that signals the end of the header.
    fields
    |> Enum.map(&(&1 |> encode_field_for_header |> IO.iodata_length))
    |> Enum.sum
    |> +(1)
  end

  # Returns the given `%Field{}` with the `:encoded_value` field set to the
  # result of encoding the `:value` field.
  defp encode_field_value(%Field{type: type, value: value} = field) do
    %{field | encoded_value: encode_type(value, type)}
  end

  # Encodes the given `%Field{}` for the header, i.e., just the field
  # representation and not the value (name, pointer to data, type). Returns
  # iodata.
  defp encode_field_for_header(%Field{pointer_to_data: ptr, type: type, name: name} = field) do
    if is_nil(ptr) do
      ptr = 0
    end

    [encode_type(name, :string), <<ptr :: 32-signed>>, type_to_int(type)]
  end

  # Encodes an instance of `type`. Returns an iodata instead of a binary.
