<commit_before>-module(grisp_led).

-behaviour(gen_server).

% API
-export([start_link/0]).
-export([color/2]).
-export([off/1]).
-export([flash/3]).
-export([pattern/2]).

% Callbacks
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([code_change/3]).
-export([terminate/2]).

%--- Records -------------------------------------------------------------------

-record(state, {driver, leds}).

%--- Macros --------------------------------------------------------------------

-define(is_component(C), C >= 0 andalso C =< 1).

%--- API -----------------------------------------------------------------------

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, undefined, []).

color(Pos, Color) -> pattern(Pos, [{infinity, Color}]).

off(Pos) -> pattern(Pos, [{infinity, black}]).

flash(Pos, Color, Interval) ->
    pattern(Pos, [{Interval, Color}, {Interval, black}]).

pattern(Pos, Pattern) -> gen_server:cast(?MODULE, {pattern, Pos, Pattern}).

%--- Callbacks -----------------------------------------------------------------

init(undefined) ->
    {ok, #state{leds = [
        {1, {[{infinity, black}], undefined}},
        {2, {[{infinity, black}], undefined}}
    ]}}.

handle_call(Request, From, _State) -> error({unknown_call, Request, From}).

handle_cast({pattern, Pos, NewPattern}, State) ->
    NewState = update_led(Pos, State, fun({_OldPattern, Timer}) ->
        tick_pattern(Pos, {NewPattern, Timer})
    end),
    {noreply, NewState}.

handle_info({tick, Pos}, State) ->
    NewState = update_led(Pos, State, fun(Led) ->
        tick_pattern(Pos, Led)
    end),
    {noreply, NewState}.

code_change(_OldVsn, State, _Extra) -> {ok, State}.

terminate(_Reason, _State) -> ok.

%--- Internal ------------------------------------------------------------------

update_led(Pos, #state{leds = Leds} = State, Fun) ->
    State#state{leds = mod(Pos, Fun, Leds)}.

mod(Pos, Fun, [{Pos, Led}|Rest]) -> [{Pos, Fun(Led)}|Rest];
mod(Pos, Fun, [Led|Rest])        -> [Led|mod(Pos, Fun, Rest)];
mod(Pos, _Fun, [])               -> error({led_not_found, Pos}).

tick_pattern(Pos, {[{infinity, Color} = Pattern|_Rest], Timer}) ->
    cancel_timer(Timer),
    write_color(Pos, Color),
    {[Pattern], undefined};
tick_pattern(Pos, {[{Time, Color} = Step|Rest], Timer}) ->
    cancel_timer(Timer),
    write_color(Pos, Color),
    NewTimer = erlang:send_after(Time, self(), {tick, Pos}),
    {Rest ++ [Step], NewTimer}.

cancel_timer(undefined) -> ok;
cancel_timer(Timer)     -> erlang:cancel_timer(Timer).

write_color(Pos, Color) ->
    {R, G, B} = translate(Color),<commit_msg>Fix for #2. Intervals < 1 lead to function clause error now<commit_after>-module(grisp_led).

-behaviour(gen_server).

% API
-export([start_link/0]).
-export([color/2]).
-export([off/1]).
-export([flash/3]).
-export([pattern/2]).

% Callbacks
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([code_change/3]).
-export([terminate/2]).

%--- Records -------------------------------------------------------------------

-record(state, {driver, leds}).

%--- Macros --------------------------------------------------------------------

-define(is_component(C), C >= 0 andalso C =< 1).

%--- API -----------------------------------------------------------------------

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, undefined, []).

color(Pos, Color) -> pattern(Pos, [{infinity, Color}]).

off(Pos) -> pattern(Pos, [{infinity, black}]).

flash(Pos, Color, Interval) ->
    pattern(Pos, [{Interval, Color}, {Interval, black}]).

pattern(Pos, Pattern) -> gen_server:cast(?MODULE, {pattern, Pos, Pattern}).

%--- Callbacks -----------------------------------------------------------------

init(undefined) ->
    {ok, #state{leds = [
        {1, {[{infinity, black}], undefined}},
        {2, {[{infinity, black}], undefined}}
    ]}}.

handle_call(Request, From, _State) -> error({unknown_call, Request, From}).

handle_cast({pattern, Pos, NewPattern}, State) ->
    NewState = update_led(Pos, State, fun({_OldPattern, Timer}) ->
        tick_pattern(Pos, {NewPattern, Timer})
    end),
    {noreply, NewState}.

handle_info({tick, Pos}, State) ->
    NewState = update_led(Pos, State, fun(Led) ->
        tick_pattern(Pos, Led)
    end),
    {noreply, NewState}.

code_change(_OldVsn, State, _Extra) -> {ok, State}.

terminate(_Reason, _State) -> ok.

%--- Internal ------------------------------------------------------------------

update_led(Pos, #state{leds = Leds} = State, Fun) ->
    State#state{leds = mod(Pos, Fun, Leds)}.

mod(Pos, Fun, [{Pos, Led}|Rest]) -> [{Pos, Fun(Led)}|Rest];
mod(Pos, Fun, [Led|Rest])        -> [Led|mod(Pos, Fun, Rest)];
mod(Pos, _Fun, [])               -> error({led_not_found, Pos}).

tick_pattern(Pos, {[{infinity, Color} = Pattern|_Rest], Timer}) ->
    cancel_timer(Timer),
    write_color(Pos, Color),
    {[Pattern], undefined};
tick_pattern(Pos, {[{Time, Color} = Step|Rest], Timer}) when Time >= 1 ->
    cancel_timer(Timer),
    write_color(Pos, Color),
    NewTimer = erlang:send_after(Time, self(), {tick, Pos}),
    {Rest ++ [Step], NewTimer}.

cancel_timer(undefined) -> ok;
cancel_timer(Timer)     -> erlang:cancel_timer(Timer).

write_color(Pos, Color) ->
    {R, G, B} = translate(Color),
{'fraction_non_alphanumeric': 0.31269811672571324, 'fraction_numerical': 0.006153272422151781, 'mean_word_length': 2.2807339449541284, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 50, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6768903', 'n_tokens_mistral': 1741, 'n_tokens_neox': 1686, 'n_words': 352}