%% ---------------------------------------------------------------------------
%% @doc 编译自己改动的文件/编译指定文件。
%% Ps:
%% 1.> 自己改动的文件依赖于svn status "./src" > "./svn_status.log" 的执行结果；
%% 2.> svn_status.log和Emakefile文件应该在current working directory下。
%% @author zhaoyu
%% @since 2016-12-26
%% ---------------------------------------------------------------------------
-module(make_file).

-export([
        make_file/0
        ,make_file/2
        ,make_files/1
        ,changed_files/0
        ,get_compile_file_list/2
    ]).

-define(DEBUG(A, B), io:format(
        "~ts: [~p:~w] "++ A ++"~n", [util:debug_time_info(), ?MODULE, ?LINE, B]
    )
).

%% svn status产生的文件
-define(SVN_STATUS_FILE, "./svn_status.log").
%% erlang Emakefile文件
-define(Emakefile_FILE, "./Emakefile").

%% ---------------------------------------------------------------------------
%% @doc 编译自己改动的文件
-spec make_file() -> up_to_date | error.
%% ---------------------------------------------------------------------------
make_file() ->
    FileNameList = changed_files(),
    make_files(FileNameList).

%% 编译单个模块
make_file(Module, Environment) ->
    File = get_compile_file_list([atom_to_list(Module)], []),
    Opts = case Environment of
        dev ->
            [{d, 'DEV_SERVER'}];
        _ ->
            []
    end,
    Res  = make:files(File, Opts),
    io:format("compile finish[~p]!!!", [Res]).
%% ---------------------------------------------------------------------------
%% @doc 从svn_status.log列表中查找到需要编译的文件列表
-spec changed_files() -> FileNameList when
    FileNameList:: [list()]. %% e.g ["abc", "def"].
%% ---------------------------------------------------------------------------
changed_files() ->
    case file:open(?SVN_STATUS_FILE, [read]) of
        {ok, File} ->
            find_changed_files(File, []);
        {error, Reason} ->
            ?DEBUG("~p", [Reason]),
            []
    end.

find_changed_files(File, FileNameList) ->
    String = io:get_line(File, ''),
    case String =:= eof of
        true ->
            file:close(File),
            FileNameList;
        false ->
            case String =:= [] orelse String =:= "\n" of
                true ->
                    find_changed_files(File, FileNameList);
                false ->
                    %% 去掉句首句尾空格
                    StripString0 = string:strip(String),
                    %% 删除第一列
                    SubString = string:sub_string(StripString0, 2),
                    %% 去掉句首句尾空格
                    StripString = string:strip(SubString),
                    %% 去除换行符\n
                    NoLineString = re:replace(
                        StripString, "\n", "", [global, {return, list}]
                    ),
                    %% 把"\" 替换为 "/"
                    ReString = re:replace(
                        NoLineString, "\\\\", "/", [global, {return, list}]
                    ),
                    %% ReString 可能是文件夹名，也可能是文件名
                    OneLineFiles = get_one_line_files(ReString),
                    NewFileNameList = FileNameList ++ OneLineFiles,
                    find_changed_files(File, NewFileNameList)
            end
    end.

%% 每一行包含的文件列表
get_one_line_files("src/test") -> [];
get_one_line_files(ReString) ->
    case filename:extension(ReString) of
        ".erl" -> %% 是erlang文件
            FileName = filename:basename(ReString, ".erl"),
            [FileName];
        [] -> %% 是文件夹
            DirName = ReString,
            dir_file_list(DirName);
        _ -> %% 其他文件
            []
    end.

%% 文件夹目录下的文件
dir_file_list(DirName) ->
    %% 带有扩展名的文件名列表 也带双引号{ok, ["a.erl", "b.erl"]}
    {ok, List} = file:list_dir(DirName),
    %% 没有扩展名的文件名列表 但是带有双引号 ["a", "b"]
    FileList = [
        filename:basename(OneFile, ".erl") || OneFile <- List
        ,filename:extension(OneFile) =:= ".erl"
    ],
    FileList.


%% ---------------------------------------------------------------------------
%% @doc 编译指定的文件
-spec make_files(FileList) -> up_to_date | error when
    FileList    :: [list()].      %% e.g: m(["a", "b"])
%% ---------------------------------------------------------------------------
make_files(FileList) when is_list(FileList) ->
    ComFileList = get_compile_file_list(FileList, []),
    Return = make:files(ComFileList),
    Return;
make_files(File) ->
    make_files([File]).

%% 文件带上相对路径
get_compile_file_list([], FileList) -> FileList;
get_compile_file_list([File|List], FileList) ->
    {ok, CompileConfigList} = file:consult(?Emakefile_FILE),
    DirList = all_compile_paths(CompileConfigList, []),
    Fun = fun(Dir, L) ->
            WildCard = io_lib:format("~ts*~ts*.erl", [Dir, File]),
            L ++ filelib:wildcard(WildCard)
    end,
    NewFileList = lists:foldl(Fun, [], DirList) ++ FileList,
    get_compile_file_list(List, NewFileList).

%% 所有的需要编译的路径
all_compile_paths([], AllPaths) ->
    AllPaths;
all_compile_paths([CompileConfig | T], AllPaths) ->
    {DirList, _CompileConfigList} = CompileConfig,
    NewAllPaths = DirList ++ AllPaths,
    all_compile_paths(T, NewAllPaths).


