src/qrkit@internal@segment.erl

-module(qrkit@internal@segment).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/qrkit/internal/segment.gleam").
-export([mode/1, data/1, count/1, bits/1, encoded_bits/3, optimise/3, append_to_stream/4]).
-export_type([segment/0]).

-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).

-opaque segment() :: {segment,
        qrkit@types:mode(),
        binary(),
        integer(),
        integer(),
        integer()}.

-file("src/qrkit/internal/segment.gleam", 18).
?DOC(false).
-spec mode(segment()) -> qrkit@types:mode().
mode(Segment) ->
    {segment, Mode, _, _, _, _} = Segment,
    Mode.

-file("src/qrkit/internal/segment.gleam", 23).
?DOC(false).
-spec data(segment()) -> binary().
data(Segment) ->
    {segment, _, Data, _, _, _} = Segment,
    Data.

-file("src/qrkit/internal/segment.gleam", 28).
?DOC(false).
-spec count(segment()) -> integer().
count(Segment) ->
    {segment, _, _, Count, _, _} = Segment,
    Count.

-file("src/qrkit/internal/segment.gleam", 33).
?DOC(false).
-spec bits(segment()) -> integer().
bits(Segment) ->
    {segment, _, _, _, Bits, _} = Segment,
    Bits.

-file("src/qrkit/internal/segment.gleam", 186).
?DOC(false).
-spec classify_char(binary()) -> qrkit@types:mode().
classify_char(Char) ->
    case qrkit@internal@mode:is_numeric_char(Char) of
        true ->
            numeric;

        false ->
            case qrkit@internal@mode:is_alphanumeric_char(Char) of
                true ->
                    alphanumeric;

                false ->
                    case qrkit@internal@mode:is_kanji_char(Char) of
                        true ->
                            kanji;

                        false ->
                            byte
                    end
            end
    end.

-file("src/qrkit/internal/segment.gleam", 201).
?DOC(false).
-spec is_alnum_family(qrkit@types:mode()) -> boolean().
is_alnum_family(Value) ->
    case Value of
        numeric ->
            true;

        alphanumeric ->
            true;

        _ ->
            false
    end.

-file("src/qrkit/internal/segment.gleam", 209).
?DOC(false).
-spec build_segment(qrkit@types:mode(), binary(), integer()) -> segment().
build_segment(Current_mode, Text, Index) ->
    {segment,
        Current_mode,
        Text,
        qrkit@internal@mode:character_count(Text, Current_mode),
        qrkit@internal@mode:data_bits_length(Text, Current_mode),
        Index}.

-file("src/qrkit/internal/segment.gleam", 98).
?DOC(false).
-spec greedy_segments(
    list(binary()),
    integer(),
    list(segment()),
    gleam@option:option({qrkit@types:mode(), binary(), integer()})
) -> list(segment()).
greedy_segments(Chars, Index, Acc, Current) ->
    case Chars of
        [] ->
            case Current of
                {some, {Current_mode, Current_text, Start}} ->
                    lists:reverse(
                        [build_segment(Current_mode, Current_text, Start) | Acc]
                    );

                none ->
                    lists:reverse(Acc)
            end;

        [Char | Rest] ->
            Next_mode = classify_char(Char),
            case Current of
                {some, {Current_mode@1, Current_text@1, Start@1}} when Current_mode@1 =:= Next_mode ->
                    greedy_segments(
                        Rest,
                        Index + 1,
                        Acc,
                        {some,
                            {Current_mode@1,
                                <<Current_text@1/binary, Char/binary>>,
                                Start@1}}
                    );

                {some, {Current_mode@2, Current_text@2, Start@2}} ->
                    greedy_segments(
                        Rest,
                        Index + 1,
                        [build_segment(Current_mode@2, Current_text@2, Start@2) |
                            Acc],
                        {some, {Next_mode, Char, Index}}
                    );

                none ->
                    greedy_segments(
                        Rest,
                        Index + 1,
                        Acc,
                        {some, {Next_mode, Char, Index}}
                    )
            end
    end.

-file("src/qrkit/internal/segment.gleam", 254).
?DOC(false).
-spec append_eci(qrkit@internal@bitstream:bit_stream(), integer()) -> {ok,
        qrkit@internal@bitstream:bit_stream()} |
    {error, qrkit@error:encode_error()}.
append_eci(Stream, Designator) ->
    case (Designator < 0) orelse (Designator > 999999) of
        true ->
            {error, {invalid_eci_designator, Designator}};

        false ->
            case Designator < 128 of
                true ->
                    {ok,
                        begin
                            _pipe = Stream,
                            _pipe@1 = qrkit@internal@bitstream:append_bits(
                                _pipe,
                                2#0111,
                                4
                            ),
                            qrkit@internal@bitstream:append_bits(
                                _pipe@1,
                                Designator,
                                8
                            )
                        end};

                false ->
                    case Designator < 16384 of
                        true ->
                            {ok,
                                begin
                                    _pipe@2 = Stream,
                                    _pipe@3 = qrkit@internal@bitstream:append_bits(
                                        _pipe@2,
                                        2#0111,
                                        4
                                    ),
                                    _pipe@4 = qrkit@internal@bitstream:append_bits(
                                        _pipe@3,
                                        2#10,
                                        2
                                    ),
                                    qrkit@internal@bitstream:append_bits(
                                        _pipe@4,
                                        Designator,
                                        14
                                    )
                                end};

                        false ->
                            {ok,
                                begin
                                    _pipe@5 = Stream,
                                    _pipe@6 = qrkit@internal@bitstream:append_bits(
                                        _pipe@5,
                                        2#0111,
                                        4
                                    ),
                                    _pipe@7 = qrkit@internal@bitstream:append_bits(
                                        _pipe@6,
                                        2#110,
                                        3
                                    ),
                                    qrkit@internal@bitstream:append_bits(
                                        _pipe@7,
                                        Designator,
                                        21
                                    )
                                end}
                    end
            end
    end.

-file("src/qrkit/internal/segment.gleam", 289).
?DOC(false).
-spec eci_designator_bits(integer()) -> integer().
eci_designator_bits(Value) ->
    case Value < 128 of
        true ->
            8;

        false ->
            case Value < 16384 of
                true ->
                    16;

                false ->
                    24
            end
    end.

-file("src/qrkit/internal/segment.gleam", 66).
?DOC(false).
-spec encoded_bits(list(segment()), integer(), gleam@option:option(integer())) -> integer().
encoded_bits(Segments, Version, Eci) ->
    Eci_bits = case Eci of
        none ->
            0;

        {some, Value} ->
            4 + eci_designator_bits(Value)
    end,
    Eci_bits + gleam@list:fold(
        Segments,
        0,
        fun(Acc, Segment) ->
            ((Acc + 4) + qrkit@internal@mode:char_count_bits(
                mode(Segment),
                Version
            ))
            + bits(Segment)
        end
    ).

-file("src/qrkit/internal/segment.gleam", 300).
?DOC(false).
-spec segment_index(segment()) -> integer().
segment_index(Segment) ->
    {segment, _, _, _, _, Index} = Segment,
    Index.

-file("src/qrkit/internal/segment.gleam", 152).
?DOC(false).
-spec promote_pair(segment(), segment()) -> gleam@option:option(segment()).
promote_pair(First, Second) ->
    First_mode = mode(First),
    Second_mode = mode(Second),
    case is_alnum_family(First_mode) andalso is_alnum_family(Second_mode) of
        true ->
            {some,
                build_segment(
                    alphanumeric,
                    <<(data(First))/binary, (data(Second))/binary>>,
                    segment_index(First)
                )};

        false ->
            none
    end.

-file("src/qrkit/internal/segment.gleam", 141).
?DOC(false).
-spec merge_numeric_with_alphanumeric(list(segment())) -> list(segment()).
merge_numeric_with_alphanumeric(Segments) ->
    case Segments of
        [First, Second | Rest] ->
            case promote_pair(First, Second) of
                {some, Merged} ->
                    merge_numeric_with_alphanumeric([Merged | Rest]);

                none ->
                    [First | merge_numeric_with_alphanumeric([Second | Rest])]
            end;

        _ ->
            Segments
    end.

-file("src/qrkit/internal/segment.gleam", 166).
?DOC(false).
-spec merge_adjacent_same_mode(list(segment())) -> list(segment()).
merge_adjacent_same_mode(Segments) ->
    case Segments of
        [First, Second | Rest] ->
            case mode(First) =:= mode(Second) of
                true ->
                    merge_adjacent_same_mode(
                        [build_segment(
                                mode(First),
                                <<(data(First))/binary, (data(Second))/binary>>,
                                segment_index(First)
                            ) |
                            Rest]
                    );

                false ->
                    [First | merge_adjacent_same_mode([Second | Rest])]
            end;

        [First@1 | Rest@1] ->
            [First@1 | merge_adjacent_same_mode(Rest@1)];

        [] ->
            []
    end.

-file("src/qrkit/internal/segment.gleam", 135).
?DOC(false).
-spec normalise_segments(list(segment())) -> list(segment()).
normalise_segments(Segments) ->
    _pipe = Segments,
    _pipe@1 = merge_numeric_with_alphanumeric(_pipe),
    merge_adjacent_same_mode(_pipe@1).

-file("src/qrkit/internal/segment.gleam", 38).
?DOC(false).
-spec optimise(binary(), integer(), qrkit@types:mode_preference()) -> {ok,
        list(segment())} |
    {error, qrkit@error:encode_error()}.
optimise(Text, Version, Preference) ->
    case Preference of
        force_byte ->
            {ok, [build_segment(byte, Text, 0)]};

        auto ->
            Greedy = begin
                _pipe = qrkit@internal@util:characters(Text),
                _pipe@1 = greedy_segments(_pipe, 0, [], none),
                normalise_segments(_pipe@1)
            end,
            Single_byte = [build_segment(byte, Text, 0)],
            case encoded_bits(Greedy, Version, none) =< encoded_bits(
                Single_byte,
                Version,
                none
            ) of
                true ->
                    {ok, Greedy};

                false ->
                    {ok, Single_byte}
            end
    end.

-file("src/qrkit/internal/segment.gleam", 219).
?DOC(false).
-spec do_append_segments(
    qrkit@internal@bitstream:bit_stream(),
    list(segment()),
    integer()
) -> {ok, qrkit@internal@bitstream:bit_stream()} |
    {error, qrkit@error:encode_error()}.
do_append_segments(Stream, Segments, Version) ->
    case Segments of
        [] ->
            {ok, Stream};

        [Segment | Rest] ->
            case qrkit@internal@mode:encode(
                data(Segment),
                mode(Segment),
                segment_index(Segment)
            ) of
                {ok, Bits} ->
                    do_append_segments(
                        begin
                            _pipe = qrkit@internal@bitstream:append_bits(
                                Stream,
                                qrkit@internal@mode:mode_bits(mode(Segment)),
                                4
                            ),
                            _pipe@1 = qrkit@internal@bitstream:append_bits(
                                _pipe,
                                count(Segment),
                                qrkit@internal@mode:char_count_bits(
                                    mode(Segment),
                                    Version
                                )
                            ),
                            qrkit@internal@bitstream:append_bytes(_pipe@1, Bits)
                        end,
                        Rest,
                        Version
                    );

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

-file("src/qrkit/internal/segment.gleam", 81).
?DOC(false).
-spec append_to_stream(
    qrkit@internal@bitstream:bit_stream(),
    list(segment()),
    integer(),
    gleam@option:option(integer())
) -> {ok, qrkit@internal@bitstream:bit_stream()} |
    {error, qrkit@error:encode_error()}.
append_to_stream(Stream, Segments, Version, Eci) ->
    With_eci = case Eci of
        none ->
            {ok, Stream};

        {some, Value} ->
            append_eci(Stream, Value)
    end,
    case With_eci of
        {error, Error} ->
            {error, Error};

        {ok, Stream_with_eci} ->
            do_append_segments(Stream_with_eci, Segments, Version)
    end.