%% ---------------------------------------------------------------------------
%% @doc ulists
%% @author ming_up@foxmail.com
%% @since  2015-11-26
%% @deprecated  lists模块拓展
%% ---------------------------------------------------------------------------
-module(ulists).
-include("common.hrl").
-export([
        concat/1                   %% 列表元素连接
        ,explode/3                 %% 分割字符串
        ,implode/2                 %% 组装字符串
        ,list_plus/1               %% 列表元素对应相加
        ,kv_list_plus/1            %% key-value列表对应相加
        ,kv_list_plus_extra/1      %% key-value列表元素相加，不要求列表元素对应
        ,kv_list_plus_extra/2
        ,object_list_plus/1        %% object_list列表对应相加
        ,object_list_merge/1       %% 合并物品列表[{Type, Id, Num}]
        ,list_shuffle/1            %% 列表随机
        ,list_to_string/2           %% 列表转化
        ,is_same_string/2           %% 判断两个字符串是否相同
        ,find/2                     %% 根据条件查找符合的第一个元素
        ,find_index/2               %% 根据条件查找符合的第一个元素的序号
        ,sorted_insert/3            %% 按照排序规则往有序列表插入一个元素
        ,sublist/2                  %% 截取list，与lists:sublist/2的区别是多返回剩余列表
        ,is_in_range/2
        % ,disorganize/1              %% 打乱一个列表 已有list_shuffle
        ,elem_multiply/2            %% 列表里每个元素裂变多次
        ,all_in_list/2
    ]).
-export([
        split/2                     %% 分割列表，与lists:split/2有所区别
        ,keymax/2                   %% TupleList按第Nth个位置取最大值
        ,keymin/2                   %% TupleList按第Nth个位置取最小值
        ,keymaxwith/3               %% TupleList按第Nth个位置取自定义最大值
        ,index_of/2                 %% 找到元素的index
        ,list_equal/2               %% 判断两个list在排除顺序差异之后是否相等
        ,min_count/2                %% 基于基准id列表，从计数列表中查找最小计数的id（可能不在计数列表中）
    ]).

%% 在List中的每两个元素之间插入一个分隔符
implode(_S, [])->
    [<<>>];
implode(S, L) when is_list(L) ->
    implode(S, L, []).
implode(_S, [H], NList) ->
    lists:reverse([uio:thing_to_string(H) | NList]);
implode(S, [H | T], NList) ->
    L = [uio:thing_to_string(H) | NList],
    implode(S, T, [S | L]).

%% 字符->列
explode(S, B)->
    re:split(B, S, [{return, list}]).
explode(S, B, int) ->
    [list_to_integer(Str) || Str <- explode(S, B), length(Str) > 0].

%% 列表元素连接
concat(List) ->
    lists:flatmap(fun uio:thing_to_string/1, List).

%% 随机打乱list元素顺序
list_shuffle(L = [_]) -> L;
list_shuffle([]) -> [];
list_shuffle(L) ->
    Len = length(L)+10000,
    List1 = [{urand:rand(1, Len), X} || X <- L],
    List2 = lists:sort(List1),
    [E || {_, E} <- List2].

%% 列表元素对应相加
list_plus([List]) -> List;
list_plus([List1, List2|T]) ->
    SumList = list_plus(List1, List2, []),
    list_plus([SumList|T]).

list_plus([], _, Result) -> lists:reverse(Result);
list_plus(_, [], Result) -> lists:reverse(Result);
list_plus([H1|T1], [H2|T2], Result) ->
    list_plus(T1, T2, [H1+H2|Result]).

%% key-value列表元素对应相加
kv_list_plus([List]) -> List;
kv_list_plus([List1, List2|T]) ->
    SumList = kv_list_plus(lists:keysort(1,List1), lists:keysort(1,List2), []),
    kv_list_plus([SumList|T]).

kv_list_plus([], T, Result) -> T++Result;
kv_list_plus(T, [], Result) -> T++Result;
kv_list_plus([{K,V1}|T1], [{K,V2}|T2], Result) ->
    kv_list_plus(T1, T2, [{K,V1+V2}|Result]);
kv_list_plus([{_,_}|_]=L1, [{K2,V2}|T2], Result) ->
    kv_list_plus(L1, T2, [{K2,V2}|Result]).

%% key-value列表元素相加，不要求列表元素对应
kv_list_plus_extra([]) -> [];
kv_list_plus_extra([List]) -> List;
kv_list_plus_extra([List1, List2|T]) ->
    SumList = kv_list_plus_extra(List1, List2),
    kv_list_plus_extra([SumList|T]).

kv_list_plus_extra(List, []) -> List;
kv_list_plus_extra(List1, [{K, V2}|T]) ->
    case lists:keyfind(K, 1, List1) of
        {K, V1} -> V = V1 + V2;
        false -> V = V2
    end,
    kv_list_plus_extra(lists:keystore(K, 1, List1, {K, V}), T).

%% object_list列表元素相加，不要求列表元素对应
object_list_plus([List]) ->
    F = fun
        ({ {ObjectType, GoodsId}, Num}, AccList) ->
            [{ObjectType, GoodsId, Num}| AccList];
        ({ObjectType, GoodsId, Num}, AccList) ->
            [{ObjectType, GoodsId, Num}| AccList]
    end,
    lists:foldl(F, [], List);
object_list_plus([List1, List2|T]) ->
    NewList1 = object_list_plus([], List1),
    SumList = object_list_plus(NewList1, List2),
    object_list_plus([SumList|T]).

object_list_plus(List, []) -> List;
object_list_plus(List1, [{ObjectType, GoodsId2, Num2}|T]) ->
    NewObjectList = case lists:keyfind({ObjectType, GoodsId2}, 1, List1) of
        {_, GoodsNum} -> [{ {ObjectType, GoodsId2}, Num2+GoodsNum}| lists:keydelete({ObjectType, GoodsId2}, 1, List1)];
        false    -> [{ {ObjectType, GoodsId2}, Num2} | List1]
    end,
    object_list_plus(NewObjectList, T).

object_list_merge(List) ->
    object_list_merge(List, []).

object_list_merge([], Acc) -> Acc;
object_list_merge([T|L], Acc) ->
    NewAcc = case T of
        {ObjectType, GoodsId, Num} ->
            case lists:keyfind(GoodsId, 2, Acc) of
                {ObjectType, GoodsId, PreNum} ->
                    [{ObjectType, GoodsId, PreNum + Num}|lists:keydelete(GoodsId, 2, Acc)];
                _ ->
                    [{ObjectType, GoodsId, Num}|Acc]
            end;
        _ -> Acc
    end,
    object_list_merge(L, NewAcc).

%% [A,B,C,D..] => "A,B,C,D"
list_to_string([], _Dot) -> "";
list_to_string(List, Dot) ->
    lists:concat(concat_with(List, Dot)).

concat_with([A], _Dot) ->
    [A];
concat_with([H|T], Dot) ->
    [H,Dot|concat_with(T, Dot)].

is_same_string(Str, OtherStr) ->
    StrBin = util:make_sure_binary(Str),
    OtherStrBin = util:make_sure_binary(OtherStr),
    StrBin == OtherStrBin.

find(Pred, [H|T]) ->
    case Pred(H) of
        true ->
            {ok, H};
        false ->
            find(Pred, T)
    end;

find(_Pred, []) -> error.

find_index(Pred, List) ->
    find_index(Pred, List, 1).

find_index(Pred, [H|T], Index) ->
    case Pred(H) of
        true ->
            Index;
        false ->
            find_index(Pred, T, Index + 1)
    end;
find_index(_Pred, [], _Index) -> 0.

%% 往一个有序的列表里面插入一个数据，插在第一个与插入元素对比，返回true的元素的前面
-spec sorted_insert(F, Item, List) -> {Index, SortedList} when
    F :: fun((Elem :: T, Elem :: T) -> boolean()),
    Item :: T,
    List :: [T],
    Index :: integer(),
    SortedList :: [T],
    T :: term().

sorted_insert(F, Item, List) ->
    sorted_insert([], 1, F, Item, List).

sorted_insert(Acc, Index, F, Item, [H|T] = L) ->
    case F(Item, H) of
        true ->
            SortedList = lists:reverse([Item|Acc], L),
            {Index, SortedList};
        false ->
            sorted_insert([H|Acc], Index + 1, F, Item, T)
    end;
sorted_insert(Acc, Index, _F, Item, []) ->
    SortedList = lists:reverse([Item|Acc]),
    {Index, SortedList}.

%%--------------------------------------------------
%% 截取一定长度的列表
%% @param  L   list
%% @param  Len 长度
%% @return     {SubList, RemainList}
%%--------------------------------------------------
sublist(L, Len) ->
    do_sublist(L, Len, 0, []).

do_sublist(L, Len, Len, Acc) -> {lists:reverse(Acc), L};
do_sublist([], _, _, Acc) -> {lists:reverse(Acc), []};
do_sublist([T|L], Len, CurLen, Acc) ->
    do_sublist(L, Len, CurLen + 1, [T|Acc]).

%%--------------------------------------------------
%% 检测Val是否在列表中的某个范围值内
%% @param  List [{Min, Max}]
%% @param  Val  整数值
%% @return      {Min, Max}|false
%%--------------------------------------------------
is_in_range([], _) -> false;
is_in_range([T|L], Val) ->
    case T of
        {Min, Max} when Val >= Min andalso Val =< Max ->
            {Min, Max};
        _ -> is_in_range(L, Val)
    end;
is_in_range(_, _) -> false.


elem_multiply(List, N) when N > 1 ->
    [I || I <- List, _ <- lists:seq(1, N)];

elem_multiply(List, 1) ->
    List.


-spec all_in_list(SmallList, BigList) -> true | false when
    SmallList :: list(), BigList :: list().
all_in_list([], _BigList) -> true;
all_in_list([I|SmallList], BigList) ->
    case lists:member(I, BigList) of
        true -> all_in_list(SmallList, BigList);
        _ -> false
    end.

%% split(N, List) -> {PreList, TailList}.
%% 与lists:split(N, List)行为不同，如果List不够长度N，则PreList = List, TailList = []
split(N, List) ->
    split(N, List, []).
split(0, List, PreList) ->
    {lists:reverse(PreList), List};
split(N, [H | List], PreList) ->
    split(N - 1, List, [H | PreList]);
split(_, [], PreList) ->
    {lists:reverse(PreList), []}.

%% 从TupleList中查找Nth最大值的Tuple项，如果空列表返回false
%% 数据不合法将直接出错
keymax(N, TupleList = [_|_]) ->
    MaxFun = fun(Elem, MaxElem) ->
            Elem > MaxElem
    end,
    keymaxwith(N, TupleList, MaxFun);
keymax(_N, _TupleList) ->
    false.

%% 从TupleList中查找Nth最小值的Tuple项，如果空列表返回false
keymin(N, TupleList = [_|_]) ->
    MaxFun = fun(Elem, MaxElem) ->
            Elem < MaxElem
    end,
    keymaxwith(N, TupleList, MaxFun);
keymin(_N, _TupleList) ->
    false.

%% 从TupleList中查找Nth最大的Tuple项（大小由自己定义的MaxFun进行比较）
%% 如果空列表或者MaxFun不合法，则返回false
keymaxwith(N, [Tuple | TupleList], MaxFun) when is_function(MaxFun, 2) ->
    Elem = element(N, Tuple),
    keymax_2(MaxFun, N, TupleList, Tuple, Elem);
keymaxwith(_N, _List, _MaxFun) ->
    false.

%% for internal call
keymax_2(MaxFun, N, [Tuple | TupleList], MaxTuple, MaxElem) ->
    Elem = element(N, Tuple),
    case MaxFun(Elem, MaxElem) of
        true  ->
            keymax_2(MaxFun, N, TupleList, Tuple, Elem);
        false ->
            keymax_2(MaxFun, N, TupleList, MaxTuple, MaxElem)
    end;
keymax_2(_MaxFun, _N, [], MaxTuple, _MaxElem) ->
    MaxTuple.

%% 元素位于列表中的位置，从1开始，没有找到的话返回0
-spec index_of(Element, List) -> Index when
    Element :: any(), List :: list(), Index :: integer().
index_of(Element, List) ->
    string:str(List, [Element]).

%% 判断单层list是否相等（不关心元素顺序）
%% 如果元素也是个需要不关心子元素顺序的list，则要分别对各元素进行检查
-spec list_equal(ListA, ListB) -> IsEqual when
    ListA :: list(), ListB :: list(), IsEqual :: boolean().
list_equal(ListA, ListB) ->
    length(ListA) =:= length(ListB) andalso
    sub_list_equal(ListA, ListB).

sub_list_equal([], []) ->
    true;
sub_list_equal(ListA, ListA) ->
    true;
sub_list_equal([ElemA | RemainListA], [ElemA | RemainListB]) ->
    sub_list_equal(RemainListA, RemainListB);
sub_list_equal([ElemA | RemainListA], ListB) ->
    case lists:member(ElemA, ListB) of
        true  ->
            NewListB = lists:delete(ElemA, ListB),
            sub_list_equal(RemainListA, NewListB);
        false ->
            false
    end;
sub_list_equal(_, _) ->
    false.

%% 基于基准id列表，从计数列表中查找最小计数的id（可能不在计数列表中）
%% 参数List为特定Id出现数量的记录列表，
%% 此接口将根据基准id列表(BaseIdList)，找出数量最少的Id。
%%
%% 在BaseIdList不为空的情况下，返回值不可能为undefined，必定为{Id, Count}
%% e.g. min_count([1,2,3,4], [{1,3},{2,5},{4,4}]) -> {3,0}.
-spec min_count(BaseIdList, List) -> {IdOfMin, CountOfMin} | undefined when
    BaseIdList :: [ Id :: integer() ],
    List :: [ {Id :: integer(), Count :: integer()} ],
    IdOfMin :: integer(), CountOfMin :: integer().
min_count(BaseIdList, List) ->
    min_count_2(BaseIdList, List, undefined).

min_count_2([], _List, Min) ->
    Min;
min_count_2([Id | _RestBaseList], [], _) ->
    {Id, 0};
min_count_2([Id | RestBaseList], List, Min) ->
    case lists:keytake(Id, 1, List) of
        {value, {Id, Count}, RestList} ->
            case Min of
                {_IdOfMin, CountOfMin} when Count >= CountOfMin ->
                    min_count_2(RestBaseList, RestList, Min);
                {_IdOfMin, _CountOfMin} ->
                    min_count_2(RestBaseList, RestList, {Id, Count});
                _ ->
                    %% 首次赋值Min
                    min_count_2(RestBaseList, RestList, {Id, Count})
            end;
        _ ->
            {Id, 0}
    end.

