Handle rebar3 sub-dependencies without a requirement

defmodule Mix.Rebar do
  @moduledoc false

  @doc """
  Returns the path supposed to host the local copy of rebar.
  """
  def local_rebar_path(manager) do
    Path.join(Mix.Utils.mix_home, Atom.to_string(manager))
  end

  @doc """
  Returns the path to the global copy of `rebar`, if one exists.
  """
  def global_rebar_cmd(manager) do
    if cmd = System.find_executable(Atom.to_string(manager)) do
      wrap_cmd(cmd)
    end
  end

  @doc """
  Returns the path to the local copy of `rebar`, if one exists.
  """
  def local_rebar_cmd(manager) do
    cmd = local_rebar_path(manager)
    if File.regular?(cmd) do
      wrap_cmd(cmd)
    end
  end

  @doc """
  Returns the path to the available `rebar` command.
  """
  def rebar_cmd(manager) do
    global_rebar_cmd(manager) || local_rebar_cmd(manager)
  end

  @doc """
  Loads `rebar.config` and evaluates `rebar.config.script` if it
  exists in the given directory.
  """
  def load_config(dir) do
    config_path = Path.join(dir, "rebar.config")
    script_path = Path.join(dir, "rebar.config.script")

    config = case :file.consult(config_path) do
      {:ok, config} ->
        config
      {:error, :enoent} ->
        []
      {:error, error} ->
        reason = :file.format_error(error)
        Mix.raise "Error consulting rebar config #{inspect config_path}: #{reason}"
    end

    if File.exists?(script_path) do
      eval_script(script_path, config)
    else
      config
    end
  end

  @doc """
  Merges a rebar3 parent config with a child config.
  """
  # From https://github.com/rebar/rebar3/blob/b1da2ec0674df89599564252734bd4d794436425/src/rebar_opts.erl#L103
  def merge_config(old, new) do
    Keyword.merge(old, new, fn
      :deps, old, _new               -> old
      {:deps, _}, _old, new          -> new
      :plugins, _old, new            -> new
      {:plugins, _}, _old, new       -> new
      :profiles, old, new            -> merge_config(old, new)
      :mib_first_files, value, value -> value
      :mib_first_files, old, new     -> old ++ new
      :relx, old, new                -> tuple_merge(new, old)
      _key, old, new when is_list(new) ->
        case :io_lib.printable_list(new) do
          true when new == [] ->
            if :io_lib.printable_list(old), do: new, else: old
          true ->
            new
          false ->
            tuple_merge(old, new)
        end
      _key, _old, new -> new
    end)
  end

  # From https://github.com/rebar/rebar3/blob/b1da2ec0674df89599564252734bd4d794436425/src/rebar_utils.erl#L282
  defp tuple_merge(old, new),
    do: do_tuple_merge(tuple_sort(old), tuple_sort(new))

  defp do_tuple_merge(old, []),
    do: old
  defp do_tuple_merge(olds, [new|news]),
    do: do_tuple_umerge_dedup(umerge(:new, olds, [], news, new), [])

  defp umerge(_, [], [], acc, current),
    do: [current|acc]
  defp umerge(:new, [], news, acc, current),
    do: Enum.reverse(news, [current|acc])
  defp umerge(:old, olds, [], acc, current),
    do: Enum.reverse(olds, [current|acc])
  defp umerge(:new, [old|olds], news, acc, current) do
    {dir, merged, new_current} = compare({:new, current}, {:old, old})
    umerge(dir, olds, news, [merged|acc], new_current)
  end
  defp umerge(:old, olds, [new|news], acc, current) do
    {dir, merged, new_current} = compare({:new, new}, {:old, current})
    umerge(dir, olds, news, [merged|acc], new_current)
  end

  defp compare({priority, a}, {secondary, b}) when is_tuple(a) and is_tuple(b) do
    ka = elem(a, 0)
    kb = elem(b, 0)
    cond do
      ka == kb -> {secondary, a, b}
      ka  < kb -> {secondary, a, b}
      ka  > kb -> {priority, b, a}
    end
  end
  defp compare({priority, a}, {secondary, b}) when not is_tuple(a) and not is_tuple(b) do
    cond do
      a == b -> {secondary, a, b}
      a  < b -> {secondary, a, b}
      a  > b -> {priority, b, a}
    end
  end
  defp compare({priority, a}, {secondary, b}) when is_tuple(a) and not is_tuple(b) do
    ka = elem(a, 0)
    cond do
      ka == b -> {secondary, a, b}
      ka  < b -> {secondary, a, b}
      ka  > b -> {priority, b, a}
    end
  end
  defp compare({priority, a}, {secondary, b}) when not is_tuple(a) and is_tuple(b) do
    kb = elem(b, 0)
    cond do
      a == kb -> {secondary, a, b}
      a  < kb -> {secondary, a, b}
      a  > kb -> {priority, b, a}
    end
  end

  defp do_tuple_umerge_dedup([], acc), do: acc
  defp do_tuple_umerge_dedup([h|t], acc) do
    if h in t do
      do_tuple_umerge_dedup(t, acc)
    else
      do_tuple_umerge_dedup(t, [h|acc])
    end
  end

  defp tuple_sort(list) do
    Enum.sort(list, fn
      a, b when is_tuple(a) and is_tuple(b) -> elem(a, 0) <= elem(b, 0)
      a, b when is_tuple(a) -> elem(a, 0) <= b
      a, b when is_tuple(b) -> a <= elem(b, 0)
      a, b -> a <= b
    end)
  end

  @doc """
  Serializes a rebar config to a term file.
  """
  def serialize_config(config) do
    Enum.map(config, &[:io_lib.print(&1) | ".\n"])
  end

  @doc """
  Parses the dependencies in given `rebar.config` to Mix's dependency format.
  """
  def deps(app, config, overrides) do
    # We don't have to handle rebar3 profiles because dependencies
    # are always in the default profile which cannot be customized
    config = apply_overrides(app, config, overrides)
    if deps = config[:deps] do
      Enum.map(deps, &parse_dep/1)
    else
      []
    end
  end

  @doc """
  Runs `fun` for the given config and for each `sub_dirs` in the
  given rebar config.
  """
  def recur(config, fun) when is_binary(config) do
    recur(load_config(config), fun)
  end

  def recur(config, fun) do
    subs =
      (config[:sub_dirs] || [])
      |> Enum.map(&Path.wildcard(&1))
      |> Enum.concat
      |> Enum.filter(&File.dir?(&1))
      |> Enum.map(&recur(&1, fun))
      |> Enum.concat

    [fun.(config)|subs]
  end

  defp parse_dep(app) when is_atom(app) do
    parse_dep({app, ">= 0.0.0"})
  end

  defp parse_dep({app, req}) when is_list(req) do
    {app, List.to_string(req)}
  end

  defp parse_dep({app, source}) when is_tuple(source) do
    parse_dep({app, ">= 0.0.0", source, []})
  end

  defp parse_dep({app, req, source}) do
    parse_dep({app, req, source, []})
  end

  defp parse_dep({app, req, source, opts}) do
    [scm, url | source] = Tuple.to_list(source)

    ref =
      case source do
        [""|_]                -> [branch: "HEAD"]
        [{:branch, branch}|_] -> [branch: to_string(branch)]
        [{:tag, tag}|_]       -> [tag: to_string(tag)]
        [{:ref, ref}|_]       -> [ref: to_string(ref)]
        [ref|_]               -> [ref: to_string(ref)]
        _                     -> []
      end

    compile =
      if :proplists.get_value(:raw, opts, false),
        do: [compile: false],
        else: []

    mix_opts = [{scm, to_string(url)}] ++ ref ++ compile
    {app, compile_req(req), mix_opts}
  end

  defp compile_req(req) do
    case Regex.compile(List.to_string(req)) do
      {:ok, re} ->
        re
      {:error, reason} ->
        Mix.raise "Unable to compile version regex: #{inspect req}, #{reason}"
    end
  end

  defp eval_script(script_path, config) do
    script = Path.basename(script_path) |> String.to_char_list

    result = File.cd!(Path.dirname(script_path), fn ->
      :file.script(script, eval_binds(CONFIG: config, SCRIPT: script))
    end)

    case result do
      {:ok, config} ->
        config
      {:error, error} ->
        reason = :file.format_error(error)
        Mix.shell.error("Error evaluating rebar config script #{script_path}:#{reason}")
        Mix.shell.error("Any dependency defined in the script won't be available " <>
                        "unless you add them to your Mix project")
        config
    end
  end

  defp eval_binds(binds) do
    Enum.reduce(binds, :erl_eval.new_bindings, fn ({k, v}, binds) ->
      :erl_eval.add_binding(k, v, binds)
    end)
  end

  defp wrap_cmd(rebar) do
    cond do
      not match?({:win32, _}, :os.type) ->
        rebar
      String.ends_with?(rebar, ".cmd") ->
        "\"#{String.replace(rebar, "/", "\\")}\""
      true ->
        "escript.exe \"#{rebar}\""
    end
  end

  defp apply_overrides(app, config, overrides) do
    # Inefficient. We want the order we get here though.

    config =
      Enum.reduce(overrides, config, fn
        {:override, overrides}, config ->
          Enum.reduce(overrides, config, fn {key, value}, config ->
            Keyword.put(config, key, value)
          end)
        _, config ->
          config
       end)

    config =
      Enum.reduce(overrides, config, fn
        {:override, oapp, overrides}, config when oapp == app ->
          Enum.reduce(overrides, config, fn {key, value}, config ->
            Keyword.put(config, key, value)
          end)
        _, config ->
          config
      end)

    Enum.reduce(overrides, config, fn
      {:add, oapp, overrides}, config when oapp == app ->
        Enum.reduce(overrides, config, fn {key, value}, config ->
          old_value = Keyword.get(config, key, [])
          Keyword.put(config, key, value ++ old_value)
      end)
      _, config ->
        config
    end)
  end
end

defmodule Mix.Rebar do
  @moduledoc false

  @doc """
  Returns the path supposed to host the local copy of rebar.
  """
  def local_rebar_path(manager) do
    Path.join(Mix.Utils.mix_home, Atom.to_string(manager))
  end

  @doc """
  Returns the path to the global copy of `rebar`, if one exists.
  """
  def global_rebar_cmd(manager) do
    if cmd = System.find_executable(Atom.to_string(manager)) do
      wrap_cmd(cmd)
    end
  end

  @doc """
  Returns the path to the local copy of `rebar`, if one exists.
  """
  def local_rebar_cmd(manager) do
    cmd = local_rebar_path(manager)
    if File.regular?(cmd) do
      wrap_cmd(cmd)
    end
  end

  @doc """
  Returns the path to the available `rebar` command.
  """
  def rebar_cmd(manager) do
    global_rebar_cmd(manager) || local_rebar_cmd(manager)
  end

  @doc """
  Loads `rebar.config` and evaluates `rebar.config.script` if it
  exists in the given directory.
  """
  def load_config(dir) do
    config_path = Path.join(dir, "rebar.config")
    script_path = Path.join(dir, "rebar.config.script")

    config = case :file.consult(config_path) do
      {:ok, config} ->
        config
      {:error, :enoent} ->
        []
      {:error, error} ->
        reason = :file.format_error(error)
        Mix.raise "Error consulting rebar config #{inspect config_path}: #{reason}"
    end

    if File.exists?(script_path) do
      eval_script(script_path, config)
    else
      config
    end
  end

  @doc """
  Merges a rebar3 parent config with a child config.
  """
  # From https://github.com/rebar/rebar3/blob/b1da2ec0674df89599564252734bd4d794436425/src/rebar_opts.erl#L103
  def merge_config(old, new) do
    Keyword.merge(old, new, fn
      :deps, old, _new               -> old
      {:deps, _}, _old, new          -> new
      :plugins, _old, new            -> new
      {:plugins, _}, _old, new       -> new
      :profiles, old, new            -> merge_config(old, new)
      :mib_first_files, value, value -> value
      :mib_first_files, old, new     -> old ++ new
      :relx, old, new                -> tuple_merge(new, old)
      _key, old, new when is_list(new) ->
        case :io_lib.printable_list(new) do
          true when new == [] ->
            if :io_lib.printable_list(old), do: new, else: old
          true ->
            new
          false ->
            tuple_merge(old, new)
        end
      _key, _old, new -> new
    end)
  end

  # From https://github.com/rebar/rebar3/blob/b1da2ec0674df89599564252734bd4d794436425/src/rebar_utils.erl#L282
  defp tuple_merge(old, new),
    do: do_tuple_merge(tuple_sort(old), tuple_sort(new))

  defp do_tuple_merge(old, []),
    do: old
  defp do_tuple_merge(olds, [new|news]),
    do: do_tuple_umerge_dedup(umerge(:new, olds, [], news, new), [])

  defp umerge(_, [], [], acc, current),
    do: [current|acc]
  defp umerge(:new, [], news, acc, current),
    do: Enum.reverse(news, [current|acc])
  defp umerge(:old, olds, [], acc, current),
    do: Enum.reverse(olds, [current|acc])
  defp umerge(:new, [old|olds], news, acc, current) do
    {dir, merged, new_current} = compare({:new, current}, {:old, old})
    umerge(dir, olds, news, [merged|acc], new_current)
  end
  defp umerge(:old, olds, [new|news], acc, current) do
    {dir, merged, new_current} = compare({:new, new}, {:old, current})
    umerge(dir, olds, news, [merged|acc], new_current)
  end

  defp compare({priority, a}, {secondary, b}) when is_tuple(a) and is_tuple(b) do
    ka = elem(a, 0)
    kb = elem(b, 0)
    cond do
      ka == kb -> {secondary, a, b}
      ka  < kb -> {secondary, a, b}
      ka  > kb -> {priority, b, a}
    end
  end
  defp compare({priority, a}, {secondary, b}) when not is_tuple(a) and not is_tuple(b) do
    cond do
      a == b -> {secondary, a, b}
      a  < b -> {secondary, a, b}
      a  > b -> {priority, b, a}
    end
  end
  defp compare({priority, a}, {secondary, b}) when is_tuple(a) and not is_tuple(b) do
    ka = elem(a, 0)
    cond do
      ka == b -> {secondary, a, b}
      ka  < b -> {secondary, a, b}
      ka  > b -> {priority, b, a}
    end
  end
  defp compare({priority, a}, {secondary, b}) when not is_tuple(a) and is_tuple(b) do
    kb = elem(b, 0)
    cond do
      a == kb -> {secondary, a, b}
      a  < kb -> {secondary, a, b}
      a  > kb -> {priority, b, a}
    end
  end

  defp do_tuple_umerge_dedup([], acc), do: acc
  defp do_tuple_umerge_dedup([h|t], acc) do
    if h in t do
      do_tuple_umerge_dedup(t, acc)
    else
      do_tuple_umerge_dedup(t, [h|acc])
    end
  end

  defp tuple_sort(list) do
    Enum.sort(list, fn
      a, b when is_tuple(a) and is_tuple(b) -> elem(a, 0) <= elem(b, 0)
      a, b when is_tuple(a) -> elem(a, 0) <= b
      a, b when is_tuple(b) -> a <= elem(b, 0)
      a, b -> a <= b
    end)
  end

  @doc """
  Serializes a rebar config to a term file.
  """
  def serialize_config(config) do
    Enum.map(config, &[:io_lib.print(&1) | ".\n"])
  end

  @doc """
  Parses the dependencies in given `rebar.config` to Mix's dependency format.
  """
  def deps(app, config, overrides) do
    # We don't have to handle rebar3 profiles because dependencies
    # are always in the default profile which cannot be customized
    config = apply_overrides(app, config, overrides)
    if deps = config[:deps] do
      Enum.map(deps, &parse_dep/1)
    else
      []
    end
  end

  @doc """
  Runs `fun` for the given config and for each `sub_dirs` in the
  given rebar config.
  """
  def recur(config, fun) when is_binary(config) do
    recur(load_config(config), fun)
  end

  def recur(config, fun) do
    subs =
      (config[:sub_dirs] || [])
      |> Enum.map(&Path.wildcard(&1))
      |> Enum.concat
      |> Enum.filter(&File.dir?(&1))
      |> Enum.map(&recur(&1, fun))
      |> Enum.concat

    [fun.(config)|subs]
  end

  defp parse_dep(app) when is_atom(app) do
    parse_dep({app, nil})
  end

  defp parse_dep({app, req}) when is_list(req) do
    {app, List.to_string(req)}
  end

  defp parse_dep({app, source}) when is_tuple(source) do
    parse_dep({app, nil, source, []})
  end

  defp parse_dep({app, req, source}) do
    parse_dep({app, req, source, []})
  end

  defp parse_dep({app, req, source, opts}) do
    [scm, url | source] = Tuple.to_list(source)

    ref =
      case source do
        [""|_]                -> [branch: "HEAD"]
        [{:branch, branch}|_] -> [branch: to_string(branch)]
        [{:tag, tag}|_]       -> [tag: to_string(tag)]
        [{:ref, ref}|_]       -> [ref: to_string(ref)]
        [ref|_]               -> [ref: to_string(ref)]
        _                     -> []
      end

    compile =
      if :proplists.get_value(:raw, opts, false),
        do: [compile: false],
        else: []

    mix_opts = [{scm, to_string(url)}] ++ ref ++ compile
    {app, compile_req(req), mix_opts}
  end

  defp compile_req(nil) do
    ">= 0.0.0"
  end

  defp compile_req(req) do
    case Regex.compile(List.to_string(req)) do
      {:ok, re} ->
        re
      {:error, reason} ->
        Mix.raise "Unable to compile version regex: #{inspect req}, #{reason}"
    end
  end

  defp eval_script(script_path, config) do
    script = Path.basename(script_path) |> String.to_char_list

    result = File.cd!(Path.dirname(script_path), fn ->
      :file.script(script, eval_binds(CONFIG: config, SCRIPT: script))
    end)

    case result do
      {:ok, config} ->
        config
      {:error, error} ->
        reason = :file.format_error(error)
        Mix.shell.error("Error evaluating rebar config script #{script_path}:#{reason}")
        Mix.shell.error("Any dependency defined in the script won't be available " <>
                        "unless you add them to your Mix project")
        config
    end
  end

  defp eval_binds(binds) do
    Enum.reduce(binds, :erl_eval.new_bindings, fn ({k, v}, binds) ->
      :erl_eval.add_binding(k, v, binds)
    end)
  end

  defp wrap_cmd(rebar) do
    cond do
      not match?({:win32, _}, :os.type) ->
        rebar
      String.ends_with?(rebar, ".cmd") ->
        "\"#{String.replace(rebar, "/", "\\")}\""
      true ->
        "escript.exe \"#{rebar}\""
    end
  end

  defp apply_overrides(app, config, overrides) do
    # Inefficient. We want the order we get here though.

    config =
      Enum.reduce(overrides, config, fn
        {:override, overrides}, config ->
          Enum.reduce(overrides, config, fn {key, value}, config ->
            Keyword.put(config, key, value)
          end)
        _, config ->
          config
       end)

    config =
      Enum.reduce(overrides, config, fn
        {:override, oapp, overrides}, config when oapp == app ->
          Enum.reduce(overrides, config, fn {key, value}, config ->
            Keyword.put(config, key, value)
          end)
        _, config ->
          config
      end)

    Enum.reduce(overrides, config, fn
      {:add, oapp, overrides}, config when oapp == app ->
        Enum.reduce(overrides, config, fn {key, value}, config ->
          old_value = Keyword.get(config, key, [])
          Keyword.put(config, key, value ++ old_value)
      end)
      _, config ->
        config
    end)
  end
end


fix refund for poping from mailbox

fix refund for poping from mailbox

defmodule Msg do
	defstruct time: 0, msg: "", size: 0, price: 0, from: "", to: ""
end
#there should be a KV thread that this thread uses to store stuff. That way when this thread dies, the mailbox doesn't get dumped.
defmodule MailBox do
	#if you are running a node with external IP, then this thread could be generating profit for you.
  use GenServer
  @name __MODULE__
  def start_link() do GenServer.start_link(__MODULE__, :ok, [name: @name]) end
  def init(:ok) do {:ok, {%HashDict{}, 0, 0}} end
	def handle_cast({:new, pub}, {db, mailboxes, m}) do
		if	db[pub] == nil do
			{:noreply, {HashDict.put(db, pub, []), mailboxes + 1, m}}
		else
			{:noreply, {db, mailboxes, m}}
		end
	end
	def handle_cast({:del_acc, pub}, {db, mailboxes, messages}) do
		if db[pub] == nil do
			{:noreply, {db, mailboxes, messages}}
		else
			{:noreply, {HashDict.delete(db, pub), mailboxes - 1, messages - length(db[pub])}}
		end
	end
	def handle_cast({:send, to, message}, {db, m, messages}) do
		msg = %Msg{msg: message, time: Timer.stamp, size: byte_size(message[:msg]) + byte_size(message[:key]), price: pure_cost(messages), to: to}
		a = db[to]
		if a == nil do a = [] end
		dd = HashDict.put(db, to, a ++ [msg])
		{:noreply, {dd, m, messages+1}}
	end
	def handle_call({:pop, pub}, _from, {db, b, messages}) do
		if is_list(db[pub]) and length(db[pub]) > 0 do
			out = hd(db[pub])
			db = HashDict.put(db, pub, tl(db[pub]))#List.delete_at(db[pub], index))
		else
			out = nil
		end
		{:reply, out, {db, b, messages - 1}}	end
	def handle_call({:size, pub}, _from, {db, b, c}) do
		cond do
			db[pub] == nil -> out = 0
			true -> out = length(db[pub])
		end
		{:reply, out, {db, b, c}}
	end
	def handle_call(:status, _from, {db, m, n}) do {:reply, %{mailboxes: m, messages: n}, {db, m, n}} end
	def pop(pub) do
		msg = GenServer.call(@name, {:pop, pub})
		cond do
			msg == nil -> nil
			pub == Keys.pubkey -> nil
			true ->
				time = Timer.now_diff(msg.time) + 10000000 #10 second fee automatically
				two_days = 24*60*60*1000000
				refund = round(msg.price *  max(0, ((two_days - time) / two_days)))
				tx = ChannelManager.spend(pub, refund)
				%MsgPop{msg: msg, payment: tx}
		end
	end
	def size(pub) do GenServer.call(@name, {:size, pub}) end
	def status do GenServer.call(@name, :status) end
	def pure_cost(m) do (1 + m) * 1000000 end
	def cost(s) do pure_cost(s.messages) end
	def cost do cost(status) end
	def accept(payment, cost, f) do if ChannelManager.accept(payment, cost) do f.() else "bad payment" end	end
	def register(payment, pub) do
		accept(payment, Constants.registration,  fn() ->
			GenServer.cast(@name, {:new, pub})
		end)
	end
	def send(payment, to, msg) do accept(payment, cost, fn() -> GenServer.cast(@name, {:send, to, msg}) end) end
	def delete_account(pub) do
		foo = size(pub)
		GenServer.cast(@name, {:del_acc, pub})
		bar = size(pub)
		if foo != bar do
			ChannelManager.send(pub, Constants.registration_fee * 9 / 10)
			#we should send a message to out peer about his new money.
		end
	end
end

#what if every network message cost money?
#the node-node communication protocol could cost money.

defmodule Msg do
	defstruct time: 0, msg: "", size: 0, price: 0, from: "", to: ""
end
#there should be a KV thread that this thread uses to store stuff. That way when this thread dies, the mailbox doesn't get dumped.
defmodule MailBox do
	#if you are running a node with external IP, then this thread could be generating profit for you.
  use GenServer
  @name __MODULE__
  def start_link() do GenServer.start_link(__MODULE__, :ok, [name: @name]) end
  def init(:ok) do {:ok, {%HashDict{}, 0, 0}} end
	def handle_cast({:new, pub}, {db, mailboxes, m}) do
		if	db[pub] == nil do
			{:noreply, {HashDict.put(db, pub, []), mailboxes + 1, m}}
		else
			{:noreply, {db, mailboxes, m}}
		end
	end
	def handle_cast({:del_acc, pub}, {db, mailboxes, messages}) do
		if db[pub] == nil do
			{:noreply, {db, mailboxes, messages}}
		else
			{:noreply, {HashDict.delete(db, pub), mailboxes - 1, messages - length(db[pub])}}
		end
	end
	def handle_cast({:send, to, message}, {db, m, messages}) do
		msg = %Msg{msg: message, time: Timer.stamp, size: byte_size(message[:msg]) + byte_size(message[:key]), price: messages*1000000, to: to}
		a = db[to]
		if a == nil do a = [] end
		dd = HashDict.put(db, to, a ++ [msg])
		{:noreply, {dd, m, messages+1}}
	end
	def handle_call({:pop, pub}, _from, {db, b, messages}) do
		if is_list(db[pub]) and length(db[pub]) > 0 do
			out = hd(db[pub])
			db = HashDict.put(db, pub, tl(db[pub]))#List.delete_at(db[pub], index))
		else
			out = nil
		end
		{:reply, out, {db, b, messages - 1}}	end
	def handle_call({:size, pub}, _from, {db, b, c}) do
		cond do
			db[pub] == nil -> out = 0
			true -> out = length(db[pub])
		end
		{:reply, out, {db, b, c}}
	end
	def handle_call(:status, _from, {db, m, n}) do {:reply, %{mailboxes: m, messages: n}, {db, m, n}} end
	def pop(pub) do
		msg = GenServer.call(@name, {:pop, pub})
		cond do
			msg == nil -> nil
			pub == Keys.pubkey -> nil
			true ->
				time = Timer.now_diff(msg.time) + 10000000 #10 second fee automatically
				two_days = 24*60*60*1000000
				refund = round(msg.price *  max(0, ((two_days - time) / two_days)))
				tx = ChannelManager.spend(pub, refund)
				%MsgPop{msg: msg, payment: tx}
		end
	end
	def size(pub) do GenServer.call(@name, {:size, pub}) end
	def status do GenServer.call(@name, :status) end
	def cost do cost(status) end
	def cost(s) do (1+s.messages)*1000000	end
	def accept(payment, cost, f) do if ChannelManager.accept(payment, cost) do f.() else "bad payment" end	end
	def register(payment, pub) do
		accept(payment, Constants.registration,  fn() ->
			GenServer.cast(@name, {:new, pub})
		end)
	end
	def send(payment, to, msg) do accept(payment, cost, fn() -> GenServer.cast(@name, {:send, to, msg}) end) end
	def delete_account(pub) do
		foo = size(pub)
		GenServer.cast(@name, {:del_acc, pub})
		bar = size(pub)
		if foo != bar do
			ChannelManager.send(pub, Constants.registration_fee * 9 / 10)
			#we should send a message to out peer about his new money.
		end
	end
end

#what if every network message cost money?
#the node-node communication protocol could cost money.


Start to support maps encoding

Start to support maps encoding

Map encoding is a nightmare because the pointer to the data that each
map key carries with it starts from the beginning of the record, not the
beginning of the map (which would make sense). Yay OrientDB :D

defmodule MarcoPolo.Protocol.RecordSerialization do
  require Record

  # We're creating records instead of structs to avoid creating lots of
  # modules. `:property` identifies a field that is in the schema's metadata
  # (`:id` is the property id) while `:named_field` identifies a "dynamic"
  # field, with a `:name` and a type (`:data_type`). Both records contain a
  # `:data_ptr` field that contains the index of the data structure for that
  # field in the serialized record.
  Record.defrecordp :property, [:id, :data_ptr]
  Record.defrecordp :named_field, [:name, :data_type, :data_ptr]

  Record.defrecordp :map_key, [:key, :data_type, :data_ptr]

  Record.defrecordp :typed_field, [:value, :type]

  defmodule Field do
    @type t :: %__MODULE__{
      name: binary,
      type: atom,
      pointer_to_data: non_neg_integer,
      value: term,
      encoded_value: iodata,
    }

    defstruct ~w(name type pointer_to_data value encoded_value)a
  end

  @doc """
  Parses a binary-serialized record.
  """
  @spec decode(binary) :: {non_neg_integer, String.t, %{}}
  def decode(data) do
    <<_version, rest :: binary>> = data
    rest |> decode_document |> elem(0)
  end

  @doc """
  Serializes a record using the binary serialization protocol.

  `class_name` is a string containing the class name of the record being
  encoded. `fields` is a list of `Field` structs.
  """
  def encode({class_name, fields}) do
    version_and_class = [0, encode_type(class_name || "", :string)]
    offset            = IO.iodata_length(version_and_class)
    encoded_fields    = encode_fields(fields, offset)

    [version_and_class, encoded_fields]
  end

  # Decodes a document (ODocument). This ODocument can be a "top-level" document
  # or an "embedded" type, since the leading serialization version byte is not
  # decoded here (but in `decode/1`).
  defp decode_document(data) do
    {class_name, rest}        = decode_type(data, :string)
    {field_definitions, rest} = decode_header(rest)
    {fields, rest}            = decode_fields(rest, field_definitions)

    if class_name == "" do
      class_name = nil
    end

    {{class_name, fields}, rest}
  end

  # Decodes an header returning a list of field definitions (which is a list of
  # `%Field{}` structs).
  defp decode_header(data, acc \\ []) do
    {i, rest} = :small_ints.decode_zigzag_varint(data)

    # If `i` is positive, that means the next field definition is a "named
    # field" and `i` is the length of the field's name. If it's negative, it
    # represents the property id of a property. If it's 0, it signals the end of
    # the header segment.
    cond do
      i == 0 ->
        # Remember to return `rest` and not `data` since `rest` doesn't contain
        # the 0 byte that signals the end of the header, while `data` does; we
        # want to ditch that byte.
        {Enum.reverse(acc), rest}
      i < 0 ->
         raise "properties aren't supported yet, only fields with a name and an explicit type"
      i > 0 ->
        {field, rest} = decode_field_definition(:named_field, data)
        decode_header(rest, [field|acc])
    end
  end

  # Decodes the definition of a named field in the header (`data`). Returns a
  # `%Field{}` struct with an empty value.
  defp decode_field_definition(:named_field, data) do
    {field_name, rest}            = decode_type(data, :string)
    {data_ptr, rest}              = decode_data_ptr(rest)
    <<data_type, rest :: binary>> = rest

    field = %Field{name: field_name, type: int_to_type(data_type), pointer_to_data: data_ptr}
    {field, rest}
  end

  # Decodes fields from the body of a serialized document (`data`) and a list of
  # `%Field{}` structs (with no `:value` field, they're definitions). Returns a
  # list of `%Field{}`s and the rest of the given data.
  defp decode_fields(data, field_definitions) do
    {fields, rest} = Enum.map_reduce field_definitions, data, fn(%Field{} = field, acc) ->
      if field.pointer_to_data == 0 do
        {%{field | value: nil}, acc}
      else
        {value, rest} = decode_type(acc, field.type)
        {%{field | value: value}, rest}
      end
    end
  end

  # The pointer to the data is just a signed int32.
  defp decode_data_ptr(data) do
    <<data_ptr :: 32-signed, rest :: binary>> = data
    {data_ptr, rest}
  end

  # Decodes an instance of `type` from `data`.
  defp decode_type(data, type)

  defp decode_type(<<0>> <> rest, :boolean), do: {false, rest}
  defp decode_type(<<1>> <> rest, :boolean), do: {true, rest}

  defp decode_type(data, type) when type in [:sint16, :sint32, :sint64] do
    :small_ints.decode_zigzag_varint(data)
  end

  defp decode_type(data, :float) do
    <<float :: 32-float, rest :: binary>> = data
    {float, rest}
  end

  defp decode_type(data, :double) do
    <<double :: 64-float, rest :: binary>> = data
    {double, rest}
  end

  defp decode_type(data, type) when type in [:string, :bytes] do
    {len, rest} = :small_ints.decode_zigzag_varint(data)
    len = len * 8
    <<string :: bits-size(len), rest :: binary>> = rest
    {string, rest}
  end

  defp decode_type(data, :embedded) do
    decode_document(data)
  end

  defp decode_type(data, type) when type in [:embedded_list, :embedded_set] do
    {nitems, rest} = :small_ints.decode_zigzag_varint(data)
    <<type, rest :: binary>> = rest

    # Only ANY is supported by OrientDB at the moment.
    :any = int_to_type(type)

    # OPTIMIZE: I have to find a better (clean) way to `map_reduce` n times
    # instead of mapreducing over a list of n times the number 0, which is
    # uselessly expensive to build and plain useless. A range doesn't work
    # because `Enum.to_list(1..0)` is `[1, 0]` which makes sense, but my
    # 1..nitems has to translate to `[]` so that the mapreducing doesn't
    # actually happen.
    Enum.map_reduce List.duplicate(0, nitems), rest, fn(_, acc) ->
      <<type, acc :: binary>> = acc
      decode_type(acc, int_to_type(type))
    end
  end

  defp decode_type(data, :embedded_map) do
    {keys, rest}   = decode_map_header(data)
    {keys_and_values, rest} = decode_map_values(rest, keys)

    {Enum.into(keys_and_values, %{}), rest}
  end

  defp decode_map_header(data) do
    {nkeys, rest} = :small_ints.decode_zigzag_varint(data)

    {keys, rest} = Enum.map_reduce List.duplicate(0, nkeys), rest, fn(_, <<string_type, acc :: binary>>) ->
      # For now, OrientDB only supports STRING keys.
      :string = int_to_type(string_type)

      {key, acc} = decode_type(acc, :string)
      {ptr, acc} = decode_data_ptr(acc)
      IO.inspect ptr
      <<type, acc :: binary>> = acc

      {map_key(key: key, data_type: int_to_type(type), data_ptr: ptr), acc}
    end
  end

  defp decode_map_values(data, keys) do
    Enum.map_reduce keys, data, fn(map_key(key: key_name, data_type: type, data_ptr: ptr), acc) ->
      if ptr == 0 do
        {{key_name, nil}, acc}
      else
        {value, acc} = decode_type(acc, type)
        {{key_name, value}, acc}
      end
    end
  end

  defp encode_fields(fields, offset) do
    offset = offset + header_offset(fields)

    {fields, values, _} = Enum.reduce fields, {[], [], offset}, fn(%Field{} = field, {fs, vs, index}) ->
      encoded_value = encode_field_value(field, index).encoded_value
      encoded_field = %{field | pointer_to_data: index} |> encode_field_for_header
      index         = index + IO.iodata_length(encoded_value)

      {[encoded_field|fs], [encoded_value|vs], index}
    end

    [Enum.reverse(fields), 0, Enum.reverse(values)]
  end

  # Returns the length of the header based on the list of fields.
  defp header_offset(fields) do
    # The last +1 is for the `0` that signals the end of the header.
    fields
    |> Enum.map(&encode_field_value(&1, 0))
    |> Enum.map(&(&1 |> encode_field_for_header |> IO.iodata_length))
    |> Enum.sum
    |> +(1)
  end

  # Returns the given `%Field{}` with the `:encoded_value` field set to the
  # result of encoding the `:value` field.
  defp encode_field_value(%Field{type: type, value: value} = field, offset) do
    %{field | encoded_value: encode_type(value, type, offset)}
  end

  # Encodes the given `%Field{}` for the header, i.e., just the field
  # representation and not the value (name, pointer to data, type). Returns
  # iodata.
  defp encode_field_for_header(%Field{pointer_to_data: ptr, type: type, name: name} = field) do
    if is_nil(ptr) do
      ptr = 0
    end

    [encode_type(name, :string), <<ptr :: 32-signed>>, type_to_int(type)]
  end

  # Encodes an instance of `type`. Returns an iodata instead of a binary.
  # Made public for testing.
  @doc false
  def encode_type(data, type, offset \\ nil)

  def encode_type(str, :string, _) when is_binary(str) do
    [:small_ints.encode_zigzag_varint(byte_size(str)), str]
  end

  def encode_type(i, type, _) when type in [:sint16, :sint32, :sint64] and is_integer(i) do
    :small_ints.encode_zigzag_varint(i)
  end

  def encode_type(map, :embedded_map, offset) when is_map(map) do
    offset = offset + map_header_offset(map)

    {keys, values, _} = Enum.reduce map, {[], [], offset}, fn({key, value}, {ks, vs, index}) ->
      typed_field(value: value, type: type) = value
      encoded_value = encode_type(value, type)
      key = [type_to_int(:string), encode_type(key, :string), <<index :: 32-signed>>, type_to_int(type)]
      index = index + IO.iodata_length(encoded_value)
      {[key|ks], [encoded_value|vs], index}
    end

    nkeys = map |> Map.keys |> Enum.count |> :small_ints.encode_zigzag_varint

    [nkeys, keys, values]
  end

  defp map_header_offset(map) do
    keys = Map.keys(map)

    # `6` means 4 bytes for the pointer to the data, 1 byte for the data type,
    # and 1 byte for the key type.
    nkeys       = :small_ints.encode_zigzag_varint(Enum.count(keys))
    key_lengths = Enum.map(keys, &(IO.iodata_length(encode_type(&1, :string)) + 6))

    byte_size(nkeys) + Enum.sum(key_lengths)
  end

  # http://orientdb.com/docs/last/Types.html
  @types [
    boolean: 0,
    sint32: 1,
    sint16: 2,
    sint64: 3,
    float: 4,
    double: 5,
    datetime: 6,
    string: 7,
    binary: 8,
    embedded: 9,
    embedded_list: 10,
    embedded_set: 11,
    embedded_map: 12,
    link: 13,
    link_list: 14,
    link_set: 15,
    link_map: 16,
    byte: 17,
    transient: 18,
    date: 19,
    custom: 20,
    decimal: 21,
    link_bag: 22,
    any: 23,
  ]

  for {type_name, type_id} <- @types do
    defp int_to_type(unquote(type_id)), do: unquote(type_name)
    defp type_to_int(unquote(type_name)), do: unquote(type_id)
  end
end

defmodule MarcoPolo.Protocol.RecordSerialization do
  import Record

  # We're creating records instead of structs to avoid creating lots of
  # modules. `:property` identifies a field that is in the schema's metadata
  # (`:id` is the property id) while `:named_field` identifies a "dynamic"
  # field, with a `:name` and a type (`:data_type`). Both records contain a
  # `:data_ptr` field that contains the index of the data structure for that
  # field in the serialized record.
  defrecordp :property, [:id, :data_ptr]
  defrecordp :named_field, [:name, :data_type, :data_ptr]

  defrecordp :map_key, [:key, :data_type, :data_ptr]

  defmodule Field do
    @type t :: %__MODULE__{
      name: binary,
      type: atom,
      pointer_to_data: non_neg_integer,
      encoded_value: iodata,
    }

    defstruct ~w(name type pointer_to_data value encoded_value)a
  end

  @doc """
  Parses a binary-serialized record.
  """
  @spec decode(binary) :: {non_neg_integer, String.t, %{}}
  def decode(data) do
    <<_version, rest :: binary>> = data
    rest |> decode_document |> elem(0)
  end

  @doc """
  Serializes a record using the binary serialization protocol.

  `class_name` is a string containing the class name of the record being
  encoded. `fields` is a list of `Field` structs.
  """
  def encode({class_name, fields}) do
    version_and_class = [0, encode_type(class_name || "", :string)]
    offset            = IO.iodata_length(version_and_class)
    encoded_fields    = encode_fields(fields, offset)

    [version_and_class, encoded_fields]
  end

  # Decodes a document (ODocument). This ODocument can be a "top-level" document
  # or an "embedded" type, since the leading serialization version byte is not
  # decoded here (but in `decode/1`).
  defp decode_document(data) do
    {class_name, rest}        = decode_type(data, :string)
    {field_definitions, rest} = decode_header(rest)
    {fields, rest}            = decode_fields(rest, field_definitions)

    if class_name == "" do
      class_name = nil
    end

    {{class_name, fields}, rest}
  end

  # Decodes an header returning a list of field definitions (which is a list of
  # `%Field{}` structs).
  defp decode_header(data, acc \\ []) do
    {i, rest} = :small_ints.decode_zigzag_varint(data)

    # If `i` is positive, that means the next field definition is a "named
    # field" and `i` is the length of the field's name. If it's negative, it
    # represents the property id of a property. If it's 0, it signals the end of
    # the header segment.
    cond do
      i == 0 ->
        # Remember to return `rest` and not `data` since `rest` doesn't contain
        # the 0 byte that signals the end of the header, while `data` does; we
        # want to ditch that byte.
        {Enum.reverse(acc), rest}
      i < 0 ->
         raise "properties aren't supported yet, only fields with a name and an explicit type"
      i > 0 ->
        {field, rest} = decode_field_definition(:named_field, data)
        decode_header(rest, [field|acc])
    end
  end

  # Decodes the definition of a named field in the header (`data`). Returns a
  # `%Field{}` struct with an empty value.
  defp decode_field_definition(:named_field, data) do
    {field_name, rest}            = decode_type(data, :string)
    {data_ptr, rest}              = decode_data_ptr(rest)
    <<data_type, rest :: binary>> = rest

    field = %Field{name: field_name, type: int_to_type(data_type), pointer_to_data: data_ptr}
    {field, rest}
  end

  # Decodes fields from the body of a serialized document (`data`) and a list of
  # `%Field{}` structs (with no `:value` field, they're definitions). Returns a
  # list of `%Field{}`s and the rest of the given data.
  defp decode_fields(data, field_definitions) do
    {fields, rest} = Enum.map_reduce field_definitions, data, fn(%Field{} = field, acc) ->
      if field.pointer_to_data == 0 do
        {%{field | value: nil}, acc}
      else
        {value, rest} = decode_type(acc, field.type)
        {%{field | value: value}, rest}
      end
    end
  end

  # The pointer to the data is just a signed int32.
  defp decode_data_ptr(data) do
    <<data_ptr :: 32-signed, rest :: binary>> = data
    {data_ptr, rest}
  end

  # Decodes an instance of `type` from `data`.
  defp decode_type(data, type)

  defp decode_type(<<0>> <> rest, :boolean), do: {false, rest}
  defp decode_type(<<1>> <> rest, :boolean), do: {true, rest}

  defp decode_type(data, type) when type in [:sint16, :sint32, :sint64] do
    :small_ints.decode_zigzag_varint(data)
  end

  defp decode_type(data, :float) do
    <<float :: 32-float, rest :: binary>> = data
    {float, rest}
  end

  defp decode_type(data, :double) do
    <<double :: 64-float, rest :: binary>> = data
    {double, rest}
  end

  defp decode_type(data, type) when type in [:string, :bytes] do
    {len, rest} = :small_ints.decode_zigzag_varint(data)
    len = len * 8
    <<string :: bits-size(len), rest :: binary>> = rest
    {string, rest}
  end

  defp decode_type(data, :embedded) do
    decode_document(data)
  end

  defp decode_type(data, :embedded_list) do
    {nitems, rest} = :small_ints.decode_zigzag_varint(data)
    <<type, rest :: binary>> = rest

    # Only ANY is supported by OrientDB at the moment.
    :any = int_to_type(type)

    # OPTIMIZE: I have to find a better (clean) way to `map_reduce` n times
    # instead of mapreducing over a list of n times the number 0, which is
    # uselessly expensive to build and plain useless. A range doesn't work
    # because `Enum.to_list(1..0)` is `[1, 0]` which makes sense, but my
    # 1..nitems has to translate to `[]` so that the mapreducing doesn't
    # actually happen.
    Enum.map_reduce List.duplicate(0, nitems), rest, fn(_, acc) ->
      <<type, acc :: binary>> = acc
      decode_type(acc, int_to_type(type))
    end
  end

  defp decode_type(data, :embedded_set) do
    decode_type(data, :embedded_list)
  end

  defp decode_type(data, :embedded_map) do
    {nkeys, rest} = :small_ints.decode_zigzag_varint(data)

    {keys, rest} = Enum.map_reduce List.duplicate(0, nkeys), rest, fn(_, <<type, acc :: binary>>) ->
      {key, acc} = decode_type(acc, int_to_type(type))
      {data_ptr, acc} = decode_data_ptr(acc)
      <<data_type, acc :: binary>> = acc
      {map_key(key: key, data_type: int_to_type(data_type), data_ptr: data_ptr), acc}
    end

    {keys_and_values, rest} = Enum.map_reduce keys, rest, fn(key, acc) ->
      map_key(key: key_name, data_type: type, data_ptr: ptr) = key

      if ptr == 0 do
        {{key_name, nil}, acc}
      else
        {value, acc} = decode_type(acc, type)
        {{key_name, value}, acc}
      end
    end

    {Enum.into(keys_and_values, %{}), rest}
  end

  # I've been bitten a few times with FunctionClauseErrors because I still haven't
  # defined how to decode all types, so let's leave this here until we know how
  # to decode them all. Makes it easier to debug :).
  defp decode_type(data, type) do
    raise "don't know how to decode #{inspect type} from data: #{inspect data}"
  end

  defp encode_fields(fields, offset) do
    fields = Enum.map(fields, &encode_field_value/1)
    offset = offset + header_offset(fields)

    {fields, values, _} = Enum.reduce fields, {[], [], offset}, fn(%Field{} = field, {fs, vs, index}) ->
      encoded_field = %{field | pointer_to_data: index} |> encode_field_for_header
      index         = index + IO.iodata_length(field.encoded_value)

      {[encoded_field|fs], [field.encoded_value|vs], index}
    end

    [Enum.reverse(fields), 0, Enum.reverse(values)]
  end

  # Returns the length of the header based on the list of fields.
  defp header_offset(fields) do
    # The last +1 is for the `0` that signals the end of the header.
    fields
    |> Enum.map(&(&1 |> encode_field_for_header |> IO.iodata_length))
    |> Enum.sum
    |> +(1)
  end

  # Returns the given `%Field{}` with the `:encoded_value` field set to the
  # result of encoding the `:value` field.
  defp encode_field_value(%Field{type: type, value: value} = field) do
    %{field | encoded_value: encode_type(value, type)}
  end

  # Encodes the given `%Field{}` for the header, i.e., just the field
  # representation and not the value (name, pointer to data, type). Returns
  # iodata.
  defp encode_field_for_header(%Field{pointer_to_data: ptr, type: type, name: name} = field) do
    if is_nil(ptr) do
      ptr = 0
    end

    [encode_type(name, :string), <<ptr :: 32-signed>>, type_to_int(type)]
  end

  # Encodes an instance of `type`. Returns an iodata instead of a binary.
  # Made public for testing.
  @doc false
  def encode_type(data, type)

  def encode_type(str, :string) do
    [:small_ints.encode_zigzag_varint(byte_size(str)), str]
  end

  def encode_type(i, type) when type in [:sint16, :sint32, :sint64] do
    :small_ints.encode_zigzag_varint(i)
  end

  defp field_name(field) when is_record(field, :named_field), do: named_field(field, :name)

  # http://orientdb.com/docs/last/Types.html
  @types [
    boolean: 0,
    sint32: 1,
    sint16: 2,
    sint64: 3,
    float: 4,
    double: 5,
    datetime: 6,
    string: 7,
    binary: 8,
    embedded: 9,
    embedded_list: 10,
    embedded_set: 11,
    embedded_map: 12,
    link: 13,
    link_list: 14,
    link_set: 15,
    link_map: 16,
    byte: 17,
    transient: 18,
    date: 19,
    custom: 20,
    decimal: 21,
    link_bag: 22,
    any: 23,
  ]

  for {type_name, type_id} <- @types do
    defp int_to_type(unquote(type_id)), do: unquote(type_name)
    defp type_to_int(unquote(type_name)), do: unquote(type_id)
  end
end


1.2.7 correction

1.2.7 correction

defmodule Noizu.SimplePool.InnerStateBehaviour do

  @callback call_forwarding(call :: any, context :: any, state :: any) :: {:noreply, state :: any}
  @callback call_forwarding(call :: any, context :: any, from :: any,  state :: any) :: {atom, reply :: any, state :: any}
  @callback fetch(this :: any, options :: any, context :: any) :: {:reply, this :: any, this :: any}

  @callback load(ref :: any) ::  any
  @callback load(ref :: any, context :: any) :: any
  @callback load(ref :: any, options :: any, context :: any) :: any

  @callback terminate_hook(reason :: any,  Noizu.SimplePool.Worker.State.t) :: {:ok, Noizu.SimplePool.Worker.State.t}
  @callback shutdown(Noizu.SimplePool.Worker.State.t, options :: any, context :: any, from :: any) :: {:ok | :wait, Noizu.SimplePool.Worker.State.t}
  @callback worker_refs(any, any, any) :: any | nil

  alias Noizu.SimplePool.OptionSettings
  alias Noizu.SimplePool.OptionValue
  alias Noizu.SimplePool.OptionList

  @required_methods([:call_forwarding, :load])
  @provided_methods([:call_forwarding_catchall, :fetch, :shutdown, :terminate_hook, :get_direct_link!, :worker_refs, :ping!, :kill!, :crash!, :health_check!])

  @methods(@required_methods ++ @provided_methods)
  @features([:auto_identifier, :lazy_load, :inactivitiy_check, :s_redirect])
  @default_features([:lazy_load, :s_redirect, :inactivity_check])

  def prepare_options(options) do
    settings = %OptionSettings{
      option_settings: %{
        pool: %OptionValue{option: :pool, required: true},
        features: %OptionList{option: :features, default: Application.get_env(Noizu.SimplePool, :default_features, @default_features), valid_members: @features, membership_set: false},
        only: %OptionList{option: :only, default: @provided_methods, valid_members: @methods, membership_set: true},
        override: %OptionList{option: :override, default: [], valid_members: @methods, membership_set: true},
      }
    }
    initial = OptionSettings.expand(settings, options)
    modifications = Map.put(initial.effective_options, :required, List.foldl(@methods, %{}, fn(x, acc) -> Map.put(acc, x, initial.effective_options.only[x] && !initial.effective_options.override[x]) end))
    %OptionSettings{initial| effective_options: Map.merge(initial.effective_options, modifications)}
  end

  defmacro __using__(options) do
    option_settings = prepare_options(options)
    options = option_settings.effective_options
    required = options.required
    features = MapSet.new(options.features)
    pool = options.pool

    quote do
      import unquote(__MODULE__)
      @behaviour Noizu.SimplePool.InnerStateBehaviour
      @base(unquote(Macro.expand(pool, __CALLER__)))
      @worker(Module.concat([@base, "Worker"]))
      @worker_supervisor(Module.concat([@base, "WorkerSupervisor"]))
      @server(Module.concat([@base, "Server"]))
      @pool_supervisor(Module.concat([@base, "PoolSupervisor"]))
      @simple_pool_group({@base, @worker, @worker_supervisor, @server, @pool_supervisor})

      alias Noizu.SimplePool.Worker.Link

      if (unquote(required.get_direct_link!)) do
        def get_direct_link!(ref, context), do: @server.get_direct_link!(ref, context)
      end

      if (unquote(required.fetch)) do
        def fetch(%__MODULE__{} = this, _options, context), do: {:reply, this, this}
      end

      if (unquote(required.ping!)) do
        def ping!(%__MODULE__{} = this, context), do: {:reply, :pong, this}
      end

      if (unquote(required.kill!)) do
        def kill!(%__MODULE__{} = this, context), do: {:stop, {:user_requested, context}, this}
      end

      if (unquote(required.crash!)) do
        def crash!(%__MODULE__{} = this, options, context) do
           raise "#{__MODULE__} - Crash Forced: #{inspect context}, #{inspect options}"
        end
      end

      if (unquote(required.health_check!)) do
        def health_check!(%__MODULE__{} = this, _options, context), do: {:reply, %{pending: true}, this}
      end

      if (unquote(required.call_forwarding_catchall)) do

        # Default Call Forwarding Catch All
        # @Deprecated
        def call_forwarding_catchall(call, context, _from, %__MODULE__{} = this) do
          if context do
            Logger.warn("[#{context.token}] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
          else
            Logger.warn("[NO_TOKEN] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
          end
          {:reply, :unsupported_call, this}
        end

        def call_forwarding_catchall(call, context, %__MODULE__{} = this) do
          if context do
            Logger.warn("[#{context.token}] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
          else
            Logger.warn("[NO_TOKEN] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
          end
          {:noreply, this}
        end
      end

      if (unquote(required.shutdown)) do
        def shutdown(%Noizu.SimplePool.Worker.State{} = state, _options \\ [], context \\ nil, _from \\ nil), do: {:ok, state}
      end

      if (unquote(required.terminate_hook)) do
        def terminate_hook(reason, state), do: {:ok, state}
      end

      if (unquote(required.worker_refs)) do
        def worker_refs(_options, _context, _state), do: nil
      end

      @before_compile unquote(__MODULE__)
    end # end quote
  end #end defmacro __using__(options)

  defmacro __before_compile__(_env) do
    quote do

      #-----------------------------------------------------------------------------
      # call_forwarding - call
      #-----------------------------------------------------------------------------
      def call_forwarding(:ping!, context, _from, %__MODULE__{} = this), do: ping!(this, context)
      def call_forwarding({:health_check!, options}, context, _from, %__MODULE__{} = this), do: health_check!(this, options, context)
      def call_forwarding({:fetch, options}, context, _from, %__MODULE__{} = this), do: fetch(this, options, context)
      def call_forwarding(call, context, _from, %__MODULE__{} = this) do
        if context do
          Logger.warn("[#{context.token}] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
        else
          Logger.warn("[NO_TOKEN] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
        end
        {:reply, :unsupported_call, this}
      end
      #-----------------------------------------------------------------------------
      # call_forwarding - cast|info
      #-----------------------------------------------------------------------------
      def call_forwarding(:kill!, context, %__MODULE__{} = this), do: kill!(this, context)
      def call_forwarding({:crash!, options}, context, %__MODULE__{} = this), do: crash!(this, options, context)
      def call_forwarding(call, context, %__MODULE__{} = this) do
        if context do
          Logger.warn("[#{context.token}] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
        else
          Logger.warn("[NO_TOKEN] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
        end
        {:noreply, this}
      end
    end # end quote
  end # end defmacro __before_compile__(_env)
end # end module

defmodule Noizu.SimplePool.InnerStateBehaviour do

  @callback call_forwarding(call :: any, context :: any, state :: any) :: {:noreply, state :: any}
  @callback call_forwarding(call :: any, context :: any, from :: any,  state :: any) :: {atom, reply :: any, state :: any}
  @callback fetch(this :: any, options :: any, context :: any) :: {:reply, this :: any, this :: any}

  @callback load(ref :: any) ::  any
  @callback load(ref :: any, context :: any) :: any
  @callback load(ref :: any, options :: any, context :: any) :: any

  @callback terminate_hook(reason :: any,  Noizu.SimplePool.Worker.State.t) :: {:ok, Noizu.SimplePool.Worker.State.t}
  @callback shutdown(Noizu.SimplePool.Worker.State.t, options :: any, context :: any, from :: any) :: {:ok | :wait, Noizu.SimplePool.Worker.State.t}
  @callback worker_refs(any, any, any) :: any | nil

  alias Noizu.SimplePool.OptionSettings
  alias Noizu.SimplePool.OptionValue
  alias Noizu.SimplePool.OptionList

  @required_methods([:call_forwarding, :load])
  @provided_methods([:call_forwarding_catchall, :fetch, :shutdown, :terminate_hook, :get_direct_link!, :worker_refs, :ping!, :kill!, :crash!, :health_check!])

  @methods(@required_methods ++ @provided_methods)
  @features([:auto_identifier, :lazy_load, :inactivitiy_check, :s_redirect])
  @default_features([:lazy_load, :s_redirect, :inactivity_check])

  def prepare_options(options) do
    settings = %OptionSettings{
      option_settings: %{
        pool: %OptionValue{option: :pool, required: true},
        features: %OptionList{option: :features, default: Application.get_env(Noizu.SimplePool, :default_features, @default_features), valid_members: @features, membership_set: false},
        only: %OptionList{option: :only, default: @provided_methods, valid_members: @methods, membership_set: true},
        override: %OptionList{option: :override, default: [], valid_members: @methods, membership_set: true},
      }
    }
    initial = OptionSettings.expand(settings, options)
    modifications = Map.put(initial.effective_options, :required, List.foldl(@methods, %{}, fn(x, acc) -> Map.put(acc, x, initial.effective_options.only[x] && !initial.effective_options.override[x]) end))
    %OptionSettings{initial| effective_options: Map.merge(initial.effective_options, modifications)}
  end

  defmacro __using__(options) do
    option_settings = prepare_options(options)
    options = option_settings.effective_options
    required = options.required
    features = MapSet.new(options.features)
    pool = options.pool

    quote do
      import unquote(__MODULE__)
      @behaviour Noizu.SimplePool.InnerStateBehaviour
      @base(unquote(Macro.expand(pool, __CALLER__)))
      @worker(Module.concat([@base, "Worker"]))
      @worker_supervisor(Module.concat([@base, "WorkerSupervisor"]))
      @server(Module.concat([@base, "Server"]))
      @pool_supervisor(Module.concat([@base, "PoolSupervisor"]))
      @simple_pool_group({@base, @worker, @worker_supervisor, @server, @pool_supervisor})

      alias Noizu.SimplePool.Worker.Link

      if (unquote(required.get_direct_link!)) do
        def get_direct_link!(ref, context), do: @server.get_direct_link!(ref, context)
      end

      if (unquote(required.fetch)) do
        def fetch(%__MODULE__{} = this, _options, context), do: {:reply, this, this}
      end

      if (unquote(required.ping!)) do
        def ping!(%__MODULE__{} = this, context), do: {:reply, :pong, this}
      end

      if (unquote(required.kill!)) do
        def kill!(%__MODULE__{} = this, context), do: {:stop, {:user_requested, context}, this}
      end

      if (unquote(required.crash!)) do
        def crash!(%__MODULE__{} = this, _options, context) do
           raise "#{__MODULE__} - Crash Forced: #{inspect context}"
        end
      end

      if (unquote(required.health_check!)) do
        def health_check!(%__MODULE__{} = this, _options, context), do: {:reply, %{pending: true}, this}
      end

      if (unquote(required.call_forwarding_catchall)) do

        # Default Call Forwarding Catch All
        # @Deprecated
        def call_forwarding_catchall(call, context, _from, %__MODULE__{} = this) do
          if context do
            Logger.warn("[#{context.token}] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
          else
            Logger.warn("[NO_TOKEN] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
          end
          {:reply, :unsupported_call, this}
        end

        def call_forwarding_catchall(call, context, %__MODULE__{} = this) do
          if context do
            Logger.warn("[#{context.token}] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
          else
            Logger.warn("[NO_TOKEN] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
          end
          {:noreply, this}
        end
      end

      if (unquote(required.shutdown)) do
        def shutdown(%Noizu.SimplePool.Worker.State{} = state, _options \\ [], context \\ nil, _from \\ nil), do: {:ok, state}
      end

      if (unquote(required.terminate_hook)) do
        def terminate_hook(reason, state), do: {:ok, state}
      end

      if (unquote(required.worker_refs)) do
        def worker_refs(_options, _context, _state), do: nil
      end

      @before_compile unquote(__MODULE__)
    end # end quote
  end #end defmacro __using__(options)

  defmacro __before_compile__(_env) do
    quote do

      #-----------------------------------------------------------------------------
      # call_forwarding - call
      #-----------------------------------------------------------------------------
      def call_forwarding(:ping!, context, _from, %__MODULE__{} = this), do: ping!(this, context)
      def call_forwarding({:health_check!, options}, context, _from, %__MODULE__{} = this), do: health_check!(this, options, context)
      def call_forwarding({:fetch, options}, context, _from, %__MODULE__{} = this), do: fetch(this, options, context)
      def call_forwarding(call, context, _from, %__MODULE__{} = this) do
        if context do
          Logger.warn("[#{context.token}] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
        else
          Logger.warn("[NO_TOKEN] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
        end
        {:reply, :unsupported_call, this}
      end
      #-----------------------------------------------------------------------------
      # call_forwarding - cast|info
      #-----------------------------------------------------------------------------
      def call_forwarding(:kill!, context, %__MODULE__{} = this), do: kill!(this, context)
      def call_forwarding({:crash!, options}, context, %__MODULE__{} = this), do: crash!(this, options, context)
      def call_forwarding(call, context, %__MODULE__{} = this), do: call_forwarding_catchall(call, context, this)

      def call_forwarding(call, context, %__MODULE__{} = this) do
        if context do
          Logger.warn("[#{context.token}] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
        else
          Logger.warn("[NO_TOKEN] Unhandle Call #{inspect {call, __MODULE__.ref(this)}}")
        end
        {:noreply, this}
      end
    end # end quote
  end # end defmacro __before_compile__(_env)
end # end module


[Weber.Http.Params] fix

[Weber.Http.Params] fix

defmodule Weber.Http.Params do
  @moduledoc """
  API for getting request params

    ## Example

    defmodule Simplechat.Main.Login do

      import Weber.Http.Params

      def render_login("GET", conn) do

        # get body request
        body = get_body(conn)

        #
        # Do something with param
        #
        {:render, [project: "SimpleChat"], []}
      end

    end
  """

  import Plug.Connection.Query

  @doc """
    Return the host binary string.
  """
  def get_host(conn) do
    conn.host
  end

  @doc """
    Return HTTP method
  """
  def get_method(conn) do
    conn.method
  end

  @doc """
    Return the path binary string.
  """
  def get_path(conn) do
    conn.path_info
  end

  @doc """
    Return the header value for the given key.
  """
  def get_header(name, conn) do
    headers = conn.req_headers

    header_value(name, headers)
  end

  @doc """
    Get all headers
  """
  def get_headers(conn) do
    conn.req_headers
  end

  @doc """
    Get cookie
  """
  def get_cookie(name, conn) do
    :cowboy_req.cookie(name, Keyword.get(conn.assigns, :req)) |> elem(0)
  end

  def get_cookie_p(name, req) do
    :cowboy_req.cookie(name, req) |> elem(0)
  end

  @doc """
    Return the full list of cookie values.
  """
  def cookies(conn) do
    :cowboy_req.cookies(Keyword.get(conn.assigns, :req)) |> elem(0)
  end

  @doc """
    Get body
  """
  def get_body(conn) do
    req = Keyword.get(conn.assigns, :req)
    case :cowboy_req.has_body(req) do
      false -> []
      true -> :cowboy_req.body(req) |> elem(1)
    end
  end

  @doc """
    Get parameter value by key from query string.
  """
  def param(key, conn) do
    req = Keyword.get(conn.assigns, :req)
    {val, _} = :cowboy_req.qs_val(key, req)
    val
  end

  #
  # Search headers for specified key, if found return the value otherwise return nil
  #
  defp header_value(_header, []), do: nil
  defp header_value(header, [first | rest]) do
    case first do
      {^header, value} -> value
      _ -> header_value(header, rest)
    end
  end
end

defmodule Weber.Http.Params do
  @moduledoc """
  API for getting request params

    ## Example

    defmodule Simplechat.Main.Login do

      import Weber.Http.Params

      def render_login("GET", conn) do

        # get body request
        body = get_body(conn)

        #
        # Do something with param
        #
        {:render, [project: "SimpleChat"], []}
      end

    end
  """

  import Plug.Connection.Query

  @doc """
    Return the host binary string.
  """
  def get_host(conn) do
    conn.host
  end

  @doc """
    Return HTTP method
  """
  def get_method(conn) do
    conn.method
  end

  @doc """
    Return the path binary string.
  """
  def get_path(conn) do
    conn.path_info
  end

  @doc """
    Return the header value for the given key.
  """
  def get_header(name, conn) do
    headers = conn.req_headers

    header_value(name, headers)
  end

  @doc """
    Get all headers
  """
  def get_headers(conn) do
    conn.req_headers
  end

  @doc """
    Get cookie
  """
  def get_cookie(name, conn) do
    :cowboy_req.cookie(name, Keyword.get(conn.assigns, :req)) |> elem(0)
  end

  def get_cookie_p(name, req) do
    :cowboy_req.cookie(name, req) |> elem(0)
  end

  @doc """
    Return the full list of cookie values.
  """
  def cookies(conn) do
    :cowboy_req.cookies(Keyword.get(conn.assigns, :req)) |> elem(0)
  end

  @doc """
    Get body
  """
  def get_body(conn) do
    req = Keyword.get(conn.assigns, :req)
    case :cowboy_req.has_body(req) do
      false -> []
      true -> :cowboy_req.body(req) |> elem(1)
    end
  end

  @doc """
    Get parameter value by key from query string.
  """
  def param(key, conn) do
    decode(conn.query_string)[key]
  end

  #
  # Search headers for specified key, if found return the value otherwise return nil
  #
  defp header_value(_header, []), do: nil
  defp header_value(header, [first | rest]) do
    case first do
      {^header, value} -> value
      _ -> header_value(header, rest)
    end
  end
end


Add log for updated settings.

Add log for updated settings.

defmodule Farmbot.Bootstrap.SettingsSync do
  @moduledoc "Handles uploading and downloading of FBOS configs."
  use Task, restart: :transient
  use Farmbot.Logger
  import Farmbot.System.ConfigStorage, only: [get_config_value: 3, update_config_value: 4, get_config_as_map: 0]

  def start_link() do
    Task.start_link(__MODULE__, :run, [])
  end

  def run() do
    with {:ok, %{body: body, status_code: 200}} <- Farmbot.HTTP.get("/api/fbos_config"),
    {:ok, data} <- Poison.decode(body)
    do
      do_sync_settings(data)
    else
      {:ok, status_code: code} ->
        Logger.error 1, "HTTP error syncing settings: #{code}"
        :ok
      err ->
        Logger.error 1, "Error syncing settings: #{inspect err}"
        :ok
    end
  rescue
    err ->
      Logger.error 1, "Error syncing settings: #{Exception.message(err)} #{inspect System.stacktrace()}"
  end

  def apply_map(old_map, new_map) do
    old_map = take_valid(old_map)
    new_map = take_valid(new_map)
    Map.new(new_map, fn({key, new_value}) ->
      # Logger.debug 1, "Applying #{key} #{inspect old_map[key]} over #{inspect new_value}"
      if old_map[key] != new_value do
        case new_value do
          val when is_boolean(val) ->
            update_config_value(:bool, "settings", key, new_value)
          val when is_binary(val) ->
            update_config_value(:string, "settings", key, new_value)
          val when is_number(val) ->
            update_config_value(:float, "settings", key, new_value / 1)
        end
        Logger.success 1, "Updating: #{key} => #{new_value}"
      end
      {key, new_value}
    end)
  end

  def do_sync_settings(%{"api_migrated" => true} = api_data) do
    Logger.info 3, "API is the source of truth; Downloading data."
    old_config = get_config_as_map()["settings"]
    apply_map(old_config, api_data)
    :ok
  end

  def do_sync_settings(_unimportant_data) do
    Logger.info 3, "FBOS is the source of truth; Uploading data."
    auto_sync = get_config_value(:bool, "settings", "auto_sync")
    beta_opt_in = get_config_value(:bool, "settings", "beta_opt_in")
    disable_factory_reset = get_config_value(:bool, "settings", "disable_factory_reset")
    firmware_output_log = get_config_value(:bool, "settings", "firmware_output_log")
    sequence_body_log = get_config_value(:bool, "settings", "sequence_body_log")
    sequence_complete_log = get_config_value(:bool, "settings", "sequence_complete_log")
    sequence_init_log = get_config_value(:bool, "settings", "sequence_init_log")
    arduino_debug_messages = get_config_value(:bool, "settings", "arduino_debug_messages")
    os_auto_update = get_config_value(:bool, "settings", "os_auto_update")
    firmware_hardware = get_config_value(:string, "settings", "firmware_hardware")
    network_not_found_timer = get_config_value(:float, "settings", "network_not_found_timer")
    payload = %{
      api_migrated: true,
      auto_sync: auto_sync,
      beta_opt_in: beta_opt_in,
      disable_factory_reset: disable_factory_reset,
      firmware_output_log: firmware_output_log,
      sequence_body_log: sequence_body_log,
      sequence_complete_log: sequence_complete_log,
      sequence_init_log: sequence_init_log,
      arduino_debug_messages: arduino_debug_messages,
      os_auto_update: os_auto_update,
      firmware_hardware: firmware_hardware,
      network_not_found_timer: network_not_found_timer,
    } |> Poison.encode!()
    Farmbot.HTTP.delete!("/api/fbos_config")
    Farmbot.HTTP.put!("/api/fbos_config", payload)
    :ok
  end

  @keys [
    "auto_sync",
    "beta_opt_in",
    "disable_factory_reset",
    "firmware_output_log",
    "sequence_body_log",
    "sequence_complete_log",
    "sequence_init_log",
    "arduino_debug_messages",
    "os_auto_update",
    "firmware_hardware",
    "network_not_found_timer"
  ]
  def take_valid(map) do
    Map.take(map, @keys ++ Enum.map(@keys, &String.to_atom(&1)))
  end
end

defmodule Farmbot.Bootstrap.SettingsSync do
  @moduledoc "Handles uploading and downloading of FBOS configs."
  use Task, restart: :transient
  use Farmbot.Logger
  import Farmbot.System.ConfigStorage, only: [get_config_value: 3, update_config_value: 4, get_config_as_map: 0]

  def start_link() do
    Task.start_link(__MODULE__, :run, [])
  end

  def run() do
    with {:ok, %{body: body, status_code: 200}} <- Farmbot.HTTP.get("/api/fbos_config"),
    {:ok, data} <- Poison.decode(body)
    do
      do_sync_settings(data)
    else
      {:ok, status_code: code} ->
        Logger.error 1, "HTTP error syncing settings: #{code}"
        :ok
      err ->
        Logger.error 1, "Error syncing settings: #{inspect err}"
        :ok
    end
  rescue
    err ->
      Logger.error 1, "Error syncing settings: #{Exception.message(err)} #{inspect System.stacktrace()}"
  end

  def apply_map(old_map, new_map) do
    old_map = take_valid(old_map)
    new_map = take_valid(new_map)
    Map.new(new_map, fn({key, new_value}) ->
      # Logger.debug 1, "Applying #{key} #{inspect old_map[key]} over #{inspect new_value}"
      if old_map[key] != new_value do
        # Logger.debug 1, "Updating: #{key} => #{new_value}"
        case new_value do
          val when is_boolean(val) ->
            update_config_value(:bool, "settings", key, new_value)
          val when is_binary(val) ->
            update_config_value(:string, "settings", key, new_value)
          val when is_number(val) ->
            update_config_value(:float, "settings", key, new_value / 1)
        end
      end
      {key, new_value}
    end)
  end

  def do_sync_settings(%{"api_migrated" => true} = api_data) do
    Logger.info 3, "API is the source of truth; Downloading data."
    old_config = get_config_as_map()["settings"]
    apply_map(old_config, api_data)
    :ok
  end

  def do_sync_settings(_unimportant_data) do
    Logger.info 3, "FBOS is the source of truth; Uploading data."
    auto_sync = get_config_value(:bool, "settings", "auto_sync")
    beta_opt_in = get_config_value(:bool, "settings", "beta_opt_in")
    disable_factory_reset = get_config_value(:bool, "settings", "disable_factory_reset")
    firmware_output_log = get_config_value(:bool, "settings", "firmware_output_log")
    sequence_body_log = get_config_value(:bool, "settings", "sequence_body_log")
    sequence_complete_log = get_config_value(:bool, "settings", "sequence_complete_log")
    sequence_init_log = get_config_value(:bool, "settings", "sequence_init_log")
    arduino_debug_messages = get_config_value(:bool, "settings", "arduino_debug_messages")
    os_auto_update = get_config_value(:bool, "settings", "os_auto_update")
    firmware_hardware = get_config_value(:string, "settings", "firmware_hardware")
    network_not_found_timer = get_config_value(:float, "settings", "network_not_found_timer")
    payload = %{
      api_migrated: true,
      auto_sync: auto_sync,
      beta_opt_in: beta_opt_in,
      disable_factory_reset: disable_factory_reset,
      firmware_output_log: firmware_output_log,
      sequence_body_log: sequence_body_log,
      sequence_complete_log: sequence_complete_log,
      sequence_init_log: sequence_init_log,
      arduino_debug_messages: arduino_debug_messages,
      os_auto_update: os_auto_update,
      firmware_hardware: firmware_hardware,
      network_not_found_timer: network_not_found_timer,
    } |> Poison.encode!()
    Farmbot.HTTP.delete!("/api/fbos_config")
    Farmbot.HTTP.put!("/api/fbos_config", payload)
    :ok
  end

  @keys [
    "auto_sync",
    "beta_opt_in",
    "disable_factory_reset",
    "firmware_output_log",
    "sequence_body_log",
    "sequence_complete_log",
    "sequence_init_log",
    "arduino_debug_messages",
    "os_auto_update",
    "firmware_hardware",
    "network_not_found_timer"
  ]
  def take_valid(map) do
    Map.take(map, @keys ++ Enum.map(@keys, &String.to_atom(&1)))
  end
end