%% ====================================================================
%% @author Gavin M. Roy <gavinmroy@gmail.com>
%% @copyright 2016, Gavin M. Roy
%% @doc Wrap a JSON parser to provide easy abstractions across
%%      implementations and ensure a consistent return interface.
%% @end
%% ====================================================================
-module(rabbitmq_aws_json).

-export([decode/1]).

-spec decode(Value :: string() | binary()) -> list().
%% @doc Decode a JSON string returning a proplist
%% @end
decode(Value) when is_list(Value) ->
  decode(list_to_binary(Value));
decode(Value) when is_binary(Value) ->
  % We set an empty list of options because we don't want the default
  % options set in rabbit_json:cecode/1. And we can't override
  % 'return_maps' with '{return_maps, false}' because of a bug in jsx's
  % options handler.
  % See https://github.com/talentdeficit/jsx/pull/115
  Decoded0 = rabbit_json:decode(Value, []),
  Decoded = if
    is_map(Decoded0)  -> maps:to_list(Decoded0);
    is_list(Decoded0) -> Decoded0
  end,
  convert_binary_values(Decoded, []).


-spec convert_binary_values(Value :: list(), Accumulator :: list()) -> list().
%% @doc Convert the binary key/value pairs returned by rabbit_json to strings.
%% @end
convert_binary_values([], Value) ->  Value;
convert_binary_values([{K, V}|T], Accum) when is_map(V) ->
  convert_binary_values(
    T,
    lists:append(
      Accum,
      [{binary_to_list(K), convert_binary_values(maps:to_list(V), [])}]));
convert_binary_values([{K, V}|T], Accum) when is_list(V) ->
  convert_binary_values(
    T,
    lists:append(
      Accum,
      [{binary_to_list(K), convert_binary_values(V, [])}]));
convert_binary_values([{}|T],Accum) ->
  convert_binary_values(T, lists:append(Accum, [{}]));
convert_binary_values([{K, V}|T], Accum) when is_binary(V) ->
  convert_binary_values(T, lists:append(Accum, [{binary_to_list(K), binary_to_list(V)}]));
convert_binary_values([{K, V}|T], Accum) ->
  convert_binary_values(T, lists:append(Accum, [{binary_to_list(K), V}]));
convert_binary_values([H|T], Accum) when is_map(H) ->
  convert_binary_values(T, lists:append(Accum, convert_binary_values(maps:to_list(H), [])));
convert_binary_values([H|T], Accum) when is_binary(H) ->
  convert_binary_values(T, lists:append(Accum, [binary_to_list(H)]));
convert_binary_values([H|T], Accum) when is_integer(H) ->
  convert_binary_values(T, lists:append(Accum, [H]));
convert_binary_values([H|T], Accum) when is_atom(H) ->
  convert_binary_values(T, lists:append(Accum, [H]));
convert_binary_values([H|T], Accum) ->
  convert_binary_values(T, lists:append(Accum, convert_binary_values(H, []))).
