%%%----------------------------------------------------------------------
%%%
%%% ejabberd, Copyright (C) 2002-2017   ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License along
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%----------------------------------------------------------------------

ModCfg0 = fun(F, Cfg, [Key|Tail], Op, Default) ->
                 {OldVal,PartCfg} = case lists:keytake(Key, 1, Cfg) of
                                        {value, {_, V1}, V2} -> {V1, V2};
                                        false -> {if Tail == [] -> Default; true -> [] end, Cfg}
                                    end,
                 case Tail of
                     [] ->
                         [{Key, Op(OldVal)} | PartCfg];
                     _ ->
                         [{Key, F(F, OldVal, Tail, Op, Default)} | PartCfg]
                 end
         end,
ModCfg = fun(Cfg, Keys, Op, Default) -> ModCfg0(ModCfg0, Cfg, Keys, Op, Default) end,

IsRebar3 = case application:get_key(rebar, vsn) of
	       {ok, VSN} ->
		   [VSN1 | _] = string:tokens(VSN, "-"),
		   [Maj, Min, Patch] = string:tokens(VSN1, "."),
		   (list_to_integer(Maj) >= 3);
	       undefined ->
		   lists:keymember(mix, 1, application:loaded_applications())
	   end,
Cfg = case file:consult(filename:join(filename:dirname(SCRIPT), "vars.config")) of
          {ok, Terms} ->
              Terms;
          _Err ->
              []
      end,

ProcessSingleVar = fun(F, Var, Tail) ->
			   case F(F, [Var], []) of
			       [] -> Tail;
			       [Val] -> [Val | Tail]
			   end
		   end,

ProcessVars = fun(_F, [], Acc) ->
                      lists:reverse(Acc);
                 (F, [{Type, Ver, Value} | Tail], Acc) when
                        Type == if_version_above orelse
                        Type == if_version_below ->
		      SysVer = erlang:system_info(otp_release),
		      Include = if Type == if_version_above ->
					SysVer > Ver;
				   true ->
					SysVer < Ver
				end,
		      if Include ->
                              F(F, Tail, ProcessSingleVar(F, Value, Acc));
			 true ->
                              F(F, Tail, Acc)
		      end;
                 (F, [{Type, Ver, Value, ElseValue} | Tail], Acc) when
                        Type == if_version_above orelse
                        Type == if_version_below ->
		      SysVer = erlang:system_info(otp_release),
		      Include = if Type == if_version_above ->
					SysVer > Ver;
				   true ->
					SysVer < Ver
				end,
		      if Include ->
                              F(F, Tail, ProcessSingleVar(F, Value, Acc));
			 true ->
                              F(F, Tail, ProcessSingleVar(F, ElseValue, Acc))
		      end;
                 (F, [{Type, Var, Value} | Tail], Acc) when
                        Type == if_var_true orelse
                        Type == if_var_false ->
                      Flag = Type == if_var_true,
                      case proplists:get_bool(Var, Cfg) of
                          V when V == Flag ->
                              F(F, Tail, ProcessSingleVar(F, Value, Acc));
                          _ ->
                              F(F, Tail, Acc)
                      end;
                 (F, [{Type, Var, Match, Value} | Tail], Acc) when
                        Type == if_var_match orelse
                        Type == if_var_no_match ->
                      case proplists:get_value(Var, Cfg) of
                          V when V == Match ->
                              F(F, Tail, ProcessSingleVar(F, Value, Acc));
                          _ ->
                              F(F, Tail, Acc)
                      end;
                 (F, [Other1 | Tail1], Acc) ->
                      F(F, Tail1, [F(F, Other1, []) | Acc]);
                 (F, Val, Acc) when is_tuple(Val) ->
                      list_to_tuple(F(F, tuple_to_list(Val), Acc));
                 (_F, Other2, _Acc) ->
                      Other2
              end,

CFLags = proplists:get_value(cflags, Cfg, ""),
CPPFLags = proplists:get_value(cppflags, Cfg, ""),
LDFLags = proplists:get_value(ldflags, Cfg, ""),

ConfigureCmd = fun(Pkg, Flags) ->
                       {'get-deps',
                        "sh -c 'cd deps/" ++ Pkg ++
                            " && CFLAGS=\""++ CFLags ++"\" CPPFLAGS=\""++ CPPFLags ++"\" LDFLAGS=\""++ LDFLags ++"\"" ++
                            " ./configure " ++ Flags ++ "'"}
               end,

Conf = ProcessVars(ProcessVars, CONFIG, []),

Conf1 = case lists:keytake(post_hook_configure, 1, Conf) of
            {value, {_, Items}, Rest} ->
                [{post_hooks, [ConfigureCmd(Mod, string:join(Opts, " ")) || {Mod, Opts} <- Items]} | Rest];
            _ ->
                Conf
        end,

{ok, Cwd} = file:get_cwd(),
TestConfigFile = filename:join([Cwd, "test", "config.ctc"]),
TestConfig = case file:read_file_info(TestConfigFile) of
                 {ok, _} ->
                     "-userconfig ct_config_plain " ++ TestConfigFile ++ " ";
                 _ ->
                     ""
	     end,

ResolveDepPath = case IsRebar3 of
		     true ->
			 fun("deps/" ++ Rest) ->
				 Slash = string:str(Rest, "/"),
				 Dir = "_build/default/lib/" ++
				     string:sub_string(Rest, 1, Slash-1),
				 Dir ++ string:sub_string(Rest, Slash);
			    (Path) ->
				 Path
			 end;
		     _ ->
			 fun(P) ->
				  P
			 end
		 end,

CtIncludes = case lists:keyfind(eunit_compile_opts, 1, Conf1) of
		 false ->
		     [];
		 {_, EunitCompOpts} ->
		     [[" -include ", filename:join([Cwd, ResolveDepPath(IncPath)])]
		      || {i, IncPath} <- EunitCompOpts]
	     end,

ProcessErlOpt = fun({i, Path}) ->
			{i, ResolveDepPath(Path)};
		   (ErlOpt) ->
			ErlOpt
		 end,

Conf1a = ModCfg(Conf1, [erl_opts],
		 fun(ErlOpts) -> lists:map(ProcessErlOpt, ErlOpts) end, []),

Conf2a = [{ct_extra_params, lists:flatten(["-ct_hooks cth_surefire ", TestConfig,
					  CtIncludes])} | Conf1a],

Conf2 = case IsRebar3 of
	    true ->
		DepsFun = fun(DepsList) ->
				  lists:filtermap(fun({rebar_elixir_plugin, _, _}) ->
							  false;
						     ({DepName,_, {git,_, _} = Git}) ->
							  {true, {DepName, Git}};
						     (Dep) ->
							  true
						  end, DepsList)
			  end,
		RB1 = ModCfg(Conf2a, [deps], DepsFun, []),
		ModCfg(RB1, [plugins], fun(V) -> V -- [deps_erl_opts,
						       rebar_elixir_compiler,
						       rebar_exunit] ++
						     [rebar3_hex] end, []);
	    false ->
		Conf2a
	end,


Conf3 = case lists:keytake(xref_exclusions, 1, Conf2) of
            {value, {_, Items2}, Rest2} ->
                [{xref_queries, [{lists:flatten(["(XC - UC) || (XU - X - B ",
                                                 [[" - ", V] || V <- Items2], ")"]), []}]} | Rest2];
            _ ->
                Conf2
        end,

Conf5 = case lists:keytake(floating_deps, 1, Conf3) of
            {value, {_, FloatingDeps}, Rest4} ->
                case lists:keytake(deps, 1, Rest4) of
                    {value, {_, Deps}, Rest41} ->
                        ND = lists:map(fun({DepName, Ver, {git, Repo, _Commit}}=Dep) ->
                                               case lists:member(DepName, FloatingDeps) of
                                                   true ->
                                                       {DepName, ".*", {git, Repo}};
                                                   _ ->
                                                       Dep
                                               end;
                                          (Dep2) ->
                                               Dep2
                                       end, Deps),
                        [{deps, ND} | Rest41];
                    _ ->
                        Rest4
                end;
            _ ->
                Conf3
        end,

%% When running Travis test, upload test coverage result to coveralls:
Conf6 = case {lists:keyfind(cover_enabled, 1, Conf5), os:getenv("TRAVIS")} of
            {{cover_enabled, true}, "true"} ->
                JobId = os:getenv("TRAVIS_JOB_ID"),
                CfgTemp = ModCfg(Conf5, [deps], fun(V) -> [{coveralls, ".*", {git, "https://github.com/markusn/coveralls-erl.git", "master"}}|V] end, []),
                ModCfg(CfgTemp, [post_hooks], fun(V) -> V ++ [{ct, "echo '\n%%! -pa ebin/ deps/coveralls/ebin\nmain(_)->{ok,F}=file:open(\"erlang.json\",[write]),io:fwrite(F,\"~s\",[coveralls:convert_file(\"logs/all.coverdata\", \""++JobId++"\", \"travis-ci\")]).' > getcover.erl"},
                                                              {ct, "escript ./getcover.erl"}] end, []);
            _ ->
                Conf5
        end,

%io:format("ejabberd configuration:~n  ~p~n", [Conf6]),

Conf6.

%% Local Variables:
%% mode: erlang
%% End:
%% vim: set filetype=erlang tabstop=8:
