defmodule DaProductApp.MercuryISO8583.Packagers do @moduledoc """ Main entry point for ISO8583 packagers system. This module provides a clean interface to the packager system and mirrors the jPOS packager architecture while being idiomatic Elixir. ## Usage Examples # Get the ISO87B packager packager = Packagers.iso87b() # Pack a field {:ok, packed} = Packagers.pack_field(4, "000000005000", packager) # Unpack a field {:ok, {value, offset}} = Packagers.unpack_field(4, binary_data, 0, packager) """ alias DaProductApp.MercuryISO8583.Packagers.ISO87BPackager alias DaProductApp.MercuryISO8583.Packagers.FieldPackagers.{ IFB_NUMERIC, IFB_LLNUM, IFB_LLCHAR, IFB_LLLCHAR, IF_CHAR, IFB_BINARY, IFB_BITMAP } @doc """ Get the ISO87B packager instance. """ def iso87b(), do: ISO87BPackager @doc """ Pack a specific field using the appropriate packager. ## Parameters - `field_number`: ISO8583 field number (0-128) - `value`: Field value to pack - `packager`: Packager module (default: ISO87BPackager) ## Returns - `{:ok, packed_binary}` on success - `{:error, reason}` on failure """ def pack_field(field_number, value, packager \\ ISO87BPackager) do case packager.get_field_packager(field_number) do nil -> {:error, "Unknown field number: #{field_number}"} field_packager -> pack_with_packager(value, field_packager, field_number) end end @doc """ Unpack a specific field using the appropriate packager. ## Parameters - `field_number`: ISO8583 field number (0-128) - `binary_data`: Binary data to unpack - `offset`: Starting offset in the binary data - `packager`: Packager module (default: ISO87BPackager) ## Returns - `{:ok, {value, new_offset}}` on success - `{:error, reason}` on failure """ def unpack_field(field_number, binary_data, offset, packager \\ ISO87BPackager) do case packager.get_field_packager(field_number) do nil -> {:error, "Unknown field number: #{field_number}"} field_packager -> unpack_with_packager(binary_data, offset, field_packager, field_number) end end @doc """ Get field information for a specific field number. ## Parameters - `field_number`: ISO8583 field number (0-128) - `packager`: Packager module (default: ISO87BPackager) ## Returns - Map with field information or nil if field doesn't exist """ def get_field_info(field_number, packager \\ ISO87BPackager) do case packager.get_field_packager(field_number) do nil -> nil field_packager -> %{ field_number: field_number, description: get_field_description(field_packager), max_length: get_field_max_length(field_packager), type: get_field_type(field_packager) } end end @doc """ List all available field packagers in the given packager. ## Parameters - `packager`: Packager module (default: ISO87BPackager) ## Returns - List of field information maps """ def list_fields(packager \\ ISO87BPackager) do 0..packager.max_fields() |> Enum.map(&get_field_info(&1, packager)) |> Enum.reject(&is_nil/1) end # Private helper functions defp pack_with_packager(value, field_packager, field_number) do case field_packager do %DaProductApp.MercuryISO8583.Packagers.FieldPackagers.BaseFieldPackager{} = packager -> DaProductApp.MercuryISO8583.Packagers.FieldPackagers.BaseFieldPackager.pack_with_packager(value, packager) _ -> # For now, use the field packager's pack function # This would be expanded based on the specific field packager type {:error, "Unsupported field packager type"} end end defp unpack_with_packager(binary_data, offset, field_packager, field_number) do case field_packager do %DaProductApp.MercuryISO8583.Packagers.FieldPackagers.BaseFieldPackager{} = packager -> DaProductApp.MercuryISO8583.Packagers.FieldPackagers.BaseFieldPackager.unpack_with_packager(%{}, binary_data, offset, packager) _ -> # For now, handle unsupported types {:error, "Unsupported field packager type"} end end defp get_field_description(field_packager) do case field_packager do %DaProductApp.MercuryISO8583.Packagers.FieldPackagers.BaseFieldPackager{description: desc} -> desc _ -> "Unknown field type" end end defp get_field_max_length(field_packager) do case field_packager do %DaProductApp.MercuryISO8583.Packagers.FieldPackagers.BaseFieldPackager{length: len} -> len _ -> 0 end end defp get_field_type(field_packager) do case field_packager do %DaProductApp.MercuryISO8583.Packagers.FieldPackagers.BaseFieldPackager{interpreter: interpreter} -> case interpreter do {:bcd_interpreter, _, _} -> :bcd_numeric {:ascii_interpreter} -> :ascii_text {:binary_interpreter} -> :binary _ -> :unknown end _ -> :unknown end end end