src/qrkit@internal@mode.erl

-module(qrkit@internal@mode).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/qrkit/internal/mode.gleam").
-export([mode_bits/1, char_count_bits/2, utf8_byte_length/1, character_count/2, is_numeric_char/1, numeric_bits_length/1, alphanumeric_bits_length/1, data_bits_length/2, numeric_increment_cost/1, alphanumeric_increment_cost/1, is_kanji_char/1, is_alphanumeric_char/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/mode.gleam", 18).
?DOC(false).
-spec mode_bits(qrkit@types:mode()) -> integer().
mode_bits(Mode) ->
    case Mode of
        numeric ->
            2#0001;

        alphanumeric ->
            2#0010;

        byte ->
            2#0100;

        kanji ->
            2#1000
    end.

-file("src/qrkit/internal/mode.gleam", 27).
?DOC(false).
-spec char_count_bits(qrkit@types:mode(), integer()) -> integer().
char_count_bits(Mode, Version) ->
    case Version < 10 of
        true ->
            case Mode of
                numeric ->
                    10;

                alphanumeric ->
                    9;

                byte ->
                    8;

                kanji ->
                    8
            end;

        false ->
            case Version < 27 of
                true ->
                    case Mode of
                        numeric ->
                            12;

                        alphanumeric ->
                            11;

                        byte ->
                            16;

                        kanji ->
                            10
                    end;

                false ->
                    case Mode of
                        numeric ->
                            14;

                        alphanumeric ->
                            13;

                        byte ->
                            16;

                        kanji ->
                            12
                    end
            end
    end.

-file("src/qrkit/internal/mode.gleam", 72).
?DOC(false).
-spec utf8_byte_length(binary()) -> integer().
utf8_byte_length(Text) ->
    _pipe = Text,
    _pipe@1 = gleam_stdlib:identity(_pipe),
    erlang:byte_size(_pipe@1).

-file("src/qrkit/internal/mode.gleam", 56).
?DOC(false).
-spec character_count(binary(), qrkit@types:mode()) -> integer().
character_count(Data, Mode) ->
    case Mode of
        byte ->
            utf8_byte_length(Data);

        _ ->
            erlang:length(qrkit@internal@util:characters(Data))
    end.

-file("src/qrkit/internal/mode.gleam", 76).
?DOC(false).
-spec is_numeric_char(binary()) -> boolean().
is_numeric_char(Char) ->
    case gleam_stdlib:parse_int(Char) of
        {ok, Value} ->
            (Value >= 0) andalso (Value =< 9);

        {error, _} ->
            false
    end.

-file("src/qrkit/internal/mode.gleam", 104).
?DOC(false).
-spec numeric_bits_length(integer()) -> integer().
numeric_bits_length(Length) ->
    (10 * (Length div 3)) + case Length rem 3 of
        0 ->
            0;

        1 ->
            4;

        _ ->
            7
    end.

-file("src/qrkit/internal/mode.gleam", 114).
?DOC(false).
-spec alphanumeric_bits_length(integer()) -> integer().
alphanumeric_bits_length(Length) ->
    (11 * (Length div 2)) + case Length rem 2 of
        0 ->
            0;

        _ ->
            6
    end.

-file("src/qrkit/internal/mode.gleam", 63).
?DOC(false).
-spec data_bits_length(binary(), qrkit@types:mode()) -> integer().
data_bits_length(Data, Mode) ->
    case Mode of
        numeric ->
            numeric_bits_length(
                erlang:length(qrkit@internal@util:characters(Data))
            );

        alphanumeric ->
            alphanumeric_bits_length(
                erlang:length(qrkit@internal@util:characters(Data))
            );

        byte ->
            utf8_byte_length(Data) * 8;

        kanji ->
            erlang:length(qrkit@internal@util:characters(Data)) * 13
    end.

-file("src/qrkit/internal/mode.gleam", 123).
?DOC(false).
-spec numeric_increment_cost(integer()) -> integer().
numeric_increment_cost(Mod3) ->
    case Mod3 of
        0 ->
            4;

        _ ->
            3
    end.

-file("src/qrkit/internal/mode.gleam", 130).
?DOC(false).
-spec alphanumeric_increment_cost(integer()) -> integer().
alphanumeric_increment_cost(Mod2) ->
    case Mod2 of
        0 ->
            6;

        _ ->
            5
    end.

-file("src/qrkit/internal/mode.gleam", 200).
?DOC(false).
-spec encode_byte(binary()) -> bitstring().
encode_byte(Data) ->
    gleam_stdlib:identity(Data).

-file("src/qrkit/internal/mode.gleam", 241).
?DOC(false).
-spec do_alphanumeric_value(list(binary()), binary(), integer()) -> {ok,
        integer()} |
    {error, nil}.
do_alphanumeric_value(Chars, Needle, Index) ->
    case Chars of
        [] ->
            {error, nil};

        [Char | Rest] ->
            case Char =:= Needle of
                true ->
                    {ok, Index};

                false ->
                    do_alphanumeric_value(Rest, Needle, Index + 1)
            end
    end.

-file("src/qrkit/internal/mode.gleam", 256).
?DOC(false).
-spec must_parse(binary()) -> integer().
must_parse(Text) ->
    case gleam_stdlib:parse_int(Text) of
        {ok, Value} ->
            Value;

        {error, _} ->
            0
    end.

-file("src/qrkit/internal/mode.gleam", 142).
?DOC(false).
-spec do_encode_numeric(list(binary()), qrkit@internal@bitstream:bit_stream()) -> qrkit@internal@bitstream:bit_stream().
do_encode_numeric(Chars, Stream) ->
    case Chars of
        [A, B, C | Rest] ->
            Value = must_parse(<<<<A/binary, B/binary>>/binary, C/binary>>),
            do_encode_numeric(
                Rest,
                qrkit@internal@bitstream:append_bits(Stream, Value, 10)
            );

        [A@1, B@1] ->
            Value@1 = must_parse(<<A@1/binary, B@1/binary>>),
            qrkit@internal@bitstream:append_bits(Stream, Value@1, 7);

        [A@2] ->
            Value@2 = must_parse(A@2),
            qrkit@internal@bitstream:append_bits(Stream, Value@2, 4);

        [] ->
            Stream
    end.

-file("src/qrkit/internal/mode.gleam", 137).
?DOC(false).
-spec encode_numeric(binary()) -> bitstring().
encode_numeric(Data) ->
    _pipe = do_encode_numeric(
        qrkit@internal@util:characters(Data),
        qrkit@internal@bitstream:new()
    ),
    qrkit@internal@bitstream:to_bit_array(_pipe).

-file("src/qrkit/internal/mode.gleam", 263).
?DOC(false).
-spec is_ok({ok, any()} | {error, any()}) -> boolean().
is_ok(Result) ->
    case Result of
        {ok, _} ->
            true;

        {error, _} ->
            false
    end.

-file("src/qrkit/internal/mode.gleam", 304).
?DOC(false).
-spec katakana_sjis(integer()) -> integer().
katakana_sjis(Codepoint) ->
    Offset = Codepoint - 16#30A1,
    case Offset >= 63 of
        true ->
            (16#8340 + Offset) + 1;

        false ->
            16#8340 + Offset
    end.

-file("src/qrkit/internal/mode.gleam", 312).
?DOC(false).
-spec punctuation_sjis(integer()) -> {ok, integer()} | {error, nil}.
punctuation_sjis(Codepoint) ->
    case Codepoint of
        16#3000 ->
            {ok, 16#8140};

        16#3001 ->
            {ok, 16#8141};

        16#3002 ->
            {ok, 16#8142};

        16#30FC ->
            {ok, 16#815B};

        _ ->
            {error, nil}
    end.

-file("src/qrkit/internal/mode.gleam", 281).
?DOC(false).
-spec codepoint_to_sjis(integer()) -> {ok, integer()} | {error, nil}.
codepoint_to_sjis(Codepoint) ->
    case (Codepoint >= 16#3041) andalso (Codepoint =< 16#3093) of
        true ->
            {ok, (16#829F + Codepoint) - 16#3041};

        false ->
            case (Codepoint >= 16#30A1) andalso (Codepoint =< 16#30F6) of
                true ->
                    {ok, katakana_sjis(Codepoint)};

                false ->
                    case (Codepoint >= 16#FF10) andalso (Codepoint =< 16#FF19) of
                        true ->
                            {ok, (16#824F + Codepoint) - 16#FF10};

                        false ->
                            case (Codepoint >= 16#FF21) andalso (Codepoint =< 16#FF3A) of
                                true ->
                                    {ok, (16#8260 + Codepoint) - 16#FF21};

                                false ->
                                    case (Codepoint >= 16#FF41) andalso (Codepoint
                                    =< 16#FF5A) of
                                        true ->
                                            {ok,
                                                (16#8281 + Codepoint) - 16#FF41};

                                        false ->
                                            punctuation_sjis(Codepoint)
                                    end
                            end
                    end
            end
    end.

-file("src/qrkit/internal/mode.gleam", 270).
?DOC(false).
-spec to_sjis(binary()) -> {ok, integer()} | {error, nil}.
to_sjis(Char) ->
    case qrkit@internal@util:characters(Char) of
        [First] ->
            case gleam@string:to_utf_codepoints(First) of
                [Codepoint] ->
                    codepoint_to_sjis(gleam_stdlib:identity(Codepoint));

                _ ->
                    {error, nil}
            end;

        _ ->
            {error, nil}
    end.

-file("src/qrkit/internal/mode.gleam", 87).
?DOC(false).
-spec is_kanji_char(binary()) -> boolean().
is_kanji_char(Char) ->
    _pipe = Char,
    _pipe@1 = to_sjis(_pipe),
    is_ok(_pipe@1).

-file("src/qrkit/internal/mode.gleam", 211).
?DOC(false).
-spec do_encode_kanji(
    list(binary()),
    integer(),
    qrkit@internal@bitstream:bit_stream()
) -> {ok, bitstring()} | {error, qrkit@error:encode_error()}.
do_encode_kanji(Chars, Index, Stream) ->
    case Chars of
        [Char | Rest] ->
            case to_sjis(Char) of
                {ok, Sjis} ->
                    Adjusted = case (Sjis >= 16#8140) andalso (Sjis =< 16#9FFC) of
                        true ->
                            Sjis - 16#8140;

                        false ->
                            Sjis - 16#C140
                    end,
                    Value = ((Adjusted div 256) * 16#C0) + (Adjusted rem 256),
                    do_encode_kanji(
                        Rest,
                        Index + 1,
                        qrkit@internal@bitstream:append_bits(Stream, Value, 13)
                    );

                {error, _} ->
                    {error, {unsupported_character, Index, Char}}
            end;

        [] ->
            {ok, qrkit@internal@bitstream:to_bit_array(Stream)}
    end.

-file("src/qrkit/internal/mode.gleam", 204).
?DOC(false).
-spec encode_kanji(binary(), integer()) -> {ok, bitstring()} |
    {error, qrkit@error:encode_error()}.
encode_kanji(Data, Index) ->
    do_encode_kanji(
        qrkit@internal@util:characters(Data),
        Index,
        qrkit@internal@bitstream:new()
    ).

-file("src/qrkit/internal/mode.gleam", 237).
?DOC(false).
-spec alphanumeric_value(binary()) -> {ok, integer()} | {error, nil}.
alphanumeric_value(Char) ->
    do_alphanumeric_value(
        [<<"0"/utf8>>,
            <<"1"/utf8>>,
            <<"2"/utf8>>,
            <<"3"/utf8>>,
            <<"4"/utf8>>,
            <<"5"/utf8>>,
            <<"6"/utf8>>,
            <<"7"/utf8>>,
            <<"8"/utf8>>,
            <<"9"/utf8>>,
            <<"A"/utf8>>,
            <<"B"/utf8>>,
            <<"C"/utf8>>,
            <<"D"/utf8>>,
            <<"E"/utf8>>,
            <<"F"/utf8>>,
            <<"G"/utf8>>,
            <<"H"/utf8>>,
            <<"I"/utf8>>,
            <<"J"/utf8>>,
            <<"K"/utf8>>,
            <<"L"/utf8>>,
            <<"M"/utf8>>,
            <<"N"/utf8>>,
            <<"O"/utf8>>,
            <<"P"/utf8>>,
            <<"Q"/utf8>>,
            <<"R"/utf8>>,
            <<"S"/utf8>>,
            <<"T"/utf8>>,
            <<"U"/utf8>>,
            <<"V"/utf8>>,
            <<"W"/utf8>>,
            <<"X"/utf8>>,
            <<"Y"/utf8>>,
            <<"Z"/utf8>>,
            <<" "/utf8>>,
            <<"$"/utf8>>,
            <<"%"/utf8>>,
            <<"*"/utf8>>,
            <<"+"/utf8>>,
            <<"-"/utf8>>,
            <<"."/utf8>>,
            <<"/"/utf8>>,
            <<":"/utf8>>],
        Char,
        0
    ).

-file("src/qrkit/internal/mode.gleam", 83).
?DOC(false).
-spec is_alphanumeric_char(binary()) -> boolean().
is_alphanumeric_char(Char) ->
    _pipe = Char,
    _pipe@1 = alphanumeric_value(_pipe),
    is_ok(_pipe@1).

-file("src/qrkit/internal/mode.gleam", 170).
?DOC(false).
-spec do_encode_alphanumeric(
    list(binary()),
    integer(),
    qrkit@internal@bitstream:bit_stream()
) -> {ok, bitstring()} | {error, qrkit@error:encode_error()}.
do_encode_alphanumeric(Chars, Index, Stream) ->
    case Chars of
        [A, B | Rest] ->
            case {alphanumeric_value(A), alphanumeric_value(B)} of
                {{ok, First}, {ok, Second}} ->
                    do_encode_alphanumeric(
                        Rest,
                        Index + 2,
                        qrkit@internal@bitstream:append_bits(
                            Stream,
                            (First * 45) + Second,
                            11
                        )
                    );

                {{error, _}, _} ->
                    {error, {unsupported_character, Index, A}};

                {_, {error, _}} ->
                    {error, {unsupported_character, Index + 1, B}}
            end;

        [A@1] ->
            case alphanumeric_value(A@1) of
                {ok, Value} ->
                    {ok,
                        begin
                            _pipe = qrkit@internal@bitstream:append_bits(
                                Stream,
                                Value,
                                6
                            ),
                            qrkit@internal@bitstream:to_bit_array(_pipe)
                        end};

                {error, _} ->
                    {error, {unsupported_character, Index, A@1}}
            end;

        [] ->
            {ok, qrkit@internal@bitstream:to_bit_array(Stream)}
    end.

-file("src/qrkit/internal/mode.gleam", 163).
?DOC(false).
-spec encode_alphanumeric(binary(), integer()) -> {ok, bitstring()} |
    {error, qrkit@error:encode_error()}.
encode_alphanumeric(Data, Index) ->
    do_encode_alphanumeric(
        qrkit@internal@util:characters(Data),
        Index,
        qrkit@internal@bitstream:new()
    ).

-file("src/qrkit/internal/mode.gleam", 91).
?DOC(false).
-spec encode(binary(), qrkit@types:mode(), integer()) -> {ok, bitstring()} |
    {error, qrkit@error:encode_error()}.
encode(Data, Mode, Index) ->
    case Mode of
        numeric ->
            {ok, encode_numeric(Data)};

        alphanumeric ->
            encode_alphanumeric(Data, Index);

        byte ->
            {ok, encode_byte(Data)};

        kanji ->
            encode_kanji(Data, Index)
    end.