#!/usr/bin/env escript
%% -*- erlang -*-
-include_lib("kernel/include/file.hrl").


is_up_to_date([Target|CheckResources]) ->
    case file:read_file_info(Target) of
        {ok, #file_info{mtime = TargetMTime}} ->
            lists:all(fun(File) ->
                              case file:read_file_info(File) of
                                  {ok, #file_info{mtime = MTime}} ->
                                      MTime =< TargetMTime;
                                  _ ->
                                      false
                              end
                      end, CheckResources);
        _ ->
            false
    end.

check_source(FileList, EchoString, Fun) ->
    case is_up_to_date(FileList) of
        true ->
            ok;
        _ ->
            io:format("~s~n",[EchoString]),
            erlang:apply(Fun, [])
    end.

gpb_compile_file(Proto, Opts) ->
    check_ok_or_halt(gpb_compile:file(Proto, Opts)).

compile_file(Erl, Opts) ->
    check_ok_or_halt(compile:file(Erl, Opts)).

check_ok_or_halt(Res) ->
    if Res =:= ok -> ok;
       element(1, Res) =:= ok -> ok;
       true -> halt(1)
    end.

deps_dir() ->
    case os:getenv("REBAR_DEPS_DIR") of
        false ->
            %% Elixir 1.12+
            os:getenv("REBAR_BARE_COMPILER_OUTPUT_DIR");
        DepsDir ->
            DepsDir
    end.

checkouts_out_dir() ->
    os:getenv("REBAR_CHECKOUTS_OUT_DIR").

gpb_exists_via_env(DirEnvValue) ->
    if DirEnvValue == false  ->
            false;
       is_list(DirEnvValue) ->
            filelib:is_dir(filename:join(DirEnvValue, "gpb"))
    end.

main(_) ->
    REBAR_CHECKOUTS_OUT_DIR = checkouts_out_dir(),
    REBAR_DEPS_DIR = deps_dir(),
    GpbExistsInCheckoutsOutDir = gpb_exists_via_env(REBAR_CHECKOUTS_OUT_DIR),
    GpbExistsInDepsDir = gpb_exists_via_env(REBAR_DEPS_DIR),

    {EbinPath, TestPath} =
        if GpbExistsInCheckoutsOutDir ->
                %% With _checkouts and rebar3 3.14 or later
                {filename:join([REBAR_CHECKOUTS_OUT_DIR, "gpb", "ebin"]),
                 filename:join([REBAR_CHECKOUTS_OUT_DIR, "gpb", "test"])};
           GpbExistsInDepsDir ->
                {filename:join([REBAR_DEPS_DIR, "gpb", "ebin"]),
                 filename:join([REBAR_DEPS_DIR, "gpb", "test"])};
           true ->
                %% rebar2 (or make)
                {"./ebin", "./test"}
        end,

    filelib:ensure_dir(filename:join(EbinPath, ".dummy")),

    CheckList =
        [{["descr_src/gpb_descriptor.erl",
           "priv/proto3/google/protobuf/descriptor.proto"],
          "Compiling descriptor.proto...",
          fun() ->
                  code:add_patha(EbinPath),
                  Opts = [{i, "priv/proto3/google/protobuf"},
                          {o, "descr_src"}, {module_name_prefix, "gpb_"}],
                  Opts2 = Opts ++ [report_warnings, report_errors],
                  gpb_compile_file("descriptor.proto", Opts2)
          end},
         {[filename:join(EbinPath, "gpb_descriptor.beam"),
           "descr_src/gpb_descriptor.erl"],
          "Compiling gpb_descriptor.erl...",
          fun() ->
                  compile_file("descr_src/gpb_descriptor.erl",
                               [{i, "descr_src"},{i,"include"},
                                {outdir, EbinPath},debug_info,
                                report])
          end},
         {[filename:join(EbinPath, "gpb_compile_descr.beam"),
           "descr_src/gpb_compile_descr.erl",
           "descr_src/gpb_descriptor.hrl"],
          "Compiling gpb_compile_descr.proto...",
          fun() ->
                  compile_file("descr_src/gpb_compile_descr.erl",
                               [{i, "descr_src"},{i,"include"},
                                {outdir, EbinPath},debug_info,
                                report])
          end}
        ],
    [check_source(FileList, EchoStr, Cmd)
     || {FileList, EchoStr, Cmd} <- CheckList],
    case filelib:is_dir(TestPath) of
        true ->
            %% eg: "rebar3 eunit" or "rebar2 eunit"
            %% but not "rebar3 compile"
            %% and not when compiling gpb as a dependency, since
            %% the test directory does not exist in the hex package.
            TestBeamFile =
                filename:join(TestPath, "gpb_compile_descr_tests.beam"),
            Fun = fun() ->
                          compile_file("descr_src/gpb_compile_descr_tests.erl",
                                       [{i, "descr_src"},{i,"include"},
                                        {outdir, TestPath},debug_info])
                  end,
            check_source([TestBeamFile,
                          "descr_src/gpb_compile_descr.erl",
                          "descr_src/gpb_compile_descr_tests.erl",
                          "descr_src/gpb_descriptor.hrl"],
                         "Compiling gpb_compile_descr_tests.erl...",
                         Fun);
        _ ->
            next
    end.
