 
 defmodule Math do
   @doc """
-  Module to provide some simple mathematic operation
+  Module to provide some simple mathematic operations
   """
+  def sum(list), do: sum(list, 0)
   def sum([], acc), do: acc
   def sum(int, acc) when is_integer(int), do: sum([int], acc)
   def sum([h | t] = list, acc) when is_list(list) and is_integer(h), do: sum(t, acc + h)
   def sum([h | t] = list, acc) when is_list(list), do: sum(t, acc + String.to_integer(h))
+
+  def multiply(list), do: multiply(list, 1)
+  def multiply([], acc), do: acc
+  def multiply(int, acc) when is_integer(int), do: multiply([int], acc)
+  def multiply([h | t] = list, acc) when is_list(list) and is_integer(h), do: multiply(t, acc * h)
+  def multiply([h | t] = list, acc) when is_list(list), do: multiply(t, acc * String.to_integer(h))
 end
 
 alias RPC.Callee

    This Caller need to be initialised with the `procedure` where the Callee is subscribed 
    and the `params` used within the remote procedure

    iex> Caller.start_link("com.spell.rpc.sum", [arguments: [1, 2, 3]])
    """
    require Logger
    defstruct [:caller, :procedure, :params, interval: 1500]

  end

  defmodule Callee do
    use GenServer
    @moduledoc """
    The callee need to be registered to the WAMP Dealer in order to be accesseble and expose
    the procedure. 
    
    iex> Callee.start_link()

    Once it is registered it will wait an invocation from the Dealer and yield 
    the result back. in this case we have two function exposed:

    - `spell.math.sum`
    - `spell.math.multiply`
    """

    @doc """
    Function that return the map of the functions exposed to the remote procedure
    """
    def function_list do
      %{ 
        "spell.math.sum" => &Math.sum/1,
        "spell.math.multiply" => &Math.multiply/1
      }
    end

    require Logger
    defstruct [:callee, register: %{}, timeout: 1000]

    # Public Functions

    @doc """
    Start the callee with the passed procedure.
    """
    def start_link(options \\ []) do
      GenServer.start_link(__MODULE__, [options])
    end

    # Private Functions

    def init(options) do
      {:ok, callee} = RPC.new_peer([Spell.Role.Callee], options)

      # `Spell.cast_register` receive the callee and the procedure where it
      # has to be registered
      state = %__MODULE__{callee: callee}

      new_register = for {proc, function} <- function_list do
        {:ok, reg_id} = Spell.call_register(callee, proc)
        {reg_id, function}
      end
      |> Enum.into %{}

      state = Map.put(state, :register, new_register)
      {:ok, state}
    end

    def handle_info({Spell.Peer, pid, %Spell.Message{args: [request, reg_id, _msg, params], type: :invocation}}, (%{callee: callee}) = state) do
      rpc = state.register[reg_id]
      Spell.cast_yield(callee, request, [arguments_kw: %{result: rpc.(params)}])
      {:noreply, state}
    end
  end

