%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     个人差异化定制活动，总的活动开启控制是走定制活动统一管理；这里的活动每个玩家的触发时间点不一样，
%%%     所以开启时间不同，结束时间也不同。这些活动判断活动是否开启，应当以这里为主
%%%     在#status_custom_act.custom_act_personal单独保存了一份活动时间
%%% @end
%%%-------------------------------------------------------------------
-module(custom_act_personal).
-author("liushl").

-include("custom_act.hrl").
-include("server.hrl").
-include("figure.hrl").
-include("predefine.hrl").
-include("rec_event.hrl").
-include("def_event.hrl").
-include("common.hrl").
-include("errcode.hrl").
-include("role_foster.hrl").
-include("rec_recharge.hrl").
-include("def_daily.hrl").
-include("def_module.hrl").
-include("child.hrl").
-include("task.hrl").
-include("dungeon.hrl").
-include("warsoul.hrl").
-include("def_fun.hrl").
%% API
-export([
    is_personal_act/1,                          %% 是否个人差异化定制活动
    login/1,
    %% 触发礼包
    handle_event/2,
    event_child_group_up/2,                     %% 宝宝培养

    notify_other_module/2,                      %% 定制活动开启或者关闭
    ask_reward_status/3,                        %% 奖励领取状态
    check_personal_act/1,                       %% 检查初始化角色差异化活动
    check_personal_act/3,                       %% 检查初始化角色差异化活动
    check_personal_act_type/2,                  %% 检查初始化角色差异化活动
    check_personal_act_type/4,                  %% 检查初始化角色差异化活动
    arrange_personal_act/2,                     %% 定制活动中的角色差异化活动在这里判断是否开启
    exclude_personal_act/1,                     %% 排除角色差异化活动
    get_custom_act_open_list_personal/1,        %% 获得开启中的角色差异化活动列表
    is_personal_act_open/3,                     %% 判断活动是否开启
    get_custom_act_personal_open_info/3,        %% 获取活动开启信息
    clear_personal_act_in_role/3                %% 清理玩家身上的活动数据
]).

-export([
    send_act_open_notice/2,
    init_personal_act_data/2,                        %% 初始化角色差异化活动
    check_condition/4,                          %% 活动开启条件判断
    save_personal_act/2,                        %% 保存活动数据
    save_personal_act_db/2                      %% 保存活动数据
]).

%% ---------------------------------------------------------------------------
%% @doc 角色差异化的活动列表-这些活动可能由某些角色属性条件触发
%% 比如：升级/VIP/某些系统开放/某些系统达到条件
personal_act_list() ->
    [
        ?CUSTOM_ACT_TYPE_LIMIT_GIFT             %% 限购礼包活动
        , ?CUSTOM_ACT_TYPE_FREE_GIFT            %% 0元礼包活动
        , ?CUSTOM_ACT_TYPE_SELLSWORDS_RESUPPLY  %% 佣兵补给
        , ?CUSTOM_ACT_TYPE_RECHARGE             %% 累充活动
        , ?CUSTOM_ACT_THEME_FIRST_RECHARGE      %% 主题活动-每日首充
        , ?CUSTOM_ACT_TYPE_INVESTMENT
    ].

%% 排除佣兵补给
personal_act_list2() ->
    [
        ?CUSTOM_ACT_TYPE_LIMIT_GIFT             %% 限购礼包活动
        , ?CUSTOM_ACT_TYPE_FREE_GIFT            %% 0元礼包活动
        , ?CUSTOM_ACT_TYPE_RECHARGE             %% 累充活动
        , ?CUSTOM_ACT_THEME_FIRST_RECHARGE      %% 主题活动-每日首充
        , ?CUSTOM_ACT_TYPE_INVESTMENT
    ].

%% 是否个人差异化定制活动
is_personal_act(Type) ->
    lists:member(Type, personal_act_list()).


%% ---------------------------------------------------------------------------
%% @doc 活动结束了，但还有奖励还没有领取也需要展示出来的活动列表。
-spec show_award_act_list() -> ActInfoList when
    ActInfoList :: [#act_info{}].
%% ---------------------------------------------------------------------------
show_award_act_list() ->
    TypeList = [?CUSTOM_ACT_TYPE_FREE_GIFT,?CUSTOM_ACT_TYPE_INVESTMENT],
    show_award_act_list(TypeList, []).

show_award_act_list([], ActInfoList) ->
    ActInfoList;
show_award_act_list([Type | T], ActInfoList) ->
    Fun = fun(SubType, ActList) ->
        ActInfo = #act_info{key = {Type, SubType}},
        [ActInfo | ActList]
          end,
    SubTypeList = lib_custom_act_util:get_subtype_list(Type),
    NewActInfoList = lists:foldl(Fun, ActInfoList, SubTypeList),
    show_award_act_list(T, NewActInfoList).

%% ---------------------------------------------------------------------------
%% @doc 登陆初始化活动数据
login(RoleId) ->
    List = db:get_all(?select_opening_custom_act_personal, [RoleId]),
    [
        begin
            ActInfo = #custom_act_personal{
                key = {Type, SubType},  %% {活动主类型, 活动次类型}
                s_time = STime,       %% 活动开始时间
                e_time = ETime,       %% 活动结束时间
                time = Time,       %% 活动数据最后更新时间
                act_data = util:bitstring_to_term(ActData),
                red_gift = util:bitstring_to_term(RedGift)
            },
            ActInfo
        end
        || [Type, SubType, STime, ETime, Time, ActData, RedGift] <- List
    ].

%% 角色升级事件
handle_event(Player, #event_callback{type_id = ?EVENT_LV_UP}) ->
    NewPlayer = check_personal_act(Player),
    {ok, NewPlayer};

%% 登录事件延后
handle_event(Player, #event_callback{type_id = ?EVENT_LOGIN_CAST}) ->
    NewPlayer = check_personal_act(Player),
    {ok, NewPlayer};

%% vip等级提升
handle_event(Player, #event_callback{type_id = ?EVENT_VIP}) ->
    NewPlayer = check_personal_act(Player),
    {ok, NewPlayer};

%% 活跃度增加事件
handle_event(Player, #event_callback{type_id = ?EVENT_ADD_LIVENESS}) ->
    NewPlayer = check_personal_act2(Player),
    {ok, NewPlayer};

%% 进阶系统进阶事件
handle_event(Player, #event_callback{type_id = ?EVENT_FOSTER_GRADE_UP}) ->
    NewPlayer = check_personal_act2(Player),
    {ok, NewPlayer};

%% 充值事件
handle_event(Player, #event_callback{type_id = ?EVENT_RECHARGE}) ->
    PersonalTypeList = personal_act_list2(),
    NewPlayer = check_personal_act(Player, PersonalTypeList, []),
    %% 普通的通用状态处理活动，全量刷新
    ActTypeList = [
        ?CUSTOM_ACT_TYPE_RECHARGE,
        ?CUSTOM_ACT_TYPE_LIMIT_GIFT,
        ?CUSTOM_ACT_THEME_FIRST_RECHARGE
    ],
    ActInfoL = get_custom_act_open_list_personal(Player, ActTypeList),
    NowTime = utime:unixtime(),
    F = fun(ActInfo) ->
        case ActInfo of
            #act_info{etime = Etime} = ActInfo when NowTime < Etime ->
                lib_custom_act:send_reward_status(NewPlayer, ActInfo);
            _ -> skip
        end
        end,
    lists:foreach(F, ActInfoL),
    {ok, NewPlayer};

%% 0点事件
handle_event(Player, #event_callback{type_id = ?EVENT_ZERO_CLOCK}) ->
    NewPlayer = check_personal_act2(Player),
    {ok, NewPlayer};

%% 完成任务 这个模块我记得改了很多个版本了
handle_event(Player, #event_callback{type_id = ?EVENT_FINISH_TASK, data = TaskId}) ->
    case lib_task:get_data(TaskId) of
        #task{type = Type}
            when Type =/= ?TASK_TYPE_GUILD andalso Type =/= ?TASK_TYPE_DAILY ->
            NewPlayer = check_personal_act2(Player);
        _ ->
            NewPlayer = Player
    end,
    {ok, NewPlayer};

%% 通关副本
handle_event(Player, #event_callback{type_id = ?EVENT_DUNGEON_SUCCESS, data = #callback_dungeon_succ{dun_id = DunId, dun_type = DunType, dun_wave = DunWare}}) ->
    if
        DunType == ?DUNGEON_TYPE_GUARD ->
            %% 达到最大波数触发
            case data_dungeon_wave:get_next_wave(DunId, DunWare) of
                [0] ->
                    NewPlayer = check_personal_act(Player, [?CUSTOM_ACT_TYPE_SELLSWORDS_RESUPPLY], [{dun_id, DunId}]);
                _ ->
                    NewPlayer = Player
            end;
        true ->
            NewPlayer = Player
    end,
    {ok, NewPlayer};

handle_event(Player, _) ->
    {ok, Player}.

%% 宝宝培养
event_child_group_up(PlayerNew, PlayerOld) ->
    case {PlayerNew#player_status.status_child, PlayerOld#player_status.status_child} of
        {#status_child{child_age_year = YearNew}, #status_child{child_age_year = YearOld}}
            when YearNew > YearOld ->
            NewPlayer = check_personal_act2(PlayerNew),
            NewPlayer;
        _ ->
            PlayerNew
    end.

%% ---------------------------------------------------------------------------
%% @doc 活动数据变化，判断是否需要更新活动时间


%% ---------------------------------------------------------------------------
%% @doc 活动派发接口：定制活动开启或者关闭 默认处理
notify_other_module(?CUSTOM_ACT_STATUS_OPEN, #act_info{key={?CUSTOM_ACT_TYPE_INVESTMENT,_SubType}}=ActInfo) ->
    lib_custom_act_investment:notify_other_module(?CUSTOM_ACT_STATUS_OPEN,ActInfo),
    lib_player:cast_online_role_apply(?APPLY_CAST_STATUS, ?MODULE, check_personal_act_type, [[ActInfo]]);
notify_other_module(?CUSTOM_ACT_STATUS_OPEN, #act_info{key={?CUSTOM_ACT_TYPE_FREE_GIFT,_SubType}}=ActInfo) ->
    zero_gift:notify_other_module(ActInfo),
    lib_player:cast_online_role_apply(?APPLY_CAST_STATUS, ?MODULE, check_personal_act_type, [[ActInfo]]);
notify_other_module(?CUSTOM_ACT_STATUS_OPEN, ActInfo) ->
    lib_player:cast_online_role_apply(?APPLY_CAST_STATUS, ?MODULE, check_personal_act_type, [[ActInfo]]);
notify_other_module(ActStatus, #act_info{key = {?CUSTOM_ACT_TYPE_INVESTMENT, _SubType}} = ActInfo)
    when ActStatus =:= ?CUSTOM_ACT_STATUS_CLOSE
    orelse ActStatus =:= ?CUSTOM_ACT_STATUS_MANUAL_CLOSE ->
    lib_custom_act_investment:notify_other_module(ActStatus,ActInfo);
notify_other_module(ActStatus, #act_info{key = {ActType, SubType}} = ActInfo)
    when ActStatus =:= ?CUSTOM_ACT_STATUS_CLOSE
    orelse ActStatus =:= ?CUSTOM_ACT_STATUS_MANUAL_CLOSE ->
    lib_custom_act:broadcast_act_info(ActStatus, [ActInfo]),
    send_forgot_rewards(ActType, SubType),
    case lib_custom_act_util:get_act_cfg_info(ActType, SubType) of
        #custom_act_cfg{clear_type = ClearType} ->
            %% 因为充值类活动强制不清理领奖数据，防止配置错误，所以这里强制清理活动时间
            case ClearType =:= ?CUSTOM_ACT_CLEAR_CLOSE
                orelse ActType =:= ?CUSTOM_ACT_TYPE_RECHARGE
                orelse ActType =:= ?CUSTOM_ACT_THEME_FIRST_RECHARGE
            of
                true  ->
                    %% 活动结束清理数据的才需要
                    db:execute(?delete_opening_custom_act_personal, [ActType, SubType]),
                    %% 玩家身上清除活动数据
                    lib_player:cast_online_role_apply(
                        ?APPLY_CAST_STATUS, ?MODULE, clear_personal_act_in_role, [ActType, SubType]
                    );
                false ->
                    ok
            end;
        _ ->
            ok
    end;
notify_other_module(_, _) -> ok.

%% ---------------------------------------------------------------------------
%% @doc 活动派发接口：奖励领取状态
ask_reward_status(Player, Type, SubType) ->
    case is_personal_act_open(Player, Type, SubType) of
        {true, ActInfo} ->
            lib_custom_act:send_reward_status(Player, ActInfo);
        false ->
            lib_custom_act:send_error_code(Player#player_status.id, ?ERRCODE(err331_act_closed))
    end.

%% ---------------------------------------------------------------------------
%% @doc 初始化角色差异化活动
%% (角色升级|登录|活动开始|vip等级提升|需要触发)
check_personal_act(Player) ->
    PersonalTypeList = personal_act_list(),
    check_personal_act(Player, PersonalTypeList, []).
%% 排除佣兵补给
check_personal_act2(Player) ->
    PersonalTypeList = personal_act_list2(),
    check_personal_act(Player, PersonalTypeList, []).

%% 根据活动类型获得当前正在开启的子类型
check_personal_act(Player, [], _Data) ->
    Player;
check_personal_act(Player, [Type | T], Data) ->
    #player_status{
        status_custom_act = StatusCustomAct
    } = Player,
    #status_custom_act{
        extra_data = ExtraData
    } = StatusCustomAct,
    if
        Type == ?CUSTOM_ACT_TYPE_SELLSWORDS_RESUPPLY ->
            OpenNum = custom_act_sellswords_resupply:calc_open_count(Player),
            if
                OpenNum >= 4 ->
                    NewData = Data,
                    OpenActList=[];
                true ->
                    NewData = [{open_num, OpenNum}|Data],
                    OpenActList = lib_custom_act_util:get_open_subtype_list(Type)
            end;
        true ->
            NewData = Data,
            OpenActList = lib_custom_act_util:get_open_subtype_list(Type)
    end,
    NewPlayer = check_personal_act_type(Player, OpenActList, NewData, ExtraData),
    check_personal_act(NewPlayer, T, Data).

check_personal_act_type(Player, OpenActList) ->
    #player_status{
        status_custom_act = StatusCustomAct
    } = Player,
    #status_custom_act{
        extra_data = ExtraData
    } = StatusCustomAct,
    Length = length([true||#act_info{key = {ActType, _}}<-OpenActList,ActType == ?CUSTOM_ACT_TYPE_SELLSWORDS_RESUPPLY]),
    if
        Length > 0 ->
            OpenNum = custom_act_sellswords_resupply:calc_open_count(Player),
            if
                OpenNum >= 4 ->
                    Data = [];
                true ->
                    Data = [{open_num, OpenNum}]
            end;
        true ->
            Data = []
    end,
    NewPlayer = check_personal_act_type(Player, OpenActList, Data, ExtraData),
    NewPlayer.

check_personal_act_type(Player, [], _Data, ExtraData) ->
    #player_status{status_custom_act = StatusCustomAct} = Player,
    NStatusCustomAct = StatusCustomAct#status_custom_act{extra_data = ExtraData},
    NewPlayer = Player#player_status{status_custom_act = NStatusCustomAct},
    NewPlayer;
check_personal_act_type(Player, [ActInfo | T], Data, ExtraData) ->
    #act_info{key = {Type, SubType}} = ActInfo,
    if
        Type =:= ?CUSTOM_ACT_TYPE_SELLSWORDS_RESUPPLY ->
            {_,GiftList} = lists:keyfind(?ALREADY_TRIGGER_GIFT, 1, ExtraData),
            case lib_custom_act_util:get_act_cfg_info(Type, SubType) of
                #custom_act_cfg{condition = Cond} ->
                    skip;
                _ ->
                    Cond = []
            end,
            case lists:keyfind(gift, 1, Cond) of
                {gift, Gift} ->
                    skip;
                _ ->
                    Gift = 0
            end,
            case lists:keyfind(open_num, 1, Data) of
                {_, OpenNum} ->
                    skip;
                _ ->
                    OpenNum = 4
            end,
            IsMax = OpenNum >= 4,
            IsOpen = lists:member(Gift, GiftList),
            {NPlayer, NData, NExtraData} =
                if
                    IsMax orelse IsOpen  ->
                        {Player, Data, ExtraData};
                    true ->
                        {Open, Player1} = check_personal_act_do(Player, ActInfo, Data, ExtraData),
                        if
                            Open ->
                                NGiftList = ?IF(Gift == 0,GiftList,[Gift|GiftList]),
                                ExtraData1 = lists:keystore(?ALREADY_TRIGGER_GIFT, 1, ExtraData, {?ALREADY_TRIGGER_GIFT, NGiftList}),
                                Sql = io_lib:format(?update_custom_act_personal_data, [Player#player_status.id, util:term_to_bitstring(ExtraData1)]),
                                db:execute(Sql),
                                Data1 = lists:keystore(open_num, 1, Data , {open_num, OpenNum+1}),
                                {Player1, Data1, ExtraData1};
                            true ->
                                {Player1, Data, ExtraData}
                        end
                end,
            check_personal_act_type(NPlayer, T, NData, NExtraData);
        true ->
            {_, NPlayer} = check_personal_act_do(Player, ActInfo, Data, []),
            check_personal_act_type(NPlayer, T, Data, ExtraData)
    end.

check_personal_act_do(Player0, #act_info{key = {Type, SubType}, etime = EndTime} = ActInfo, Data, ExtraData) ->
    Player1 = lib_custom_act_investment:check_clean_invest(Player0,ActInfo),
    Player = zero_gift:check_clean(Player1,ActInfo),
    case get_custom_act_personal_open_info(Player, Type, SubType) of
        #custom_act_personal{e_time = TheTime} = PersonalActInfo when EndTime < TheTime ->
            %% 一般是活动配置发生变化，需要提前关闭，则更新活动时间
            NewPlayer = update_personal_act(Player, PersonalActInfo#custom_act_personal{e_time = EndTime}),
            {false, NewPlayer};
        #custom_act_personal{} ->
            %% 活动已经触发了，不再重复触发
            {false, Player};
        _ ->
            init_personal_act(Player, ActInfo, Data, ExtraData)
    end.

%% 活动更新
update_personal_act(Player, PersonalActInfo) ->
    #custom_act_personal{key = {ActType, SubType}} = PersonalActInfo,
    %% 配置了last_days的需要保存时间
    Save = is_act_save(ActType, SubType),
    Save andalso custom_act_personal:save_personal_act_db(Player#player_status.id, PersonalActInfo),
    Player1 = custom_act_personal:save_personal_act(Player, PersonalActInfo),
    custom_act_personal:send_act_open_notice(Player, PersonalActInfo),
    Player1.

%% 角色差异化活动初始化
init_personal_act(Player, ActInfo, Data, ExtraData) ->
    #act_info{key = {ActType, SubType}} = ActInfo,
    ActCfg = lib_custom_act_util:get_act_cfg_info(ActType, SubType),
    case ActCfg of
        #custom_act_cfg{condition = Condition} ->
            case check_condition(Player, Condition, Data, ExtraData) of
                true ->
                    case lists:keyfind(last_days, 1, Condition) of
                        {_, Days} ->
                            %% 持续N天
                            StartTime = utime:unixtime(),
                            Date = erlang:date(),
                            ThatDate = utime:get_before_day(Date, Days, 1),
                            ThatTime = utime:unixtime({ThatDate, {0, 0, 0}}),
                            EndTime = min(ThatTime, ActInfo#act_info.etime);
                        _ ->
                            case lists:keyfind(last_seconds, 1, Condition) of
                                {_, Seconds} ->
                                    %% 持续数个小时
                                    StartTime = utime:unixtime(),
                                    EndTime = StartTime + Seconds;
                                _ ->
                                    IsForce = is_force_set_start_time_act(ActType),
                                    if
                                        IsForce ->
                                            %% 一些涉及到充值的活动，写死活动开始时间就是触发时间
                                            StartTime = utime:unixtime(),
                                            EndTime = ActInfo#act_info.etime;
                                        true ->
                                            StartTime = ActInfo#act_info.stime,
                                            EndTime = ActInfo#act_info.etime
                                    end
                            end
                    end,
                    Key = {ActType, SubType},
                    PersonalActInfo = #custom_act_personal{
                        key = Key,               %% {活动主类型, 活动次类型}
                        s_time = StartTime,      %% 活动开始时间
                        e_time = EndTime,        %% 活动结束时间
                        act_data = lib_custom_act_dispatcher:init_personal_act_data(Player, ActInfo)
                    },
                    Save = is_act_save(ActType, SubType),
                    Save andalso custom_act_personal:save_personal_act_db(Player#player_status.id, PersonalActInfo),
                    Player1 = custom_act_personal:save_personal_act(Player, PersonalActInfo),
                    custom_act_personal:send_act_open_notice(Player, PersonalActInfo),
                    {true, Player1};
                _ ->
                    {false, Player}
            end;
        _ ->
            {false, Player}
    end.

init_personal_act_data(_Player, _ActInfo) -> [].

%% 强制设置开始时间的活动，不管配置是什么样子的。一般是涉及到充值的活动，防止充值数据回溯到活动触发之前
is_force_set_start_time_act(ActType) ->
    List = [
        ?CUSTOM_ACT_TYPE_RECHARGE,
        ?CUSTOM_ACT_TYPE_LIMIT_GIFT,
        ?CUSTOM_ACT_THEME_FIRST_RECHARGE
    ],
    lists:member(ActType, List).

%% 发送活动开启信息
send_act_open_notice(Player,
        #custom_act_personal{key = Key,
            s_time = Stime,
            e_time = Etime}
) ->
    #player_status{sid = Sid} = Player,
    ActInfo = #act_info{
        key = Key,
        stime = Stime,
        etime = Etime,
        trigger_time = 1
    },
    PackList = lib_custom_act:pack_custom_act_list([ActInfo]),
    {ok, BinData} = pt_331:write(33102, [PackList]),
    lib_server_send:send_to_sid(Sid, BinData).

is_act_save(Type, SubType) ->
    case is_force_set_start_time_act(Type) of
        true  -> true;
        false ->
            Cfg = lib_custom_act_util:get_act_cfg_info(Type, SubType),
            Condition = Cfg#custom_act_cfg.condition,
            lists:keymember(last_days, 1, Condition) orelse lists:keymember(last_seconds, 1, Condition)
    end.
%% ---------------------------------------------------------------------------
%% @doc 获取开启中的活动列表
%% ---------------------------------------------------------------------------
%% 定制活动中的角色差异化活动在这里判断是否开启
arrange_personal_act(Player, ActList) ->
    ActList1 = exclude_personal_act(ActList),
    PersonalActList = get_custom_act_open_list_personal(Player),
    PersonalActList ++ ActList1.

%% 排除角色差异化活动
exclude_personal_act(ActList) ->
    lists:foldl(
        fun(#act_info{key = {Type, _}} = ActInfo, Temp) ->
            case is_personal_act(Type) of
                true ->
                    Temp;
                false ->
                    [ActInfo | Temp]
            end
        end, [], ActList
    ).

%% 获得开启中的角色差异化活动
get_custom_act_open_list_personal(Player, ActTypes) ->
    ActList = get_custom_act_open_list_personal(Player),
    [
        One || One = #act_info{key = {Type, _}} <- ActList, lists:member(Type, ActTypes)
    ].
get_custom_act_open_list_personal(Player) ->
    #player_status{
        status_custom_act = #status_custom_act{custom_act_personal = PersonalAct}
    } = Player,
    NowTime = utime:unixtime(),
    NewActList = lists:foldl(
        fun(#custom_act_personal{
            key = Key,  %% {活动主类型, 活动次类型}
            s_time = STime,       %% 活动开始时间
            e_time = ETime       %% 活动结束时间
        }, Temp) ->
            case ETime > NowTime orelse ETime =:= 1 of
                true ->
                    [#act_info{
                        key = Key,                       %% {type, subtype}
                        stime = STime,
                        etime = ETime
                    } | Temp];
                false ->
                    Temp
            end
        end, [], PersonalAct
    ),
    %% 还有一些活动是不在开启中了，但还有奖励还没有领取也需要展示出来。。
    NewActList2 = append_show_act_list(Player, NewActList),
    NewActList2.


%% ---------------------------------------------------------------------------
%% @doc 活动结束了但还有奖励还没有领取需要展示出来的活动列表
-spec append_show_act_list(Player, ActList) -> NewActList when
    Player :: #player_status{},
    ActList :: [#act_info{}],   %% 正在开启中的活动列表
    NewActList :: [#act_info{}].
%% ---------------------------------------------------------------------------
append_show_act_list(Player, ActList) ->
    MayNeedShowList = show_award_act_list(),
    append_show_act_list(Player, ActList, MayNeedShowList).

append_show_act_list(_Player, ActList, []) ->
    ActList;
append_show_act_list(Player, ActList, [#act_info{key = Key} | T]) ->
    case lists:keymember(Key, #act_info.key, ActList) of
        true -> %% 已在列表内
            append_show_act_list(Player, ActList, T);
        false ->
            {Type, SubType} = Key,
            %% 判断是否需要追加
            NewActList = case is_personal_act_open(Player, Type, SubType) of
                             {true, Act2} -> %% 需要追加
                                 [Act2 | ActList];
                             false ->
                                 ActList
                         end,
            append_show_act_list(Player, NewActList, T)
    end.

%% ---------------------------------------------------------------------------
%% @doc 判断活动是否开启
%% 0元礼包活动.这个接口只能用来判断是否展示图标。不能用来判断是否可以购买
is_personal_act_open(Player, Type = ?CUSTOM_ACT_TYPE_FREE_GIFT, SubType) ->
    case get_custom_act_personal_open_info(Player, Type, SubType) of
        #custom_act_personal{s_time = STime, e_time = ETime, act_data = AwardList} ->
            Now = utime:unixtime(),
            Fun = fun(AwardInfo) ->
                #custom_free_gift_award{is_get = IsGet} = AwardInfo,
                case IsGet of
                    ?ACT_REWARD_HAS_GET ->
                        0;
                    _ -> %% 购买了没有领取的话需要一直展示
                        true
                end
                  end,
            EndTimeList = [Fun(AwardInfo) || AwardInfo <- AwardList],
            RewardList = lib_custom_act_util:get_act_reward_grade_list(Type, SubType),
            %% 是否全部都购买了
            IsAllBuy = length(EndTimeList) >= length(RewardList),
            EndTime = case EndTimeList of
                          List when is_list(List) andalso IsAllBuy andalso List =/= [] ->
                              %% 全部购买了
                              lists:max(EndTimeList);
                          _ -> %% 没有全部购买
                              ActETime =
                                  case lib_custom_act_util:get_custom_act_open_info(Type, SubType) of
                                      #act_info{etime = Etime} -> Etime;
                                      _ -> 0
                                  end,
                              lists:max([ActETime | EndTimeList])
                      end,
            if
                Now >= EndTime ->
                    false;
                true -> %% 需要展示的话，都是返回1
                    {true, #act_info{
                        key = {Type, SubType},              %% {type, subtype}
                        stime = STime,
                        etime = ETime
                    }}
            end;
        _ ->
            false
    end;
is_personal_act_open(Player, Type = ?CUSTOM_ACT_TYPE_INVESTMENT, SubType) ->
    lib_custom_act_investment:is_personal_act_open(Player,Type,SubType);
is_personal_act_open(Player, Type, SubType) ->
    NowTime = utime:unixtime(),
    case get_custom_act_personal_open_info(Player, Type, SubType) of
        #custom_act_personal{s_time = STime, e_time = EndTime}
            when NowTime < EndTime orelse EndTime =:= 1 ->
            {true, #act_info{
                key = {Type, SubType},              %% {type, subtype}
                stime = STime,
                etime = EndTime
            }};
        _ ->
            false
    end.

%% @doc 获取活动开启信息 @return #custom_act_personal{}|false
get_custom_act_personal_open_info(Player, Type, SubType) ->
    #player_status{
        status_custom_act = #status_custom_act{custom_act_personal = PersonalAct}
    } = Player,
    case lists:keyfind({Type, SubType}, #custom_act_personal.key, PersonalAct) of
        #custom_act_personal{} = ActInfo ->
            ActInfo;
        _ ->
            false
    end.

%% ---------------------------------------------------------------------------
%% @doc 活动开启条件判断
check_condition(_Player, [], _Data, _ExtraData) -> true;
check_condition(Player, [{open_lv, LvNeed} | T], Data, ExtraData) ->
    #player_status{figure = #figure{lv = RoleLv}} = Player,
    case RoleLv >= LvNeed of
        true ->
            check_condition(Player, T, Data, ExtraData);
        false ->
            {false, 3}
    end;
check_condition(Player, [{role_lv, LvNeed} | T], Data, ExtraData) ->
    #player_status{figure = #figure{lv = RoleLv}} = Player,
    case RoleLv >= LvNeed of
        true ->
            check_condition(Player, T, Data, ExtraData);
        false ->
            {false, 4}
    end;
check_condition(Player, [{foster_grade, FosterType, FosterGrade} | T], Data, ExtraData) ->
    #player_status{role_foster = RoleFoster} = Player,
    #role_foster{fosters = CellFosters} = RoleFoster,
    case lists:keyfind(FosterType, #cell_foster.type, CellFosters) of
        #cell_foster{grade = Grade} = _Info when Grade =:= FosterGrade ->
            check_condition(Player, T, Data, ExtraData);
        _ ->
            {false, 5}
    end;
check_condition(Player, [{act_poiont, ActPoiont} | T], Data, ExtraData) ->
    #player_status{figure = #figure{id = RoleId}} = Player,
    Live = lib_daily:get_count(RoleId, ?MOD_ACTIVITY, ?ACTIVITY_LIVE_DAILY),
    if
        Live >= ActPoiont -> check_condition(Player, T, Data, ExtraData);
        true -> {false, 6}
    end;
check_condition(Player, [{m, MonetaryList} | T], Data, ExtraData) ->
    #player_status{recharge_statistic = RechargeStatistic} = Player,
    #role_recharge_statistic{monetary = Monetary} = RechargeStatistic,
    case lists:member(Monetary, MonetaryList) of
        true ->
            check_condition(Player, T, Data, ExtraData);
        _ ->
            {false, 7}
    end;
check_condition(Player, [{t, TopList} | T], Data, ExtraData) ->
    #player_status{recharge_statistic = RechargeStatistic} = Player,
    #role_recharge_statistic{top = Top} = RechargeStatistic,
    case lists:member(Top, TopList) of
        true ->
            check_condition(Player, T, Data, ExtraData);
        _ ->
            {false, 8}
    end;
check_condition(Player, [{f, FrequencyList} | T], Data, ExtraData) ->
    #player_status{recharge_statistic = RechargeStatistic} = Player,
    #role_recharge_statistic{frequency = Frequency} = RechargeStatistic,
    case lists:member(Frequency, FrequencyList) of
        true ->
            check_condition(Player, T, Data, ExtraData);
        _ ->
            {false, 9}
    end;
%% 宝宝系统
check_condition(Player, [{child_condition, Year} | T], Data, ExtraData) ->
    #player_status{status_child = Child} = Player,
    #status_child{child_age_year = ChildYear, child_sex = ChildSex} = Child,
    case ChildSex > 0 of
        true  ->
            case ChildYear >= Year of
                true  ->
                    check_condition(Player, T, Data, ExtraData);
                false ->
                    {false, 10}
            end;
        false ->
            {false, 11}
    end;
%% 完成任务
check_condition(Player, [{task_id, TaskId} | T], Data, ExtraData) ->
    case lib_role_task:is_finish_task_id(TaskId) of
        true  ->
            check_condition(Player, T, Data, ExtraData);
        false ->
            {false, 12}
    end;
%% 爬塔副本
check_condition(Player, [{tower_dun, Num} | T], Data, ExtraData)  ->
    {_,TowerTimes} = lists:keyfind(?TOWER_PASS_BEST_NUM, 1, ExtraData),
    if
        TowerTimes >= Num ->
            check_condition(Player, T, Data, ExtraData);
        true ->
            {false, 13}
    end;
%% 宠物副本
check_condition(Player, [{pet_dun, Num} | T], Data, ExtraData)  ->
    {_,PetTimes} = lists:keyfind(?PET_PASS_BEST_NUM, 1, ExtraData),
    if
        PetTimes >= Num ->
            check_condition(Player, T, Data, ExtraData);
        true ->
            {false, 14}
    end;
%% 星辰副本
check_condition(Player, [{dun_galaxy, Num} | T], Data, ExtraData)  ->
    {_,Chapter} = lists:keyfind(?GALAXY_DUN_CHAPTER, 1, ExtraData),
    if
        Chapter >= Num ->
            check_condition(Player, T, Data, ExtraData);
        true ->
            {false, 15}
    end;
%% 守卫信标
check_condition(Player, [{dun_guard, Id} | T], Data, ExtraData)  ->
    case lists:keyfind(dun_id, 1, Data) of
        {_, DunId} when Id == DunId ->
            check_condition(Player, T, Data, ExtraData);
        _ ->
            {false, 16}
    end;
%% 战魂星级
check_condition(Player, [{warsoul_star, MaxStar, Num} | T], Data, ExtraData)  ->
    {_,StarList} = lists:keyfind(?WARSOUL_STAR_INFO, 1, ExtraData),
    case lists:keyfind(MaxStar, 1, StarList) of
        {_, Sum} ->
            if
                Sum >= Num ->
                    check_condition(Player, T, Data, ExtraData);
                true ->
                    {false, 17}
            end;
        _ ->
            {false, 17}
    end;

check_condition(Player, [_ | T], Data, ExtraData) ->
    check_condition(Player, T, Data, ExtraData).

%% ---------------------------------------------------------------------------
%% @doc 活动结束
%% 清理玩家身上的活动数据
clear_personal_act_in_role(Player, Type, SubType) ->
    #player_status{
        status_custom_act = StatusCustomAct
    } = Player,
    #status_custom_act{
        custom_act_personal = CustomActPerson
    } = StatusCustomAct,
    NewCustomActPerson = lists:keydelete({Type, SubType}, #custom_act_personal.key, CustomActPerson),
    Player#player_status{status_custom_act = StatusCustomAct#status_custom_act{custom_act_personal = NewCustomActPerson}}.

%% @doc 返回未领取的奖励
send_forgot_rewards(?CUSTOM_ACT_TYPE_RECHARGE, SubType) ->
    lib_custom_act_do_recharge:send_forgot_rewards(?CUSTOM_ACT_TYPE_RECHARGE, SubType);
send_forgot_rewards(?CUSTOM_ACT_THEME_FIRST_RECHARGE, SubType) ->
    lib_custom_act_do_recharge:send_forgot_rewards(?CUSTOM_ACT_THEME_FIRST_RECHARGE, SubType);
send_forgot_rewards(_ActType, _SubType) -> ok.
%% ---------------------------------------------------------------------------
%% @doc 保存活动数据
save_personal_act(Player, ActInfo) ->
    #player_status{
        status_custom_act = StatusCustomAct
    } = Player,
    #status_custom_act{
        custom_act_personal = CustomActPerson
    } = StatusCustomAct,
    NewCustomActPerson = lists:keystore(ActInfo#custom_act_personal.key, #custom_act_personal.key, CustomActPerson, ActInfo),
    Player#player_status{status_custom_act = StatusCustomAct#status_custom_act{custom_act_personal = NewCustomActPerson}}.

save_personal_act_db(RoleId, ActInfo) ->
    #custom_act_personal{
        key = {Type, SubType},
        s_time = Sime,
        e_time = EndTime,
        time = Time,
        act_data = ActData,
        red_gift = RedGift
    } = ActInfo,
    NewData = util:term_to_string(ActData),
    RedGiftBin = util:term_to_string(RedGift),
    SQL = io_lib:format(?insert_opening_custom_act_personal, [RoleId, Type, SubType, Sime, EndTime, Time, NewData, RedGiftBin]),
    db:execute(SQL).