%% ---------------------------------------------------------------------------
%% @doc uio
%% @author ming_up@foxmail.com
%% @since  2016-04-08
%% @deprecated 带顺序的string格式化
%% ---------------------------------------------------------------------------
%% @author huangyongxing@yeah.net
%% @doc
%% 在原有的带序号格式化设计理念基础上，
%% 修改解析的方法（改为右往左解析，{}中只读取合法数字），提高解析效率；
%% 并增加对binary格式的Format字符串的自动转换支持。
%%
%% 此模块接口主要用于海外版本翻译时可以直接按序号调整参数位置，无须进行代码修改
%% 通常不涉及汉语的format，例如SQL语句等无须使用此模块接口（此模块效率相对较低）
%% （与io:format/2,3等不同，如果Format中需要同一个变量，
%% 可按序号多次在Format中出现，不必与Args数量一致）
%%
%% 备注：
%% 由于本项目统一使用utf8编码方式处理字符串，传入的参数format必然是unicode:charlist()，
%% 如果不是，要求使用text_enc:fix_enc/1进行处理后再作为参数，
%% 所以不会出现极端特殊情况下对{}匹配解析有误的可能性，
%% 原项目的字符串处理方式下，Format可能是latin1编码，
%% 通常情况下也无问题，但是极端情况下不能保证此点
%%
%% @end
%% ---------------------------------------------------------------------------

%% eg:  uio:format("params: {2}, {1}", [p1, p2])
%%      output:
%%        "params: p2, p1"
-module(uio).
-export([
        format/2
        ,thing_to_string/1
    ]).

%% 注意事项：
%% format的参数（包括Format和Args中的数据），需要注意编码，
%% 如果是后台web调用，经过mochiweb接口转换，变成了utf8编码的字节列表，
%% 此时需要先调用text_enc:fix_enc/1进行编码转换处理后才可以作为参数正常使用
format("", _) ->
    "";
format(<<>>, _) ->
    "";
format(Format, []) ->
    Format;
format(Format = [_ | _], Args = [_ | _]) ->
    Max = length(Args),
    ReverseFmt = lists:reverse(Format),
    parse_args(ReverseFmt, Args, Max, []);
format(Format, Args = [_ | _]) when is_binary(Format) ->
    format(unicode:characters_to_list(Format), Args);
format(_Format, _Args) ->
    % ?ERR("uio error: ~w", [_Format]),
    "".

%% 通常情况下从右往左解析，比从左往右解析效率更高，
%% 所以先反转Format字符串，再使用此接口进行参数填充格式化
parse_args([$} | ReverseFmt], Args, Max, Acc) ->
    case parse_num(ReverseFmt, []) of
        {true, Num, Remain} ->
            NewAcc =
            case Num > Max orelse Num =< 0 of
                true  ->
                    lists:concat(["{", Num, "}", Acc]);
                false ->
                    Elem = lists:nth(Num, Args),
                    thing_to_string(Elem) ++ Acc
            end,
            parse_args(Remain, Args, Max, NewAcc);
        {false, NumChars, Remain} ->
            parse_args(Remain, Args, Max, NumChars ++ [$} | Acc])
    end;
parse_args([Char | ReverseFmt], Args, Max, Acc) ->
    parse_args(ReverseFmt, Args, Max, [Char | Acc]);
parse_args([], _Args, _Max, Acc) ->
    Acc.

%% 解析花括号{}之间的数字
%% -> {true, Num, Remain} | {false, NumChars, Remain}
parse_num([${ | Remain], TmpNumAcc) ->
    Num = list_to_integer(TmpNumAcc),
    {true, Num, Remain};
parse_num([C | Remain], TmpNumAcc) when C >= $0 andalso C =< $9 ->
    parse_num(Remain, [C | TmpNumAcc]);
parse_num([], TmpNumAcc) ->
    {false, TmpNumAcc, []};
parse_num(List, TmpNumAcc) ->
    {false, TmpNumAcc, List}.

%% 转换为
thing_to_string(X) when is_integer(X) -> integer_to_list(X);
thing_to_string(X) when is_float(X)   -> float_to_list(X);
thing_to_string(X) when is_atom(X)    -> atom_to_list(X);
thing_to_string(X) when is_binary(X)  -> unicode:characters_to_list(X);
thing_to_string(X) when is_tuple(X)   -> io_lib:format("~w", [X]);
thing_to_string(X) when is_list(X)    -> X.

