%% -*- erlang -*-
%%---- BEGIN COPYRIGHT -------------------------------------------------------
%%
%% Copyright (C) 2013 Feuerlabs Inc. All rights reserved.
%%
%% This Source Code Form is subject to the terms of the Mozilla Public
%% License, v. 2.0. If a copy of the MPL was not distributed with this
%% file, You can obtain one at http://mozilla.org/MPL/2.0/.
%%
%%---- END COPYRIGHT ---------------------------------------------------------

Configs0 = proplists:get_value(configurations, CONFIG, []).
{_, Mandatory0} = lists:keyfind(mandatory_deps, 1, CONFIG).
Mandatory = ordsets:from_list(Mandatory0).
Expand = fun(C, Exp) when is_list(C) ->
		 R = lists:foldl(fun({Cfg}, Acc) ->
				     CVal = proplists:get_value(
					      Cfg, Configs0, []),
				     ordsets:union(
				       Acc,
				       ordsets:from_list(Exp(CVal, Exp)));
				(App, Acc) when is_atom(App) ->
				     ordsets:add_element(App, Acc);
				(Other, Acc) ->
				     Acc
				 end, Mandatory, C),
		 R;
	    (C, Exp) -> Exp([C], Exp)
	 end.
Configs = [{K, Expand(V, Expand)} || {K,V} <- Configs0].
{_, Deps} = lists:keyfind(deps, 1, CONFIG).
Unpar = fun(Name) -> ")" ++ Rev = lists:reverse(Name),
		     list_to_existing_atom(lists:reverse(Rev))
	end.
EnsureApp = fun(A, As) ->
		    case lists:member(A, As) of
			true -> As;
			false ->
			    case lists:keymember(A, 1, Deps) of
				false -> As;
				true  -> [A|As]
			    end
		    end
	    end.
DelApp = fun(A, As) ->
		 case lists:member(A, Mandatory) of
		     false ->
			 [A1 || A1 <- As, A1 =/= A];
		     true ->
			 As
		 end
	 end.
CONFIG1 =
case os:getenv("EXOMETER_PACKAGES") of
    Str when is_list(Str) ->
	L = string:tokens(Str, "\t, \"\n"),
	Deps1 =
	    try As = lists:foldl(
		       fun("(" ++ P, Acc) ->
			       Apps = Expand({Unpar(P)}, Expand),
			       lists:foldl(fun(A, Acc1) ->
						   EnsureApp(A, Acc1)
					   end, Acc, Apps);
			  ("+(" ++ P, Acc) ->
			       %% Same as above
			       Apps = Expand({Unpar(P)}, Expand),
			       lists:foldl(fun(A, Acc1) ->
						   EnsureApp(A, Acc1)
					   end, Acc, Apps);
			  ("-(" ++ P, Acc) ->
			       Apps = Expand({Unpar(P)}, Expand),
			       lists:foldl(fun(A, Acc1) ->
						   DelApp(A, Acc1)
					   end, Acc, Apps);
			  ("-" ++ AppStr, Acc) ->
			       try A = list_to_existing_atom(AppStr),
				     DelApp(A, Acc)
			       catch
				   error:_ -> Acc
			       end;
			  ("+" ++ AppStr, Acc) ->
			       try A = list_to_existing_atom(AppStr),
				     EnsureApp(A, Acc)
			       catch
				   error:_ -> Acc
			       end;
			  (AppStr, Acc) ->
			       try A = list_to_existing_atom(AppStr),
				     EnsureApp(A, Acc)
			       catch
				   error:_ -> Acc
			       end
		       end, Mandatory, L),
		 lists:filter(fun(D) ->
				      lists:member(element(1,D), As)
			      end, Deps)
	    catch
		error:Err ->
		    io:fwrite("Caught ~p~nT = ~p~n",
			      [Err, erlang:get_stacktrace()]),
		    Deps
	    end,
	lists:keyreplace(deps, 1, CONFIG, {deps, Deps1});
    false ->
	CONFIG
end.
%%
%% Ensure macros defined for each non-mandatory dependency
%% e.g. {d, dep_afunix} if afunix is included.
%%
{_, FinalDeps} = lists:keyfind(deps, 1, CONFIG1).
ErlOpts = proplists:get_value(erl_opts, CONFIG, []),
Defs = lists:foldl(fun(Dep, Acc) ->
			   A = element(1, Dep),
			   case lists:member(A, Mandatory) of
			       true -> Acc;
			       false ->
				   [{d,list_to_atom(
					 "dep_" ++ atom_to_list(A))}
				    | Acc]
			   end
		   end, [], FinalDeps),
lists:keystore(erl_opts, 1, CONFIG1, {erl_opts, ErlOpts ++ Defs}).
