src/qrkit@internal@structured_append.erl

-module(qrkit@internal@structured_append).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/qrkit/internal/structured_append.gleam").
-export([parity_of/1, encode/3]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

?MODULEDOC(false).

-file("src/qrkit/internal/structured_append.gleam", 45).
?DOC(false).
-spec parity_of_bits(bitstring(), integer()) -> integer().
parity_of_bits(Bytes, Acc) ->
    case Bytes of
        <<>> ->
            Acc;

        <<Byte:8, Rest/bitstring>> ->
            parity_of_bits(Rest, erlang:'bxor'(Acc, Byte));

        _ ->
            Acc
    end.

-file("src/qrkit/internal/structured_append.gleam", 41).
?DOC(false).
-spec parity_of(binary()) -> integer().
parity_of(Data) ->
    parity_of_bits(gleam_stdlib:identity(Data), 0).

-file("src/qrkit/internal/structured_append.gleam", 136).
?DOC(false).
-spec build_header(integer(), integer(), integer()) -> bitstring().
build_header(Index, Total, Parity) ->
    _pipe = qrkit@internal@bitstream:new(),
    _pipe@1 = qrkit@internal@bitstream:append_bits(_pipe, 2#0011, 4),
    _pipe@2 = qrkit@internal@bitstream:append_bits(_pipe@1, Index, 4),
    _pipe@3 = qrkit@internal@bitstream:append_bits(_pipe@2, Total - 1, 4),
    _pipe@4 = qrkit@internal@bitstream:append_bits(_pipe@3, Parity, 8),
    qrkit@internal@bitstream:to_bit_array(_pipe@4).

-file("src/qrkit/internal/structured_append.gleam", 103).
?DOC(false).
-spec encode_multiple_shards(
    list(binary()),
    binary(),
    integer(),
    integer(),
    qrkit@types:error_correction(),
    integer(),
    list(qrkit@internal@standard:encoded())
) -> {ok, list(qrkit@internal@standard:encoded())} |
    {error, qrkit@error:encode_error()}.
encode_multiple_shards(Chunks, Data, Total, Max_version, Ecc, Index, Acc) ->
    case Chunks of
        [] ->
            {ok, lists:reverse(Acc)};

        [Chunk | Rest] ->
            Parity = parity_of(Data),
            Header = build_header(Index, Total, Parity),
            case qrkit@internal@standard:encode_prefixed(
                Chunk,
                Ecc,
                1,
                Max_version,
                none,
                auto,
                Header
            ) of
                {ok, Encoded} ->
                    encode_multiple_shards(
                        Rest,
                        Data,
                        Total,
                        Max_version,
                        Ecc,
                        Index + 1,
                        [Encoded | Acc]
                    );

                {error, Error} ->
                    {error, Error}
            end
    end.

-file("src/qrkit/internal/structured_append.gleam", 72).
?DOC(false).
-spec encode_shards(
    list(binary()),
    binary(),
    integer(),
    integer(),
    qrkit@types:error_correction()
) -> {ok, list(qrkit@internal@standard:encoded())} |
    {error, qrkit@error:encode_error()}.
encode_shards(Chunks, Data, Total, Max_version, Ecc) ->
    case Total of
        1 ->
            case Chunks of
                [Chunk] ->
                    case qrkit@internal@standard:encode_prefixed(
                        Chunk,
                        Ecc,
                        1,
                        Max_version,
                        none,
                        auto,
                        <<>>
                    ) of
                        {ok, Encoded} ->
                            {ok, [Encoded]};

                        {error, Error} ->
                            {error, Error}
                    end;

                _ ->
                    {error, empty_input}
            end;

        _ ->
            encode_multiple_shards(Chunks, Data, Total, Max_version, Ecc, 0, [])
    end.

-file("src/qrkit/internal/structured_append.gleam", 171).
?DOC(false).
-spec take_chars(list(binary()), integer(), list(binary())) -> {list(binary()),
    list(binary())}.
take_chars(Chars, Count, Acc) ->
    case {Chars, Count} of
        {Rest, 0} ->
            {lists:reverse(Acc), Rest};

        {[], _} ->
            {lists:reverse(Acc), []};

        {[First | Rest@1], _} ->
            take_chars(Rest@1, Count - 1, [First | Acc])
    end.

-file("src/qrkit/internal/structured_append.gleam", 183).
?DOC(false).
-spec ceil_div(integer(), integer()) -> integer().
ceil_div(Numerator, Denominator) ->
    case Denominator =< 0 of
        true ->
            Numerator;

        false ->
            case Denominator of
                0 -> 0;
                Gleam@denominator -> ((Numerator + Denominator) - 1) div Gleam@denominator
            end
    end.

-file("src/qrkit/internal/structured_append.gleam", 151).
?DOC(false).
-spec do_split(list(binary()), integer(), integer(), list(binary())) -> list(binary()).
do_split(Chars, Remaining_parts, Remaining_chars, Acc) ->
    case Remaining_parts of
        0 ->
            lists:reverse(Acc);

        1 ->
            lists:reverse([erlang:list_to_binary(Chars) | Acc]);

        _ ->
            Take_count = ceil_div(Remaining_chars, Remaining_parts),
            {Taken, Rest} = take_chars(Chars, Take_count, []),
            do_split(
                Rest,
                Remaining_parts - 1,
                Remaining_chars - Take_count,
                [erlang:list_to_binary(Taken) | Acc]
            )
    end.

-file("src/qrkit/internal/structured_append.gleam", 145).
?DOC(false).
-spec split_string(binary(), integer()) -> list(binary()).
split_string(Data, Parts) ->
    Chars = qrkit@internal@util:characters(Data),
    Total_chars = erlang:length(Chars),
    do_split(Chars, Parts, Total_chars, []).

-file("src/qrkit/internal/structured_append.gleam", 54).
?DOC(false).
-spec find_split(binary(), integer(), qrkit@types:error_correction(), integer()) -> {ok,
        list(qrkit@internal@standard:encoded())} |
    {error, qrkit@error:encode_error()}.
find_split(Data, Max_version, Ecc, Total) ->
    case Total > 16 of
        true ->
            {error, {data_exceeds_capacity, 0, 0}};

        false ->
            Chunks = split_string(Data, Total),
            case encode_shards(Chunks, Data, Total, Max_version, Ecc) of
                {ok, Encodes} ->
                    {ok, Encodes};

                {error, _} ->
                    find_split(Data, Max_version, Ecc, Total + 1)
            end
    end.

-file("src/qrkit/internal/structured_append.gleam", 25).
?DOC(false).
-spec encode(binary(), integer(), qrkit@types:error_correction()) -> {ok,
        list(qrkit@internal@standard:encoded())} |
    {error, qrkit@error:encode_error()}.
encode(Data, Max_version, Ecc) ->
    case Data =:= <<""/utf8>> of
        true ->
            {error, empty_input};

        false ->
            case (Max_version < 1) orelse (Max_version > 40) of
                true ->
                    {error, {invalid_version, Max_version}};

                false ->
                    find_split(Data, Max_version, Ecc, 1)
            end
    end.