defmodule CodeLock do def start_link(code) when is_list(code) do :gen_statem.start_link({:local, :code_lock}, __MODULE__, code, []) end def button(digit) when is_integer(digit) do :gen_statem.cast(:code_lock, {:button, digit}) end def init(code) do do_lock() :erlang.start_timer(10000, self(), :kabum) {:ok, :locked, %{code: code, remaining: code}} end def callback_mode() do [:state_functions, :state_enter] end def locked(:enter, _old_state, _data) do IO.puts("Entered 'locked' state") :keep_state_and_data end def locked(:info, {:timeout, _timer, _key}, _data) do IO.puts("Kabum in 'locked' state") :keep_state_and_data end def locked(:cast, {:button, digit}, %{remaining: [digit]} = data) do do_unlock() {:next_state, :open, %{data | remaining: []}, [{:state_timeout, 5000, :lock}]} end def locked(:cast, {:button, digit}, %{remaining: [digit|rest]} = data) do {:next_state, :locked, %{data | remaining: rest}, [{:state_timeout, 5000, :lock}]} end def locked(:cast, {:button, _digit}, %{code: code} = data) do {:next_state, :locked, %{data | remaining: code}} end def locked(:state_timeout, :lock, %{code: code} = data) do do_lock() {:repeat_state, %{data | remaining: code}} end def open(:enter, _old_state, _data) do IO.puts("Entered 'open' state") :keep_state_and_data end def open(:state_timeout, :lock, %{code: code} = data) do do_lock() {:next_state, :locked, %{data | remaining: code}} end def open(:info, {:timeout, _timer, _key}, _data) do IO.puts("Kabum in 'open' state") :keep_state_and_data end def do_lock() do IO.puts("Lock") end def do_unlock() do IO.puts("Unlock") end def terminate(_reason, state, _data) do if(state == :locked, do: do_lock()) :ok end def code_change(_vsn, state, data, _extra) do {:ok, state, data} end end