%%%--------------------------------------
%%% @Module  : lib_resource_back
%%% @Author  : liuxl
%%% @Created : 2017-04-06
%%% @Description:  资源召回
%%%--------------------------------------
-module(lib_resource_back).
-compile(export_all).

-include("server.hrl").
-include("figure.hrl").
-include("errcode.hrl").
-include("common.hrl").
-include("def_module.hrl").
-include("def_daily.hrl").
-include("resource_back.hrl").
-include("def_fun.hrl").
-include("daily.hrl").
-include("activitycalen.hrl").
-include("def_goods.hrl").
-include("goods.hrl").
-include("def_event.hrl").
-include("rec_event.hrl").
-include("predefine.hrl").
-include("dungeon.hrl").
-include("attr.hrl").
-include("dungeon_pet.hrl").
-include("vip.hrl").
-include("skill.hrl").

-export([
    login/1
    , handle_event/2
    , resource_back/5
]).

%% 事件处理
handle_event(PS,
    #event_callback{
        type_id = ?EVENT_PARTICIPATE_ACT,
        data    = #act_data{act_id = ActId, act_sub = ActSub, num = Num}}) ->
    #player_status{figure = #figure{lv = Lv}} = PS,
    case lib_module:is_open(Lv,?MOD_RESOURCE_BACK) of
        true ->
            increase_ps_act_status(PS, {ActId, ActSub, Num});
        false -> {ok, PS}
    end;
handle_event(PS, #event_callback{type_id = ?EVENT_LV_UP}) ->
    #player_status{figure = #figure{lv = Lv}} = PS,
    OpenLv = lib_module:get_open_lv(?MOD_RESOURCE_BACK),
    if
        Lv < OpenLv -> {ok, PS};
        true ->
            NewPS = login(PS),
            lib_player_event:remove_listener(?EVENT_LV_UP, lib_resource_back, handle_event),
            {ok, NewPS}
    end;
handle_event(PS, _) ->
    {ok, PS}.


increase_ps_act_status(PS, {ActId, ActSub, Num}) ->
    %?%PRINT("handle event ~p~n", [{ActId, Num}]),
    ActIds = data_resource_back:get_act_ids(),
    case lists:member({ActId, ActSub}, ActIds) of
        true -> increase_ps_act_status_do(PS, {ActId, ActSub, Num});
        false -> {ok, PS}
    end.

increase_ps_act_status_do(PS, {ActId, ActSub, Num}) ->
    #player_status{id = RoleId, resource_back = ResourceBack} = PS,
    #resource_back{res_act_map = ResActMap} = ResourceBack,
    {NeedUpdateDb, NewResActMap} = update_today_res_act(PS, ResActMap, {ActId, ActSub, Num}),
    NewResourceBack = ResourceBack#resource_back{res_act_map = NewResActMap},
    case NeedUpdateDb of
        true -> db_update_resource_back(RoleId, NewResourceBack);
        false -> skip
    end,
    NewPS = PS#player_status{resource_back = NewResourceBack},
    {ok, NewPS}.

%%巅峰竞技完成一次就完成
update_today_res_act(_PS, ResActMap, {ActId, ActSub, _})
    when ActId == ?MOD_TOPPK ->
    Today = maps:get(?TODAY, ResActMap, #{}),
    ResAct = #res_act{act_id = ActId, act_sub = ActSub, lefttimes = 0, max = 0, state = ?STATE_FINISHED},
    NewToday = maps:put({ActId, ActSub}, ResAct, Today),
    {true, maps:put(?TODAY, NewToday, ResActMap)};
update_today_res_act(_PS, ResActMap, {ActId, ActSub, Num}) ->
    Today = maps:get(?TODAY, ResActMap, #{}),
    case maps:get({ActId, ActSub}, Today, none) of
        #res_act{lefttimes = LeftTimes, state = State} = ResAct when State == ?STATE_NOT_FIND ->
            NewLeftTime = max(LeftTimes - Num, 0),
            NewState = ?IF(NewLeftTime == 0, ?STATE_FINISHED, ?STATE_NOT_FIND),
            NewResAct = ResAct#res_act{lefttimes = NewLeftTime, state = NewState},
            NewToday = maps:put({ActId, ActSub}, NewResAct, Today),
            {true, maps:put(?TODAY, NewToday, ResActMap)};
        #res_act{} -> {false, ResActMap};
        none ->
            Limit = get_act_limit(ActId, ActSub),
            LeftTimes = max(Limit - Num, 0),
            State = ?IF(LeftTimes == 0, ?STATE_FINISHED, ?STATE_NOT_FIND),
            ResAct = #res_act{act_id = ActId, act_sub = ActSub, lefttimes = LeftTimes, max = Limit, state = State},
            NewToday = maps:put({ActId, ActSub}, ResAct, Today),
            {true, maps:put(?TODAY, NewToday, ResActMap)}
    end.

%%---------------------------------- 登陆 ------------------------------------
login(PS) ->
    NowTime = utime:unixtime(),
    #player_status{id = RoleId, figure = #figure{lv = Lv}} = PS,
    case lib_module:is_open(Lv,?MOD_RESOURCE_BACK) of
        true ->
            ActIds = data_resource_back:get_act_ids(),
            {OldClearTime, ResActMap}
                = case db_select_res_act_list(RoleId) of
                      [] ->
                          Today = init_today_res_act(PS, ActIds),
                          {get_today_clear_time(), maps:put(?TODAY, Today, #{})};
                      List ->
                          F = fun(Item, {_Time, Map}) ->
                              [ClearTime1, DayType, ActList_B] = Item,
                              ActList = util:bitstring_to_term(ActList_B),
                              ResActMap = make_res_act_map(DayType, PS, ActIds, ActList),
                              {ClearTime1, maps:put(DayType, ResActMap, Map)}
                              end,
                          lists:foldl(F, {NowTime, #{}}, List)
                  end,
            ResourceBack = update_res_act_login(PS, ResActMap, OldClearTime, NowTime),
            NewPS = PS#player_status{resource_back = ResourceBack},
            db_update_resource_back(RoleId, ResourceBack),
            NewPS;
        false ->
            lib_player_event:add_listener(?EVENT_LV_UP, lib_resource_back, handle_event, []),
            PS#player_status{resource_back = #resource_back{}}
    end.

update_res_act_login(PS, ResActMap, OldClearTime, NowTime) ->
    case NowTime - OldClearTime of
        DTime when DTime =< 1 -> #resource_back{cleartime = OldClearTime, res_act_map = ResActMap};
        DTime ->
            NewResActMap = move_res_act_list(PS, DTime, ResActMap),
            NewClearTime = get_today_clear_time(),
            #resource_back{cleartime = NewClearTime, res_act_map = NewResActMap}
    end.


move_res_act_list(PS, DTime, ResActMap) ->
    MoveTimes = 1 + (DTime div ?ONE_DAY_SECONDS),
    %?PRINT("move_res_act_list MoveTimes ~p~n", [MoveTimes]),
    ActIds = data_resource_back:get_act_ids(),
    if
        MoveTimes == 1 -> move_res_act_list_1(PS, ActIds, ResActMap);
        MoveTimes == 2 -> move_res_act_list_2(PS, ActIds, ResActMap);
        true -> move_res_act_list_more(PS, ActIds, ResActMap)
    end.

%% 登陆离上次离线跨一天
move_res_act_list_1(PS, ActIds, ResActMap) ->
    Yesterday = maps:get(?YESTERDAY, ResActMap, #{}),
    F = fun(_K, V) -> V#res_act.state == ?STATE_NOT_FIND end,
    BYesterday = maps:filter(F, Yesterday),
    Today = maps:get(?TODAY, ResActMap, #{}),
    %PreWeekDay = get_pre_weekday(),
    Today1 = correct_logout_act(PS, Today, ActIds, yesterday),
    NewYesterday = maps:filter(F, Today1),
    NewToday = init_today_res_act(PS, ActIds),
    ResActMap2 = maps:put(?TODAY, NewToday, #{}),
    ResActMap3 = maps:put(?YESTERDAY, NewYesterday, ResActMap2),
    maps:put(?B_YESTERDAY, BYesterday, ResActMap3).

%% 登陆离上次离线跨两天
move_res_act_list_2(PS, ActIds, ResActMap) ->
    F = fun(_K, V) -> V#res_act.state == ?STATE_NOT_FIND end,
    Today = maps:get(?TODAY, ResActMap, #{}),
    %PrePreWeekDay = get_pre_pre_weekday(),
    Today1 = correct_logout_act(PS, Today, ActIds, byesterday),
    BYesterday = maps:filter(F, Today1),
    NewToday = init_today_res_act(PS, ActIds),
    ResActMap2 = maps:put(?TODAY, NewToday, #{}),
    Yesterday = init_res_act_yesterday(PS, ActIds),
    ResActMap3 = maps:put(?YESTERDAY, Yesterday, ResActMap2),
    maps:put(?B_YESTERDAY, BYesterday, ResActMap3).

%% 登陆离上次离线跨两天以上
move_res_act_list_more(PS, ActIds, _ResActMap) ->
    Today = init_today_res_act(PS, ActIds),
    ResActMap2 = maps:put(?TODAY, Today, #{}),
    Yesterday = init_res_act_yesterday(PS, ActIds),
    ResActMap3 = maps:put(?YESTERDAY, Yesterday, ResActMap2),
    BYesterday = init_res_act_b_yesterday(PS, ActIds),
    maps:put(?B_YESTERDAY, BYesterday, ResActMap3).


%%一键找回
quick_resource_back(Status, Type)->
    #player_status{id=RoleID,resource_back = ResourceBack} = Status,
    NType = ?IF(Type == 1, ?TYPE_BGOLD, ?TYPE_COIN),
    [Gains0,Costs0,ResActMap0] = calc_total(?YESTERDAY,NType,Status),
    [Gains1,Costs1,NewResActMap] = calc_total(?B_YESTERDAY,NType,Status#player_status{resource_back=ResourceBack#resource_back{res_act_map=ResActMap0}}),
    Gains = Gains0++Gains1,
    Costs = Costs0++Costs1,
    case lib_goods_api:cost_object_list_with_check(Status, Costs, resource_back, "") of
        {true, NewStatus,_} ->
            Produce = #produce{title = utext:get(11), content = utext:get(12), reward = Gains, type = resource_back, show_tips = 1},
            {ok, NewStatus1} = lib_goods_api:send_reward_with_mail(NewStatus, Produce, ?MOD_RESOURCE_BACK),
            NewResourceBack = ResourceBack#resource_back{res_act_map=NewResActMap},
            db_update_resource_back(RoleID, NewResourceBack),
            {true, NewStatus1#player_status{resource_back = NewResourceBack}};
        {false, Res, NewPS} ->
            {false, Res, NewPS}
    end.


calc_total(DayT,Type,Status)->
    #player_status{figure = #figure{lv = Lv}, resource_back = #resource_back{res_act_map = ResActMap}} = Status,
    DatasMap = maps:get(DayT, ResActMap, #{}),
    Datas = maps:to_list(DatasMap),
    [NDatas,Gains,Costs] = calc_total(Status,Datas,Type,Lv,[],[],[]),
    NDatasMap = maps:from_list(NDatas),
    NewResActMap = maps:put(DayT,NDatasMap,ResActMap),
    [Gains,Costs,NewResActMap].

calc_total(_Status,[],_Type,_Lv,Gains,Costs,Datas)->
    [Datas,Gains,Costs];
calc_total(Status,[{K,H}|T],Type,Lv,Gains,Costs,Datas)->
    #res_act{
        act_id = Id,
        act_sub = ActSub,
        lefttimes=Times
    }=H,
    case get_cfg(Status,Id, ActSub, Lv) of
        #base_res_act{coin_per = CoinCost, coin_goods = CoinReward0,coin_exp=CExp, gold_per = GoldCost, gold_goods = GoldReward0,gold_exp=GExp} when Times>0 ->
            case (CExp>0 orelse GExp>0) orelse (CoinReward0 /= [] orelse GoldReward0/= []) of
                true->%%有实际奖励才给找回
                    Extra = case Type of
                        ?TYPE_BGOLD->
                            GExp;
                        _->
                            CExp
                    end,
                    ExpValue0 = data_exp_base:get(Lv),
                    LvExtra = 10000,%data_award_exp:get_exp_extra(Id, ActSub,Lv),
                    ExpValue = round(ExpValue0*LvExtra/10000*Extra/10000),
                    CoinReward = ?IF(Extra>0,[{?TYPE_EXP,?GOODS_ID_EXP,ExpValue}|CoinReward0],CoinReward0),
                    GoldReward = ?IF(Extra>0,[{?TYPE_EXP,?GOODS_ID_EXP,ExpValue}|GoldReward0],GoldReward0),
                    case CoinCost of
                        [{Type, _, _}] ->
                            RealReward = calc_reward(Status, Id, ActSub, CoinReward, Times),
                            RealCost = [{MoneyType, MoneyTypeId, MoneyNum * Times} || {MoneyType, MoneyTypeId, MoneyNum} <- CoinCost],
                            NewCosts = case RealReward /= [] of
                                true->
                                    RealCost++Costs;
                                _->
                                    Costs
                            end,
                            calc_total(Status,T,Type,Lv,RealReward++Gains,NewCosts,[{K,H#res_act{lefttimes=0,state=?STATE_FIND}}|Datas]);
                        _ ->
                            case GoldCost of
                                [{Type, _, _}] ->
                                    RealReward = calc_reward(Status, Id, ActSub, GoldReward, Times),
                                    RealCost = [{MoneyType, MoneyTypeId, MoneyNum * Times} || {MoneyType, MoneyTypeId, MoneyNum} <- GoldCost],
                                    NewCosts = case RealReward /= [] of
                                        true->
                                            RealCost++Costs;
                                        _->
                                            Costs
                                    end,
                                    calc_total(Status,T,Type,Lv,RealReward++Gains,NewCosts,[{K,H#res_act{lefttimes=0,state=?STATE_FIND}}|Datas]);
                                _->
                                    calc_total(Status,T,Type,Lv,Gains,Costs,[{K,H}|Datas])
                            end
                    end;
                _->
                    calc_total(Status,T,Type,Lv,Gains,Costs,[{K,H}|Datas])
            end;
        _->
            calc_total(Status,T,Type,Lv,Gains,Costs,[{K,H}|Datas])
    end.

%%----------------------------------- 资源找回 ---------------------------------------
resource_back(PS, Id, ActSub, Type, Times) ->
    %CostType = ?IF(Type == ?TYPE_COIN, ?TYPE_COIN, ?TYPE_GOLD),
    case check_resource_back(PS, Id, ActSub, Type, Times) of
        {true, RewardLv, Reward, Cost, YResAct, BYResAct} ->
            resource_back_do(PS, Id, ActSub, RewardLv, Reward, Cost, Times, YResAct, BYResAct);
        {false, Res} -> {false, Res, PS}
    end.

resource_back_do(PS, Id, ActSub, RewardLv, Reward, Cost, Times, YResAct, BYResAct) ->
    #player_status{id = RoleId, sid = Sid, resource_back = ResourceBack} = PS,
    case lib_goods_api:cost_object_list_with_check(PS, Cost, resource_back, "") of
        {true, NewPS,_} ->
            %?PRINT("resource_back succ Reward:~p~n", [Reward]),
            Produce = #produce{title = utext:get(255), content = utext:get(256), reward = Reward, type = resource_back, show_tips = 1},
            {ok, NewPS1} = lib_goods_api:send_reward_with_mail(NewPS, Produce, ?MOD_RESOURCE_BACK),
            {NewResourceBack, LeftTimes} = resource_back_do_core(ResourceBack, Id, ActSub, Times, YResAct, BYResAct),
            %% 日志
            lib_log_api:log_resource_back(RoleId, Id, ActSub, Cost, Times, LeftTimes, Reward),
            db_update_resource_back(RoleId, NewResourceBack),
            lib_server_send:send_to_sid(Sid, pt_419, 41903, [?SUCCESS, Id, ActSub, LeftTimes, RewardLv]),
            {true, NewPS1#player_status{resource_back = NewResourceBack}};
        {false, Res, NewPS} -> {false, Res, NewPS}
    end.

resource_back_do_core(ResourceBack, Id, ActSub, Times, YResAct, BYResAct) ->
    #resource_back{res_act_map = ResActMap} = ResourceBack,
    Yesterday = maps:get(?YESTERDAY, ResActMap, #{}),
    BYesterday = maps:get(?B_YESTERDAY, ResActMap, #{}),
    if
        is_record(BYResAct, res_act) == false ->
            NewLeftTime = YResAct#res_act.lefttimes - Times,
            NewState = ?IF(NewLeftTime == 0, ?STATE_FIND, ?STATE_NOT_FIND),
            NewYResAct = YResAct#res_act{lefttimes = NewLeftTime, state = NewState},
            NewYesterday = maps:put({Id, ActSub}, NewYResAct, Yesterday),
            NewResActMap = maps:put(?YESTERDAY, NewYesterday, ResActMap),
            {ResourceBack#resource_back{res_act_map = NewResActMap}, NewLeftTime};
        is_record(YResAct, res_act) == false ->
            NewLeftTime = BYResAct#res_act.lefttimes - Times,
            NewState = ?IF(NewLeftTime == 0, ?STATE_FIND, ?STATE_NOT_FIND),
            NewBYResAct = BYResAct#res_act{lefttimes = NewLeftTime, state = NewState},
            NewBYesterday = maps:put({Id, ActSub}, NewBYResAct, BYesterday),
            NewResActMap = maps:put(?B_YESTERDAY, NewBYesterday, ResActMap),
            {ResourceBack#resource_back{res_act_map = NewResActMap}, NewLeftTime};
        true ->
            LeftTimesY = YResAct#res_act.lefttimes,
            LeftTimesBY = BYResAct#res_act.lefttimes,
            {NewLeftTimesBY, NewLeftTimesY} = ?IF((LeftTimesBY - Times) >= 0, {LeftTimesBY - Times, LeftTimesY}, {0, LeftTimesY + LeftTimesBY - Times}),
            NewStateBY = ?IF(NewLeftTimesBY == 0, ?STATE_FIND, ?STATE_NOT_FIND),
            NewStateY = ?IF(NewLeftTimesY == 0, ?STATE_FIND, ?STATE_NOT_FIND),
            NewYResAct = YResAct#res_act{lefttimes = NewLeftTimesY, state = NewStateY},
            NewBYResAct = BYResAct#res_act{lefttimes = NewLeftTimesBY, state = NewStateBY},
            NewYesterday = maps:put({Id, ActSub}, NewYResAct, Yesterday),
            NewBYesterday = maps:put({Id, ActSub}, NewBYResAct, BYesterday),
            ResActMap1 = maps:put(?YESTERDAY, NewYesterday, ResActMap),
            NewResActMap = maps:put(?B_YESTERDAY, NewBYesterday, ResActMap1),
            {ResourceBack#resource_back{res_act_map = NewResActMap}, NewLeftTimesBY + NewLeftTimesY}
    end.

%%---------------------------------- 凌晨4点刷新 ---------------------------------
refresh(_DelaySec) ->
    util:rand_time_to_delay(1000, lib_resource_back, refresh, []).

refresh() ->
    RefreshList = ets:tab2list(?ETS_ONLINE),
    [refresh_res_act_status(OnlineRole#ets_online.id) || OnlineRole <- RefreshList].

refresh_res_act_status(PlayerId) ->
    lib_player:apply_cast(PlayerId, ?APPLY_CAST_SAVE, lib_resource_back, refresh_4_clock_res_act, []).

refresh_4_clock_res_act(PS) ->
    #player_status{id = RoleId, figure = #figure{lv = Lv}, resource_back = ResourceBack} = PS,
    case lib_module:is_open(Lv,?MOD_RESOURCE_BACK) of
        true ->
            ActIds = data_resource_back:get_act_ids(),
            NewResourceBack = update_res_act_4_clock(PS, ActIds, ResourceBack),
            db_update_resource_back(RoleId, NewResourceBack),
            NewPS = PS#player_status{resource_back = NewResourceBack},
            report_res_act(NewPS),
            {ok, NewPS};
        false -> {ok, PS}
    end.

update_res_act_4_clock(PS, ActIds, ResourceBack) ->
    %?PRINT("update 3 clock ResourceBack: ~p ~n", [ResourceBack]),
    ClearTime = get_today_clear_time(),
    F = fun(_K, V) -> V#res_act.state == ?STATE_NOT_FIND end,
    #resource_back{res_act_map = ResActMap} = ResourceBack,
    OldYesterday = maps:get(?YESTERDAY, ResActMap, #{}),
    BYesterday = maps:filter(F, OldYesterday),
    Today = maps:get(?TODAY, ResActMap, #{}),
    Today1 = correct_logout_act(PS, Today, ActIds, yesterday),
    NewYesterday = maps:filter(F, Today1),
    NewToday = init_today_res_act(PS, ActIds),
    ResActMap2 = maps:put(?TODAY, NewToday, #{}),
    ResActMap3 = maps:put(?YESTERDAY, NewYesterday, ResActMap2),
    ResActMap4 = maps:put(?B_YESTERDAY, BYesterday, ResActMap3),
    #resource_back{cleartime = ClearTime, res_act_map = ResActMap4}.

%% --------------------------------------- other ------------------------------------

init_today_res_act(PS, ActIds) ->
    %WeekDay = get_today_weekday(),
    FilterActIds = filter_unopen_act(PS, ActIds, today),
    %?PRINT("init_today_res_act FilterActIds ~p ~n", [ActIds]),
    F = fun({Id, ActSub}, Map) ->
        Limit = get_act_limit(Id, ActSub),
        case Limit == 0 of
            true -> Map;
            _ ->
                ResAct = #res_act{act_id = Id, act_sub = ActSub, lefttimes = Limit, max = Limit, state = ?STATE_NOT_FIND},
                maps:put({Id, ActSub}, ResAct, Map)
        end
        end,
    lists:foldl(F, #{}, FilterActIds).

init_res_act_yesterday(PS, ActIds) ->
    FilterActIds = filter_unopen_act(PS, ActIds, yesterday),
    F = fun({Id, ActSub}, Map) ->
        Limit = get_act_limit(Id, ActSub),
        case Limit == 0 of
            true -> Map;
            _ ->
                ResAct = #res_act{act_id = Id, act_sub = ActSub, lefttimes = Limit, max = Limit, state = ?STATE_NOT_FIND},
                maps:put({Id, ActSub}, ResAct, Map)
        end
        end,
    DataMap = lists:foldl(F, #{}, FilterActIds),
    filter_unfinsh_dungen(DataMap,PS).



init_res_act_b_yesterday(PS, ActIds) ->
    FilterActIds = filter_unopen_act(PS, ActIds, byesterday),
    F = fun({Id, ActSub}, Map) ->
        Limit = get_act_limit(Id, ActSub),
        case Limit == 0 of
            true -> Map;
            _ ->
                ResAct = #res_act{act_id = Id, act_sub = ActSub, lefttimes = Limit, max = Limit, state = ?STATE_NOT_FIND},
                maps:put({Id, ActSub}, ResAct, Map)
        end
        end,
    DataMap = lists:foldl(F, #{}, FilterActIds),
    filter_unfinsh_dungen(DataMap,PS).


%% 纠正奖励找回(由于下线没有补充数据)：
%% 例如：下线当天由20级升到30级，这个等级段新开了3个活动，然后下线，再次上线后，要将这个等级段的活动补充进record里面
correct_logout_act(PS, Today, ActIds, WeekDay) ->
    FilterActIds = filter_unopen_act(PS, ActIds, WeekDay),
    %%?PRINT("correct_logout_act FilterActIds ~p ~n", [{WeekDay, FilterActIds}]),
    F = fun({ActId, ActSub}, Map2) ->
        case maps:get({ActId, ActSub}, Map2, none) of
            #res_act{} -> Map2;
            _ ->
                Limit = get_act_limit(ActId, ActSub),
                case Limit == 0 of
                    true -> Map2;
                    _ ->
                        ResAct = #res_act{act_id = ActId, act_sub = ActSub, lefttimes = Limit, max = Limit, state = ?STATE_NOT_FIND},
                        maps:put({ActId, ActSub}, ResAct, Map2)
                end
        end
        end,
    NewToday = lists:foldl(F, Today, FilterActIds),
    case WeekDay of
        yesterday->
            filter_unfinsh_dungen(NewToday,PS);
        _->
            NewToday
    end.

filter_unfinsh_dungen(DataMap,Status)->
    #player_status{dungeon_record = DungeonRecord} = lib_dungeon:load_dungeon_record(Status),
    maps:fold(fun({ActId,AcSub},_RecAct,ArgsMap)->
        case ActId == ?MOD_DUNGEON of
            true->
                case data_dungeon:get_dun(AcSub) of
                     #dun_cfg{type=?DUNGEON_TYPE_MATERIAL}->
                        Record = maps:get(AcSub, DungeonRecord, []),
                        case lists:keyfind(?DUNGEON_REC_PASSTIME,1,Record) of
                            false->
                                maps:remove({ActId,AcSub},ArgsMap);
                            _->
                                ArgsMap
                        end;
                    _->
                        ArgsMap
                end;
            _->
                ArgsMap
        end
    end,DataMap,DataMap).


%% 过滤今天没开启或者等级不足的活动的活动
filter_unopen_act(PS, ActIds, WeekDay) ->
    #player_status{id = _RoleId, figure = #figure{lv = Lv, guild_id = GuildId}} = PS,
    Now = utime:unixtime(),
    DataTime = case WeekDay of
                   today ->
                       utime:unixtime_to_localtime(Now);
                   yesterday ->
                       utime:unixtime_to_localtime(Now - ?ONE_DAY_SECONDS);
                   byesterday ->
                       utime:unixtime_to_localtime(Now - 2 * ?ONE_DAY_SECONDS);
                   _ ->
                       utime:unixtime_to_localtime(Now)
               end,
    %?PRINT("filter_unopen_act DataTime ~p ~n", [ActIds]),
    F = fun({Id, AcSub}, List) ->
        case lib_activitycalen_api:check_ac_start(PS, Id, AcSub, DataTime) of
            true ->
                RewardLv = get_reward_lv(Id, AcSub, Lv),
                GuildLim = get_guild_limit(GuildId, Id, AcSub, Lv),
                case RewardLv =:= false orelse GuildLim =:= false of
                    true -> List;
                    false -> [{Id, AcSub} | List]
                end;
            false -> List
        end
        end,
    lists:foldl(F, [], ActIds).

%% 获取活动的参与次数
get_act_limit(ActId, ActSub) ->
    case data_activitycalen:get_live_config(ActId, ActSub) of
        #ac_liveness{res_max = Max} -> Max;
        _ ->
            Max = 0
    end,
    Max.

rectify_max_num(ActId, _ActSub, Max, Num) ->
    case ActId of
        %% ?MOD_HUNTING -> {1, Num};
        %% ?MOD_HERO_WAR -> {1, Num};
        _ -> {Max, Num}
    end.

%% 获取凌晨4点时间戳  4 点
%% 4-24点 获取的是第二天的时间戳
%% 0-4点  获取当天时间戳
get_today_clear_time() ->
    {_, {H, _, _}} = calendar:local_time(),
    NowTime = utime:unixtime(),
    ZeroTime = utime:unixdate(NowTime),
    FourClock = ZeroTime + 4 * 3600,
    case H < 4 of
        true -> FourClock;
        false -> FourClock + ?ONE_DAY_SECONDS
    end.


%% 资源找回状态report
report_res_act(PS) ->
    #player_status{id = _RoleId, sid = Sid, figure = #figure{lv = Lv}, resource_back = #resource_back{res_act_map = ResActMap}} = PS,
    Yesterday = maps:get(?YESTERDAY, ResActMap, #{}),
    BYesterday = maps:get(?B_YESTERDAY, ResActMap, #{}),
    F = fun(_K, V, List) ->
        #res_act{act_id = Id, act_sub = ActSub, lefttimes = LeftTimes, max = Limit} = V,
        case Limit == 0 of
            false ->
                case lists:keyfind({Id, ActSub}, 1, List) of
                    {{Id, ActSub}, OldLeftTimes} ->
                        lists:keystore({Id, ActSub}, 1, List, {{Id, ActSub}, OldLeftTimes + LeftTimes});
                    _ -> [{{Id, ActSub}, LeftTimes} | List]
                end;
            true -> List
        end
        end,
    List1 = maps:fold(F, [], Yesterday),
    List2 = maps:fold(F, List1, BYesterday),
    List3 = [{Id, ActSub, LeftTimes, 0, round(calc_reward_ratio(PS, Id, ActSub) * 100)}
        || {{Id, ActSub}, LeftTimes} <- List2, LeftTimes /= 0, get_reward_lv(Id, ActSub, Lv) =/= false],
    % ?PRINT("report_res_act ~p~n", [List3]),
    {ok, Bin} = pt_419:write(41900, [?SUCCESS, List3]),
    lib_server_send:send_to_sid(Sid, Bin).

make_res_act_map(DayType, _PS, _ActIds, ActList) when DayType == ?TODAY ->
    F = fun(H, Map) ->
        case H of
            {Id, ActSub, LeftTimes, State} ->
                Limit = get_act_limit(Id, ActSub),
                Act = #res_act{act_id = Id, act_sub = ActSub, lefttimes = LeftTimes, max = Limit, state = State},
                maps:put({Id, ActSub}, Act, Map);
            _ -> Map
        end
        end,
    lists:foldl(F, #{}, ActList);

make_res_act_map(_DayType, _PS, _ActIds, ActList) ->
    F = fun(H, Map) ->
        case H of
            {Id, ActSub, LeftTimes, State} ->
                Limit = get_act_limit(Id, ActSub),
                Act = #res_act{act_id = Id, act_sub = ActSub, lefttimes = LeftTimes, max = Limit, state = State},
                maps:put({Id, ActSub}, Act, Map);
            _ -> Map
        end
        end,
    lists:foldl(F, #{}, ActList).

%% -------------------------------- db函数 ---------------------------------
db_update_resource_back(RoleId, ResourceBack) ->
    #resource_back{cleartime = ClearTime, res_act_map = ResActMap} = ResourceBack,
    F = fun(DayType, V, List) ->
        F1 = fun(_K, ResAct, List1) ->
            #res_act{act_id = Id, act_sub = ActSub, lefttimes = LeftTimes, state = State} = ResAct,
            [{Id, ActSub, LeftTimes, State} | List1]
             end,
        DbActList = maps:fold(F1, [], V),
        [{DayType, DbActList} | List]
        end,
    ResActList = maps:fold(F, [], ResActMap),
    SqlValues = format_resource_back_values(RoleId, ClearTime, ResActList, [], 1),
    %?PRINT("update_resource_back ~p~n", [SqlValues]),
    Sql = ?SQL_BATCH_REPLACE_RES_BACK ++ SqlValues,
    db:execute(Sql).

db_select_res_act_list(RoleId) ->
    Sql = io_lib:format(?SQL_SELECT_RES_BACK, [RoleId]),
    db:get_all(Sql).


format_resource_back_values(_, _, [], List, _) -> List;
format_resource_back_values(RoleId, ClearTime, [H | ResActList], List, Num) ->
    {DayType, ActList} = H,
    ActList_B = util:term_to_bitstring(ActList),
    Value = io_lib:format(?SQL_BATCH_REPLACE_VALUES, [RoleId, ClearTime, DayType, ActList_B]),
    if
        Num =:= 1 ->
            NewList = Value ++ List;
        true ->
            NewList = Value ++ "," ++ List
    end,
    format_resource_back_values(RoleId, ClearTime, ResActList, NewList, Num + 1).

calc_cost(PS, Cost) ->
    #player_status{bgold = BGold} = PS,
    F = fun(Item, {Bind, List}) ->
        case Item of
            {?TYPE_COIN, _, _} -> {no_bind, [Item | List]};
            {?TYPE_GOLD, _, Num} ->
                case BGold >= Num of
                    true -> {bind, [{?TYPE_BGOLD, 0, Num} | List]};
                    false when BGold > 0 ->
                        {bind, [{?TYPE_BGOLD, 0, BGold}, {?TYPE_GOLD, 0, Num - BGold} | List]};
                    _ -> {no_bind, [{?TYPE_GOLD, 0, Num} | List]}
                end;
            _ -> {Bind, List}
        end
        end,
    lists:foldl(F, {no_bind, []}, Cost).



find_reward_by_bind(Reward, bind) ->
    F = fun(Item, List) ->
        case Item of
            {?TYPE_GOODS, Id, Num} ->
                [{?TYPE_BIND_GOODS, Id, Num} | List];
            _ -> [Item | List]
        end
        end,
    lists:foldl(F, [], Reward);
find_reward_by_bind(Reward, _) -> Reward.

get_guild_limit(GuildId, Module, ModuleSub, Lv) ->
    case get_cfg(Module, ModuleSub, Lv) of
        #base_res_act{guild_limit = GuildLim} ->
            case GuildLim of
                0 -> true;
                _ ->
                    case GuildId of
                        0 -> false;
                        _ -> true
                    end
            end;
        _ -> false
    end.


%% -------------------------------- 检查函数 ---------------------------------
check_resource_back(PS, Id, ActSub, TmpType, Times) ->
    #player_status{id = _RoleId, figure = #figure{lv = Lv}, resource_back = #resource_back{res_act_map = ResActMap}} = PS,
    OpenLv = lib_module:get_open_lv(?MOD_RESOURCE_BACK),
    if
        Lv < OpenLv -> {false, ?FAIL};
        TmpType == 1 orelse TmpType == 2 ->
            Type = ?IF(TmpType == 1, ?TYPE_BGOLD, ?TYPE_COIN),
            case get_cfg(PS,Id, ActSub, Lv) of
                #base_res_act{coin_per = CoinCost, coin_goods = CoinReward0,coin_exp=CExp, gold_per = GoldCost, gold_goods = GoldReward0,gold_exp=GExp}->
                    case check_res_act_times(ResActMap, Id, ActSub, Times) of
                        {true, YResAct, BYResAct} ->
                            %?PRINT("~p,~p~n", [YResAct, BYResAct]),
                            Extra = case Type of
                                ?TYPE_BGOLD->
                                    GExp;
                                _->
                                    CExp
                            end,
                            ExpValue0 = data_exp_base:get(Lv),
                            LvExtra = 10000,%data_award_exp:get_exp_extra(Id, ActSub,Lv),
                            ExpValue = round(ExpValue0*LvExtra/10000*Extra/10000),
                            CoinReward = ?IF(Extra>0,[{?TYPE_EXP,?GOODS_ID_EXP,ExpValue}|CoinReward0],CoinReward0),
                            GoldReward = ?IF(Extra>0,[{?TYPE_EXP,?GOODS_ID_EXP,ExpValue}|GoldReward0],GoldReward0),
                            case CoinCost of
                                [{Type, _, _}] ->
                                    % RealReward = [{RewardType, RewardTypeId, RewardNum * Times} || {RewardType, RewardTypeId, RewardNum} <- CoinReward],
                                    RealReward = calc_reward(PS, Id, ActSub, CoinReward, Times),
                                    RealCost = [{MoneyType, MoneyTypeId, MoneyNum * Times} || {MoneyType, MoneyTypeId, MoneyNum} <- CoinCost],
                                    case lib_goods_api:can_give_goods(PS, RealReward) of
                                        true -> {true, 0, RealReward, RealCost, YResAct, BYResAct};
                                        _ -> {false, ?ERRCODE(err150_cell_max)}
                                    end;
                                _ ->
                                    case GoldCost of
                                        [{Type, _, _}] ->
                                            % RealReward = [{RewardType, RewardTypeId, RewardNum * Times} || {RewardType, RewardTypeId, RewardNum} <- GoldReward],
                                            RealReward = calc_reward(PS, Id, ActSub, GoldReward, Times),
                                            RealCost = [{MoneyType, MoneyTypeId, MoneyNum * Times} || {MoneyType, MoneyTypeId, MoneyNum} <- GoldCost],
                                            case lib_goods_api:can_give_goods(PS, RealReward) of
                                                true -> {true, 0, RealReward, RealCost, YResAct, BYResAct};
                                                _ -> {false, ?ERRCODE(err150_cell_max)}
                                            end;
                                        _ -> {false, ?ERRCODE(err_config)}
                                    end
                            end;
                        {false, Res1} -> {false, Res1}
                    end;
                _ ->
                    case get_reward_lv(Id, ActSub, Lv) of
                        false -> {false, ?ERRCODE(err419_lv_not_match)};
                        _ -> {false, ?ERRCODE(err_config)}
                    end
            end;
        true -> {false, ?ERRCODE(err_config)}
    end.

%%计算额外比例
calc_reward(PS, ?MOD_DUNGEON, ?DUNGEON_TYPE_EXP, Reward, Times) ->
    Ratio = calc_reward_ratio(PS, ?MOD_DUNGEON, ?DUNGEON_TYPE_EXP),
    Fun = fun
        ({?TYPE_EXP, RewardTypeId, RewardNum})->
            {?TYPE_EXP, RewardTypeId, round(RewardNum * Ratio) * Times};
        ({RewardType, RewardTypeId, RewardNum}) ->
            {RewardType, RewardTypeId, RewardNum * Times};
        (Element) -> Element
    end,
    [Fun(Item) || Item <- Reward];
calc_reward(PS, ?MOD_CONVOY, 0, Reward, Times) ->
    %% 宠物生活技能，奖励加倍
    PetAddRatio = lib_pet_api:pet_life_skill_about_convoy_re_back(PS),
    RealReward = [{CType, CGoodTypeId, CNum*Times*(1+PetAddRatio)}||{CType, CGoodTypeId, CNum}<-Reward],
    RealReward;
calc_reward(_PS, _, _, Reward, Times) ->
    [{RewardType, RewardTypeId, RewardNum * Times} || {RewardType, RewardTypeId, RewardNum} <- Reward].

calc_reward_ratio(PS, ?MOD_DUNGEON, ?DUNGEON_TYPE_EXP) ->
    #player_status{
        figure = Figure, skill = SkillStatus, vip_status = _VipStatus,
        goods_buff_exp_ratio = BuffExpRatio
    } = PS,
    #figure{lv = Lv} = Figure,
    %% 世界等级加成
    ServerLv = util:get_world_lv(),
    ServerLvExpRatio = data_exp_add:get_add(ServerLv, Lv),
    %% 被动技能加成
    #status_skill{skill_attr = SkillAttr} = SkillStatus,
    case lists:keyfind(?EXP_ADD_RATIO, 1, SkillAttr) of
        false -> SkillExpRatio = 0;
        {_, SkillExpRatio} -> skip
    end,
    % VipExp = VipStatus#vip_status.exp_buff,
    Ratio = 1+SkillExpRatio/?RATIO_MAX + ServerLvExpRatio/100 + BuffExpRatio,
    Ratio;
calc_reward_ratio(_PS, _, _) -> 1.

check_res_act_times(ResActMap, Id, ActSub, Times) ->
    Yesterday = maps:get(?YESTERDAY, ResActMap, #{}),
    BYesterday = maps:get(?B_YESTERDAY, ResActMap, #{}),
    YResAct = maps:get({Id, ActSub}, Yesterday, none),
    BYResAct = maps:get({Id, ActSub}, BYesterday, none),
    if
        is_record(BYResAct, res_act), is_record(YResAct, res_act) ->
            LeftTimes1 = YResAct#res_act.lefttimes,
            LeftTimes2 = BYResAct#res_act.lefttimes,
            case check_times_valid(LeftTimes1 + LeftTimes2, Times) of
                true -> {true, YResAct, BYResAct};
                Res -> Res
            end;
        is_record(BYResAct, res_act) ->
            LeftTimes1 = BYResAct#res_act.lefttimes,
            case check_times_valid(LeftTimes1, Times) of
                true -> {true, none, BYResAct};
                Res -> Res
            end;
        is_record(YResAct, res_act) ->
            LeftTimes1 = YResAct#res_act.lefttimes,
            case check_times_valid(LeftTimes1, Times) of
                true -> {true, YResAct, none};
                Res -> Res
            end;
        true -> {false, ?ERRCODE(err419_no_res_act)}
    end.

check_times_valid(LeftTimes, Times) ->
    case LeftTimes of
        0 -> {false, ?ERRCODE(err419_no_res_act)};
        Num when Times > Num -> {false, ?ERRCODE(err419_no_more_times)};
        _ -> true
    end.

get_reward_lv(Id, ActSub, Lv) ->
    case get_cfg(Id, ActSub, Lv) of
        #base_res_act{} -> true;
        _ -> false
    end.

%%宠物副本找回 积分
get_cfg(Status,?MOD_DUNGEON,?DUNGEON_TYPE_PET,Lv) ->
    #player_status{dungeon_record = Rec,role_dun_pet = RoleDunPet} = Status,
    #role_dun_pet{reward_choose = RewardChooseMap} = RoleDunPet,
    {MaxDunID,Datas} = filter_dun(Rec,?DUNGEON_TYPE_PET),
    case lists:keyfind(?DUNGEON_REC_MAX_SCORE,1,Datas) of
        {_,Score}->
            skip;
        _->
            Score = 0
    end,
    Index = maps:get(MaxDunID, RewardChooseMap,1),
    AwardsList = data_dungeon_pet:get_dun_pet_reward_cfg(MaxDunID,Score),
    case lists:keyfind(Index, 1, AwardsList) of
        {_,Awards}->
            skip;
        _->
            Awards = []
    end,
    OCfg   =  get_cfg(?MOD_DUNGEON,?DUNGEON_TYPE_PET,Lv),
    case is_record(OCfg,base_res_act) of
        true->
            OCfg#base_res_act{coin_goods=Awards,gold_goods=Awards};
        _->
            OCfg
    end;
%%幻光副本找回 扫荡
get_cfg(Status,?MOD_DUNGEON,?DUNGEON_TYPE_VISUS,Lv) ->
    #player_status{dungeon_record = Rec} = Status,
    SweepWaves = merge_waves(Rec,?DUNGEON_TYPE_VISUS),
    Awards =  lib_dungeon_visus:calc_visus_dun_sweep(SweepWaves),
    OCfg   =  get_cfg(?MOD_DUNGEON,?DUNGEON_TYPE_VISUS,Lv),
    case is_record(OCfg,base_res_act) of
        true->
            OCfg#base_res_act{coin_goods=Awards, gold_goods=Awards};
        _->
            OCfg
    end;
%无尽回廊找回 每日
get_cfg(Status,?MOD_DUNGEON,?DUNGEON_TYPE_TOWER,Lv) ->
    #player_status{dungeon_record = Rec} = Status,
    DunIds = sum_dun_list(Rec,?DUNGEON_TYPE_TOWER),
    Awards =  lib_dungeon_tower:calc_sweep_reward(DunIds),
    OCfg   =  get_cfg(?MOD_DUNGEON,?DUNGEON_TYPE_TOWER,Lv),
    case is_record(OCfg,base_res_act) of
        true->
            OCfg#base_res_act{coin_goods=Awards,gold_goods=Awards};
        _->
            OCfg
    end;
%守卫信标
get_cfg(Status,?MOD_DUN_MULTI,2,Lv)->
    #player_status{dungeon_record = Rec} = Status,
    {MaxDunID,_Datas} = filter_dun(Rec,?DUNGEON_TYPE_GUARD),
    % case lists:keyfind(?DUNGEON_REC_MAX_SCORE,1,Datas) of
    %     {_,Score}->
    %         skip;
    %     _->
    %         Score = 0
    % end,

    % case data_dungeon_wave:get_dun_wave(MaxDunID,Score) of
    %     #base_dun_wave_cfg{normal_reward=Awards}->
    %         skip;
    %     _->
    %        Awards = []
    % end,

    case data_dungeon:get_dun(MaxDunID) of
        #dun_cfg{base_reward=Awards}->
            skip;
        _->
            Awards = []
    end,

    OCfg   =  get_cfg(?MOD_DUN_MULTI,2,Lv),
    case is_record(OCfg,base_res_act) of
        true->
            OCfg#base_res_act{coin_goods=Awards,gold_goods=Awards};
        _->
            OCfg
    end;
get_cfg(_,Id,AcSub,Lv)->
    get_cfg(Id,AcSub, Lv).
get_cfg(Id, ActSub, Lv) ->
    case data_resource_back:get_type(Id, ActSub) of
        1 -> data_resource_back:get_res_cfg_by_lv(Id, ActSub, Lv);
        _ -> data_resource_back:get_res_cfg_by_server_lv(Id, ActSub, util:get_world_lv())
    end.

filter_dun(Rec,DunType)->
    Ids = data_dungeon:get_ids_by_type(DunType),
    All = maps:to_list(Rec),
    filter_dun(All,Ids,0,[]).
filter_dun([],_,DunId,Datas)->
    {DunId,Datas};
filter_dun([{H,HDatas}|T],Ids,DunId,Datas)->
    case lists:member(H,Ids) of
        true when H>DunId->
            filter_dun(T,Ids,H,HDatas);
        _->
            filter_dun(T,Ids,DunId,Datas)
    end.

merge_waves(Rec,DunType)->
    Ids = data_dungeon:get_ids_by_type(DunType),
    All = maps:to_list(Rec),
    merge_waves(All,Ids,[]).
merge_waves([],_,SweepWaves)->
    SweepWaves;
merge_waves([{H,HDatas}|T],Ids,SweepWaves)->
    case lists:member(H,Ids) of
        true ->
            Waves =  case lists:keyfind(?DUNGEON_REC_MAX_SCORE,1,HDatas) of
                {_,Wave}->
                    lists:seq(1,Wave);
                _->
                    []
            end,
            merge_waves(T,Ids,[{H,Waves}|SweepWaves]);
        _->
            merge_waves(T,Ids,SweepWaves)
    end.


sum_dun_list(Rec,DunType)->
     Ids = data_dungeon:get_ids_by_type(DunType),
     All = maps:to_list(Rec),
     sum_dun_list(All,Ids,[]).
sum_dun_list([],_,DunIds)->
    DunIds;
sum_dun_list([{H,_}|T],Ids,DunIds)->
    case lists:member(H,Ids) of
        true ->
            sum_dun_list(T,Ids,[H|DunIds]);
        _->
            sum_dun_list(T,Ids,DunIds)
    end.
