 
   Must be given a nonempty args list - get_item called before this should return if len(args)<1
   """
-  @spec get_currency(String.t, nonempty_list(String.t)) :: :ok
-  def get_currency(username, args) do
+  @spec get_currency(Exedra.User.Data, nonempty_list(String.t)) :: :ok
+  def get_currency(player, args) do
+    # TODO add room arg, since everything calling this has it? Or wait until Mnesia is added?
     # TODO: combine with get_item() to only call Integer.parse, User.get once.
     [num_or_noun|noun_rest] = args
     case Integer.parse(num_or_noun) do
       {num, _} ->
-        if length(noun_rest) < 1 do
-          IO.puts not_here_text()
-        else
-          noun = List.first(noun_rest)
-          get_currency_noun_num(username, noun, num)
-        end
+        get_currency_num(player, num, noun_rest)
       :error ->
         noun = num_or_noun
-        get_currency_noun_num(username, noun, :all)
-    end
+        get_currency_noun_num(player, noun, :all)
+    end
+  end
+
+  @spec get_currency_num(Exedra.User.Data, integer, list(String.t)) :: :ok
+  def get_currency_num(_, _, noun_rest) when length(noun_rest) < 1, do: IO.puts not_here_text()
+  def get_currency_num(player, num, noun_rest) do
+    noun = List.first(noun_rest)
+    get_currency_noun_num(player, noun, num)
   end
 
   @doc """
   Checks if the given noun is an alias for currency, and gets the requested amount, which may be :all
   """
-  @spec get_currency_noun_num(String.t, String.t, pos_integer|:all) :: :ok
-  def get_currency_noun_num(username, noun, num) do
-    {:ok, player} = Exedra.User.get(username)
+  @spec get_currency_noun_num(Exedra.User.Data, String.t, pos_integer|:all) :: :ok
+  def get_currency_noun_num(player, noun, num) do
     {:ok, room} = Exedra.Room.get(player.room_id)
     if !MapSet.member?(currency_nouns(), noun) || room.currency == 0 do
       IO.puts not_here_text()

    end
  end

  def currency_text_singular() do
    "gold coin"
  end

  def currency_text_plural() do
    "gold coins"
  end

  def currency_color() do
    Exedra.ANSI.colors[:yellow]
  end

  def items(username) do
    {:ok, player} = Exedra.User.get(username)

    end
  end

  def create_no_name_desc_msg(), do: "You must specify a name and brief description."
  def create_msg(brief_description), do: "A " <> brief_description <> " forms in your hands."

  @spec create_npc(String.t, list(String.t)) :: :ok
  def create_npc(_, args) when length(args) < 2, do: IO.puts create_no_name_desc_msg()
  def create_npc(username, args) when length(args) >= 2 do
    [name | description_list] = args
    brief_description = Enum.join(description_list, " ")
    {:ok, player} = Exedra.User.get(username)
    Exedra.NPC.create(player, name, brief_description)
    IO.puts create_msg(brief_description)
  end

  @spec create_item(String.t, list(String.t)) :: :ok
  def create_item(_, args) when length(args) < 2, do: IO.puts create_no_name_desc_msg()
  def create_item(username, args) do
    [name | description_list] = args
    brief_description = Enum.join(description_list, " ")
    {:ok, player} = Exedra.User.get(username)
    Exedra.Item.create(player, name, brief_description)
    IO.puts create_msg(brief_description)
  end

  def create_currency_no_name_desc_msg(), do: "You must specify a quantity."
  def create_currency_no_num_str(), do: "HOW many?"
  def create_currency_msg(num_str) do
    if num_str == "1" do
      num_str <> " " <> currency_text_singular() <> " materializes in your hands."
    else
      num_str <> " " <> currency_text_plural() <> " materialize in your hands."
    end
  end


  @spec create_currency(String.t, list(String.t)) :: :ok
  def create_currency(_, args) when length(args) < 1, do: IO.puts create_currency_no_name_desc_msg()
  def create_currency(username, args) do
    num_str = List.first(args)
    case Integer.parse(num_str) do
      :error ->
        IO.puts create_currency_no_num_str()
      {num, _} ->
        {:ok, player} = Exedra.User.get(username)
        Exedra.User.set(%{player | currency: player.currency + num}) # TODO atomic/lock; race condition
        IO.puts create_currency_msg(num_str)
    end
  end

  def describe_item_too_short_msg(), do: "What do you want to describe?"
  def describe_item_no_item_msg(),   do: "You are not carrying that."
  def room_describe_item_describe_msg(brief),  do: "A vision of " <> brief <> " on the ground flashes in your mind's eye."
  def describe_item_describe_msg(brief),  do: "A vision of " <> brief <> " flashes in your mind's eye."

  @spec room_describe_item(String.t, list(String.t)) :: :ok
  def room_describe_item(_, args) when length(args) < 5, do: IO.puts describe_item_too_short_msg() # len(args) >= 5 because name_or_id is arg 1, and the minimal grammatically correct description is 'article noun verb adverb', e.g. 'the sword lies here'
  def room_describe_item(username, args) do
    [name_or_id | description_words] = args
    room_description = Enum.join(description_words, " ")
    {:ok, player} = Exedra.User.get(username)
    case Integer.parse(name_or_id) do
      {id, _} ->
        room_describe_item_by_id(player, room_description, id)
      :error ->
        name = name_or_id
        room_describe_item_by_name(player, room_description, name)
    end
  end

  @spec room_describe_item_by_id(Exedra.User.Data, String.t, integer) :: :ok
  def room_describe_item_by_id(player, room_description, id) do
    if MapSet.member?(player.items, id) do
      {:ok, item} = Exedra.Item.get(id)
      Exedra.Item.set %{item | room_description: room_description}
      IO.puts room_describe_item_describe_msg(item.brief)
    else
      IO.puts describe_item_no_item_msg()
    end
  end

  @spec room_describe_item_by_name(Exedra.User, String.t, String.t) :: :ok
  def room_describe_item_by_name(player, room_description, name) do
    item_id = Enum.find player.items, fn(item_id) ->
      {:ok, item} = Exedra.Item.get(item_id)
      item.name == name
    end
    if item_id == nil do
      IO.puts describe_item_no_item_msg()
    else
      {:ok, item} = Exedra.Item.get(item_id)
      Exedra.Item.set %{item | room_description: room_description}
      IO.puts room_describe_item_describe_msg(item.brief)
    end
  end

  # TODO: abstract duplication with room_describe_item
  @spec describe_item(String.t, list(String.t)) :: :ok
  def describe_item(_, args) when length(args) < 5, do: IO.puts describe_item_too_short_msg() # len(args) >= 5 because name_or_id is arg 1, and the minimal grammatically correct description is 'article noun verb noun', e.g. 'the sword lies here'
  def describe_item(username, args) do
    [name_or_id | description_words] = args
    description = Enum.join(description_words, " ")
    {:ok, player} = Exedra.User.get(username)
    case Integer.parse(name_or_id) do
      {id, _} ->
        describe_item_by_id(player, description, id)
      :error ->
        name = name_or_id
        describe_item_by_name(player, description, name)
    end
  end

  @spec describe_item_by_id(Exedra.User.Data, String.t, integer) :: :ok
  def describe_item_by_id(player, description, id) do
    if MapSet.member?(player.items, id) do
      {:ok, item} = Exedra.Item.get(id)
      Exedra.Item.set %{item | description: description}
      IO.puts describe_item_describe_msg(item.brief)
    else
      IO.puts describe_item_no_item_msg()
    end
  end

  @spec describe_item_by_name(Exedra.User.Data, String.t, String.t) :: :ok
  def describe_item_by_name(player, description, name) do
    item_id = Enum.find player.items, fn(item_id) ->
      {:ok, item} = Exedra.Item.get(item_id)
      item.name == name
    end
    if item_id == nil do
      IO.puts describe_item_no_item_msg()
    else
      {:ok, item} = Exedra.Item.get(item_id)
      Exedra.Item.set %{item | description: description}
      IO.puts describe_item_describe_msg(item.brief)
    end
  end

