%% ---------------------------------------------------------------------------
%% @doc 跨服竞榜
%% @author zhaoyu
%% @since  2018-11-30
%% ---------------------------------------------------------------------------
-module(race_rank).


-export([
    load_role_race_rank_data/1          %% 加载角色竞榜活动数据
    ,db_save_role_race_rank_data/2      %% 角色竞榜活动数据保存到数据库
    ,db_delete_role_race_rank_data/1    %% 根据活动Id删除角色竞榜活动数据
]).


-export([
    race_rank_award/3                   %% 结算排行榜，发奖励.
    ,race_rank_act_info/2               %% 查询竞榜活动信息
    ,draw/4                             %% 抽奖
    %,wish/4
    ,times_award/3                      %% 累计抽奖次数奖励领取
    ,opening_race_rank_act_info/1       %% 所有的正在开启中的竞榜活动信息
    ,get_act_world_lev_stage/1          %% 活动世界等级阶段数据
    ,del_role_race_rank_act_data/1      %% 删除角色竞榜活动数据
    ,role_del_race_rank_act_data/2      %% 角色删除竞榜活动数据
    ,fetch_rank_data/2
]).

-export([
    handle_merge/3                      %% 合服处理(在跨服中执行)
]).


-include("race_rank.hrl").
-include("common.hrl").
-include("server.hrl").
-include("predefine.hrl").
-include("custom_act.hrl").
-include("errcode.hrl").
-include("goods.hrl").
-include("figure.hrl").
-include("def_module.hrl").

-compile(export_all).

%% 多次结果数据
-record(multi_result, {
        award_id_list = []              %% 抽中的奖励Id列表
        ,award_list = []                %% 抽中的物品列表
        ,log_list = []                  %% 展示列表
        ,broadcast_list = []            %% 传闻列表
    }).

%% ---------------------------------------------------------------------------
%% @doc 加载角色竞榜活动数据
-spec load_role_race_rank_data(RoleId) -> RoleRaceRank when
    RoleId      :: integer(),
    RoleRaceRank:: #role_race_rank{}.
%% ---------------------------------------------------------------------------
load_role_race_rank_data(RoleId) ->
    SQL = io_lib:format(?RR_ROLE_SELECT, [RoleId]),
    DbList = db:get_all(SQL),
    DataList = load_role_race_rank_data_do(DbList, []),
    #role_race_rank{act_data = DataList}.

load_role_race_rank_data_do([], DataList) ->
    DataList;
load_role_race_rank_data_do([OneActData | T], DataList) ->
    ActData = make_record(OneActData),
    DataList2 = [ActData | DataList],
    load_role_race_rank_data_do(T, DataList2).

make_record(OneActData) ->
    [
        ActId, Times, BuyTenTimes, BuyTenTime, Score, UseFreeTimes, FreeTime
        ,AwardLogDb, ValidTime, OCoin, OStage, OTime, OCDraw, OCTime, ORDraw, ORTime
    ] = OneActData,
    AwardLog = util:bitstring_to_term(AwardLogDb),
    Now = utime:unixtime(),
    {Coin,Stage,Time} = case utime:is_today(OTime) of
        true->
            {OCoin,OStage,OTime};
        _->
            {0,0,Now}
    end,
    {CDraw,CTime} = case utime:is_today(OCTime) of
        true->
            {OCDraw,OCTime};
        _->
            {0,Now}
    end,
    {RDraw,RTime} = case utime:is_today(ORTime) of
        true->
            {ORDraw,ORTime};
        _->
            {0,Now}
    end,
    #rr_act_data{
        act_id = ActId, times = Times, buy_ten_times = BuyTenTimes
        ,buy_ten_time = BuyTenTime, score = Score, use_free_times = UseFreeTimes
        ,free_time = FreeTime, award_log = AwardLog, valid_time = ValidTime
        ,coin=Coin,stage=Stage,time=Time,coin_draw=CDraw
        ,coin_time=CTime,rune_draw=RDraw,rune_time=RTime
    }.



%% ---------------------------------------------------------------------------
%% @doc 加载角色竞榜活动数据保存到数据库
-spec db_save_role_race_rank_data(Role, ActData) -> ok when
    Role      :: #player_status{},
    ActData   :: #rr_act_data{}.
%% ---------------------------------------------------------------------------
db_save_role_race_rank_data(Role = #player_status{}, ActData) ->
    #player_status{id = RoleId} = Role,
    db_save_role_race_rank_data(RoleId, ActData);
db_save_role_race_rank_data(RoleId, ActData) ->
    #rr_act_data{
        act_id = ActId, times = Times, buy_ten_times = BuyTenTimes
        ,buy_ten_time = BuyTenTime, score = Score, use_free_times = UseFreeTimes
        ,free_time = FreeTime, award_log = AwardLog, valid_time = ValidTime
        ,coin=Coin,stage=Stage,time=Time,coin_draw=CDraw
        ,coin_time=CTime,rune_draw=RDraw,rune_time=RTime
    } = ActData,
    AwardLogStr = util:term_to_string(AwardLog),
    Data = [
        RoleId, ActId, Times, BuyTenTimes, BuyTenTime, Score, UseFreeTimes, FreeTime
        ,AwardLogStr, ValidTime,Coin,Stage,Time,CDraw,CTime,RDraw,RTime
    ],
    SQL = io_lib:format(?RR_ROLE_REPLACE, Data),
    db:execute(SQL),
    ok.


%% ---------------------------------------------------------------------------
%% @doc 根据活动Id删除角色竞榜活动数据
-spec db_delete_role_race_rank_data(ActId) -> ok when
    ActId     :: integer().
%% ---------------------------------------------------------------------------
db_delete_role_race_rank_data(ActId) ->
    db:execute(io_lib:format(?RR_ROLE_DEL, [ActId])),
    ok.

% db_delete_server_score(ActId)->
%     db:execute(io_lib:format(?SQL_SERVER_SCORE_DEL, [ActId])),
%     ok.


%% ---------------------------------------------------------------------------
%% @doc 结算排行榜，发奖励.
race_rank_award(_StartTime,_EndTime,[]) ->
    ok;
race_rank_award(StartTime,EndTime,RankRoleList = [#rr_rank_role{act_id = ActId} | _]) ->
    Stage = get_act_world_lev_stage(ActId),
    race_rank_award(StartTime,EndTime,RankRoleList, Stage).

race_rank_award(_StartTime,_EndTime,[], _Stage) ->
    ok;
race_rank_award(StartTime,EndTime,[RankRole | T], Stage) ->
    #rr_rank_role{
        role_id = RoleId, act_id = ActId, ranking = Ranking, score = Score
    } = RankRole,
    case data_race_rank:get_race_rank_award(ActId, Stage, Ranking) of
        #race_rank_award{award = Award} when Award =/= [] ->
            ActName = case data_race_rank:get_race_rank_act(ActId) of
                #race_rank_act{name = AName} -> AName;
                _ -> <<>>
            end,
            case data_race_rank:get_race_rank_act(ActId) of
                #race_rank_act{type=?TYPE_RACE}->
                    Title = utext:get(3330003);
                _->
                    Title = utext:get(3330005)
            end,
            Content = utext:get(3330004, [ActName, Score, Ranking]),
            lib_mail_api:send_sys_mail([RoleId], Title, Content, Award),
            %% local log
            local_log_award(StartTime,EndTime,RankRole);
        _ ->
            String = "race_rank_award no config:RoleId:~w,ActId:~w,Ranking:~w",
            ?ERR(String, [RoleId, ActId, Ranking])
    end,
    race_rank_award(StartTime,EndTime,T, Stage).

local_log_award(Stime,Etime,RankRole) ->
    #rr_rank_role{
        act_id  = ActId, zone_id = ZoneId, role_id = RoleId
        ,role_name = Name,score = Score, ranking = Rank, time = Time
    } = RankRole,
    Data = [
        ActId, ZoneId, RoleId, Name,Stime,Etime
        ,Score, Rank, Time, utime:unixtime()
    ],
    db:execute(io_lib:format(?RR_LOG_RANK_REPLACE, Data)),
    ok.


%% ---------------------------------------------------------------------------
%% @doc 竞榜活动展示期结束时间戳
-spec act_show_end_time(ActId) -> ShowEndTime :: integer() when
    ActId       :: integer().
%% ---------------------------------------------------------------------------
act_show_end_time(ActId) ->
    case data_race_rank:get_race_rank_act(ActId) of
        #race_rank_act{
            end_time = EndTime, show_time = ShowTime
        } ->
            EndTime + ShowTime;
        _ ->
            0
    end.


%% ---------------------------------------------------------------------------
%% @doc 角色竞榜活动数据
-spec role_race_rank_act_data(Role, ActId) -> ActData when
    Role        :: #player_status{},
    ActId       :: integer(),
    ActData     :: #rr_act_data{}.
%% ---------------------------------------------------------------------------
role_race_rank_act_data(Role, ActId) ->
    #player_status{race_rank = RaceRankData} = Role,
    #role_race_rank{act_data = DataList} = RaceRankData,
    case lists:keyfind(ActId, #rr_act_data.act_id, DataList) of
        ActData = #rr_act_data{} ->
            refresh_act_data(ActData);
        _ ->
            #rr_act_data{act_id = ActId}
    end.

%% 刷新活动数据
refresh_act_data(ActData) ->
    #rr_act_data{free_time = FreeTime, buy_ten_time = BuyTenTime} = ActData,
    Now = utime:unixtime(),
    ActData2 = case utime:is_same_day(Now, FreeTime) of
        true -> ActData;
        false -> ActData#rr_act_data{use_free_times = 0}
    end,
    case utime:is_same_day(Now, BuyTenTime) of
        true -> ActData2;
        false -> ActData2#rr_act_data{buy_ten_times = 0}
    end.



%% ---------------------------------------------------------------------------
%% @doc 角色竞榜活动数据
-spec save_race_rank_act_data(Role, ActData) -> NewRole when
    Role        :: #player_status{},
    ActData     :: #rr_act_data{},
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
save_race_rank_act_data(Role, ActData) ->
    #player_status{id = RoleId, race_rank = RaceRankData} = Role,
    #role_race_rank{act_data = DataList} = RaceRankData,
    #rr_act_data{act_id = ActId} = ActData,
    DataList2 = lists:keystore(ActId, #rr_act_data.act_id, DataList, ActData),
    RaceRankData2 = #role_race_rank{act_data = DataList2},
    db_save_role_race_rank_data(RoleId, ActData),
    Role#player_status{race_rank = RaceRankData2}.


%% ---------------------------------------------------------------------------
%% @doc 所有的正在开启中的竞榜活动信息
-spec opening_race_rank_act_info(Role) -> ok when
    Role      :: #player_status{}.
%% ---------------------------------------------------------------------------
opening_race_rank_act_info(Role) ->
    #player_status{sid = Sid} = Role,
    race_rank_act_local:cast({send_opening_act_info, Sid}).


%% ---------------------------------------------------------------------------
%% @doc 查询竞榜活动信息
-spec race_rank_act_info(Role, ActId) -> ok when
    Role        :: #player_status{},
    ActId       :: integer().
%% ---------------------------------------------------------------------------
race_rank_act_info(Role, ActId) ->
    case race_rank_act_local:is_race_rank_act_icon_opening(ActId) of
        false ->
            ok;
        true ->
            ActData = #rr_act_data{
                times = Times, score = Score, use_free_times = UseFreeTimes
                ,buy_ten_times = BuyTenTimes
            } = role_race_rank_act_data(Role, ActId),
            FreeTimes = 0,
            LessTimes = max(0, FreeTimes - UseFreeTimes),
            Stage = get_act_world_lev_stage(ActId),
            TimesAwardList = data_race_rank:get_times_award_list(ActId, Stage),
            StatusList = [
                {AwardId, times_award_status(ActData, ActId, AwardId)} ||
                AwardId <- TimesAwardList
            ],
            #player_status{sid = Sid} = Role,
            Data = [ActId, LessTimes, BuyTenTimes, Score, Times, StatusList],
            ServerId = config:get_server_id(),
            clusters_node:apply_cast(
                race_rank_mgr, cast, [{race_rank_act_info, 33301,Sid, ServerId, Data}]
            ),
            ok
    end.

%%获取数据
% wish_rank_act_info(Status, ActId)->
%     #player_status{id=RoleID}=Status,
%     case race_rank_act_local:is_race_rank_act_icon_opening(ActId) of
%         false ->
%             ok;
%         true ->
%             ActData = #rr_act_data{
%                 score = Score,
%                 coin  = Coin,
%                 rune_draw = RTime
%             } = race_rank:role_race_rank_act_data(Status, ActId),
%             Stage = race_rank:get_act_world_lev_stage(ActId),
%             TimesAwardList = data_race_rank:get_times_award_list(ActId, Stage),
%             StatusList = [
%                 {AwardId, race_rank:times_award_status(ActData, ActId, AwardId)} ||
%                 AwardId <- TimesAwardList
%             ],
%             Total = lib_recharge_api:get_today_pay_gold(RoleID),
%             Data = [ActId,Coin,Total,Score,RTime,StatusList],
%             {ok,Bin} = pt_333:write(33305,Data),
%             lib_server_send:send_to_uid(RoleID,Bin),
%             ok
%     end.

%%获取榜单数据
fetch_rank_data(Status, ActId)->
    case race_rank_act_local:is_race_rank_act_icon_opening(ActId) of
        false ->
            ok;
        true ->
            #player_status{sid = Sid} = Status,
            ServerId = config:get_server_id(),
            clusters_node:apply_cast(
                race_rank_mgr, cast, [{fetch_rank_data,33304,Sid,ServerId,ActId}]
            ),
            ok
    end.

%% ---------------------------------------------------------------------------
%% @doc 抽奖次数奖励领取状态
-spec times_award_status(ActData, ActId, AwardId) -> Status when
    ActData     :: #rr_act_data{},
    ActId       :: integer(),
    AwardId     :: integer(),
    Status      :: integer().
%% ---------------------------------------------------------------------------
times_award_status(ActData, ActId, AwardId) ->
    #rr_act_data{score = Score, award_log = AwardLog} = ActData,
    case lists:member(AwardId, AwardLog) of
        true ->
            ?ACT_REWARD_HAS_GET;
        false ->
            Stage = get_act_world_lev_stage(ActId),
            case data_race_rank:get_race_rank_times_award(ActId, Stage, AwardId) of
                #race_rank_times_award{need_score = NScore} when Score >= NScore ->
                    ?ACT_REWARD_CAN_GET;
                _ ->
                    ?ACT_REWARD_CAN_NOT_GET
            end
    end.


%% ---------------------------------------------------------------------------
%% @doc 抽奖
-spec draw(Role, ActId, Times, IsAutoBuy) ->
    {false, Reason} | {ok, NewRole, Score, DrawTime, Stage, AwardIdList} when
    Role        :: #player_status{},
    ActId       :: integer(),
    Times       :: integer(),
    IsAutoBuy   :: integer(),   %% 0:不自动购买 1:自动购买(数量不足时自动花勾玉购买)
    Reason      :: integer(),
    NewRole     :: #player_status{},
    Score       :: integer(),
    DrawTime    :: integer(),
    Stage       :: integer(),
    AwardIdList :: [AwardId :: integer()].
%% ---------------------------------------------------------------------------
draw(Role, ActId, Times, IsAutoBuy) ->
    IsOpen = race_rank_act_local:is_race_rank_act_opening(ActId),

    ActData = role_race_rank_act_data(Role, ActId),
    #rr_act_data{buy_ten_times = BuyTenTimes} = ActData,
    CostList = case Times of
        1 ->
            data_race_rank:get_race_rank_config(rock_cost_1);
        10 ->
            OpenDay = util:get_open_day(),
            DiscountDay = data_race_rank:get_race_rank_config(discount_opday),
            case OpenDay >= DiscountDay of
                false ->
                    data_race_rank:get_race_rank_config(rock_cost_10);
                _ ->
                    data_race_rank:get_ten_times_cost(BuyTenTimes + 1)
            end
    end,
    Stage = get_act_world_lev_stage(ActId),
    Ids = data_race_rank:get_draw_pool_award_ids(ActId, Stage),
    IsConnected = clusters_api:is_connected_center(),
    #player_status{id = RoleId, figure = #figure{lv = Lev, name = Name, vip=Vip}} = Role,
    NeedLev = case data_race_rank:get_race_rank_act(ActId) of
        #race_rank_act{lv = NLev} -> NLev;
        _ -> false
    end,
    if
        IsOpen =:= false ->
            {false, ?ERRCODE(err333_act_closed)}; %%?ERR333_ACT_CLOSED};
        IsConnected =:= false ->
            {false, ?ERRCODE(err333_not_connect_center)};%?ERR333_NOT_CONNECT_CENTER};
        not is_list(CostList) orelse CostList =:= [] ->
            {false, ?ERRCODE(err333_no_cost_config)};%?ERR333_NO_COST_CONFIG};
        Ids =:= [] ->
            {false, ?ERRCODE(err333_no_pool_cfg)};%?ERR333_NO_POOL_CFG};
        Lev < NeedLev ->
            {false, ?ERRCODE(err333_role_lev_less)};%ERR333_ROLE_LEV_LESS};
        true ->
            case check_cost(Role, Times, ActData, CostList, IsAutoBuy, ActId) of
                {false, ErrorCode, _RoleTemp} ->
                    {false, ErrorCode};
                {true, Role2, ActData1, RealCostList} ->
                    Now = utime:unixtime(),
                    {ActData2, MultiResult} = draw_goods(
                        ActData1, Times, ActId, Stage, Ids, #multi_result{}, Now
                    ),
                    #multi_result{
                        award_id_list = GetAwardIdList, award_list = AwardList
                        ,broadcast_list = BroadcastList
                    } = MultiResult,
                    AddScore0 = case Times of
                        1 -> data_race_rank:get_race_rank_config(rock_score_1);
                        10 -> data_race_rank:get_race_rank_config(rock_score_10)
                    end,
                    AddScore = case is_integer(AddScore0) of
                        true -> AddScore0;
                        false -> 0
                    end,
                    #rr_act_data{score = Score} = ActData,
                    ValidTime = act_show_end_time(ActId),
                    ActData3_0 = ActData2#rr_act_data{
                        score = Score + AddScore, valid_time = ValidTime
                    },
                    ActData3 = case Times of
                        10 ->
                            ActData3_0#rr_act_data{
                                buy_ten_times = BuyTenTimes + 1, buy_ten_time = Now
                            };
                        _ ->
                            ActData3_0
                    end,
                    Role3 = save_race_rank_act_data(Role2, ActData3),
                    Produce = #produce{
                        type = race_rank, subtype = ActId, reward = AwardList
                        ,title = utext:get(3330001), content = utext:get(3330002)
                    },
                    {ok, Role4} = lib_goods_api:send_reward_with_mail(Role3, Produce),
                    #rr_act_data{times = Times2, score = Score2} = ActData3,
                    hit_rank(Role4, ActData3),
                    % 传闻
                    NewBroadcastList = [
                        TvData#custom_tv_data{role_id = RoleId, name = Name} ||
                        TvData <- BroadcastList
                    ],
                    #race_rank_act{
                        name = ActName
                    } = data_race_rank:get_race_rank_act(ActId),
                    send_broadcast(ActName, ActId, 1, NewBroadcastList),
                    % 日志
                    LogData = [
                        RoleId,Name,ActId,util:term_to_string(AwardList),util:term_to_string(RealCostList),
                        IsAutoBuy, Role#player_status.gold, Role4#player_status.gold, Times, Score2, Vip, Now
                    ],
                    lib_log_api:log_race_rank_draw(LogData),
                    % ServerAdd = calc_server_add(Times),
                    % race_rank_act_local:cast({draw_add_score,Stage,ActId,ServerAdd,Now}),
                    {ok, Role4, Score2,Times2, Stage, GetAwardIdList}
            end
    end.

calc_server_add(Times)->
    [Min,Max] = data_race_rank:get_race_rank_config(random_score_area),
    calc_server_add(Times,Min,Max,0).
calc_server_add(0,_,_,Add)->
    Add;
calc_server_add(Times,Min,Max,Add)->
    A = urand:rand(Min,Max),
    calc_server_add(Times-1,Min,Max,Add+A).

% %%祈愿
% wish(Status, ActId, OpTy,Auto) ->
%     IsOpen = race_rank_act_local:is_race_rank_act_opening(ActId),
%     ActData = role_race_rank_act_data(Status, ActId),
%     Stage = get_act_world_lev_stage(ActId),
%     Ids0 = data_race_rank:get_draw_pool_award_ids(ActId, Stage),
%     Ids  = filter_wish_ids(Ids0,ActId,Stage,OpTy),
%     IsConnected = clusters_api:is_connected_center(),
%     #player_status{id = RoleId, figure = #figure{lv = Lev, name = Name}} = Status,
%     NeedLev = case data_race_rank:get_race_rank_act(ActId) of
%         #race_rank_act{lv = NLev} -> NLev;
%         _ -> false
%     end,
%     if
%         IsOpen =:= false ->
%             {false, ?ERRCODE(err333_act_closed)}; %%?ERR333_ACT_CLOSED};
%         IsConnected =:= false ->
%             {false, ?ERRCODE(err333_not_connect_center)};%?ERR333_NOT_CONNECT_CENTER};
%         Ids =:= [] ->
%             {false, ?ERRCODE(err333_no_pool_cfg)};%?ERR333_NO_POOL_CFG};
%         Lev < NeedLev ->
%             {false, ?ERRCODE(err333_role_lev_less)};%ERR333_ROLE_LEV_LESS};
%         true ->
%             case check_wish_cost(Status,OpTy,ActData, ActId,Auto) of
%                 {false, ErrorCode} ->
%                     {false, ErrorCode};
%                 {true,Cost,NActData,NStatus} ->
%                     Now = utime:unixtime(),
%                     MultiResult = wish_goods(NActData, OpTy, ActId, Stage, Ids, #multi_result{}, Now),
%                     #multi_result{
%                         award_id_list = GetAwardIdList, award_list = AwardList
%                         ,broadcast_list = BroadcastList
%                     } = MultiResult,
%                     AddScore0 = case OpTy of
%                         1 -> data_race_rank:get_race_rank_config(wish_score_coin);
%                         _-> data_race_rank:get_race_rank_config(wish_score_rune)
%                     end,
%                     AddScore = case is_integer(AddScore0) of
%                         true -> AddScore0;
%                         false -> 0
%                     end,
%                     #rr_act_data{score = Score} = NActData,
%                     ValidTime = act_show_end_time(ActId),
%                     NActData0 = NActData#rr_act_data{score = Score + AddScore, valid_time = ValidTime},
%                     NStatus0 = save_race_rank_act_data(NStatus, NActData0),
%                     Produce = #produce{
%                         type = wish_rank, subtype = ActId, reward = AwardList
%                         ,title = utext:get(3330005), content = utext:get(3330002)
%                     },
%                     {ok, NStatus1} = lib_goods_api:send_reward_with_mail(NStatus0, Produce),
%                     hit_rank(NStatus1, NActData0),
%                     %% 传闻
%                     NewBroadcastList = [
%                         TvData#custom_tv_data{role_id = RoleId, name = Name} ||
%                         TvData <- BroadcastList
%                     ],
%                     #race_rank_act{
%                         name = ActName
%                     } = data_race_rank:get_race_rank_act(ActId),
%                     send_broadcast(ActName, ActId, 3, NewBroadcastList),
%                     %% 日志
%                     LogData = [
%                         RoleId,ActId,OpTy,Score + AddScore, util:term_to_string(AwardList)
%                         ,util:term_to_string(Cost), Now
%                     ],
%                     lib_log_api:log_race_rank_wish(LogData),
%                     {ok, NStatus1, Stage, GetAwardIdList}
%             end
%     end.

%% 上榜
hit_rank(Role, ActData) ->
    #rr_act_data{act_id = ActId, score = Score} = ActData,
    MaxRanking = data_race_rank:get_race_rank_config(max_rank),
    RankingScore = data_race_rank:get_ranking_requirement(ActId, MaxRanking),
    case Score < RankingScore of
        true ->
            ok;
        false ->
            #player_status{id = RoleId, figure = #figure{name = Name}} = Role,
            RankRole = #rr_rank_role{
                act_id = ActId, role_id = RoleId, role_name = Name
                ,server_id = config:get_server_id()
                ,server_num = config:get_server_num()
                ,score = Score, time = utime:unixtime()
            },
            clusters_node:apply_cast(race_rank_act, cast, [{hit_rank, RankRole}])
    end.


%% 发送传闻
send_broadcast(_ActName, _ActId, _TvType, []) ->
    ok;
send_broadcast(ActName, ActId, TvType, [TvData | T]) ->
    #custom_tv_data{name = Name, goods_id = GoodsId, num = Num} = TvData,
    Message = [Name, ActName, GoodsId, Num, ActId],
    BinData = lib_chat:make_tv(333, TvType, Message),
    lib_server_send:send_to_all(BinData),
    send_broadcast(ActName, ActId, TvType, T).


%% ---------------------------------------------------------------------------
%% @doc 扣除消耗
-spec check_cost(Role, Times, ActData, CostList, IsAutoBuy, ActId) ->
    {true, NewRole, NewActData, RealCostList} | {false, ErrorCode, NewRole} when
    Role            :: #player_status{},
    Times           :: integer(),
    ActData         :: #rr_act_data{},
    CostList        :: list(),
    IsAutoBuy       :: integer(),
    ActId           :: integer(),
    NewRole         :: #player_status{},
    NewActData      :: #rr_act_data{},
    RealCostList    :: list(),
    ErrorCode       :: integer().
%% ---------------------------------------------------------------------------
check_cost(Role, Times, ActData, CostList, IsAutoBuy, ActId) ->
    Result = if
        Times =:= 1 ->
            #rr_act_data{use_free_times = UseTimes} = ActData,
            FreeTimes = 0,
            LessTimes = max(0, FreeTimes - UseTimes),
            case LessTimes >= Times of
                true -> %% 使用免费次数
                    ActData#rr_act_data{
                        use_free_times = UseTimes + 1, free_time = utime:unixtime()
                    };
                false ->
                    false
            end;
        true ->
            false
    end,
    case Result of
        false ->
            About = integer_to_list(ActId),
            Result2 = case IsAutoBuy of
                1 ->
                    lib_goods_api:cost_objects_with_auto_buy(
                        Role, CostList, race_rank, About
                    );
                _ ->
                    case lib_goods_api:cost_object_list_with_check(
                            Role, CostList, race_rank, About) of
                        {true, TmpNewPlayer} -> {true, TmpNewPlayer, CostList};
                        Other -> Other
                    end
            end,
            case Result2 of
                {true, NewRole, RealCostList} ->
                    {true, NewRole, ActData, RealCostList};
                {false, ErrorCode, NewRole} ->
                    {false, ErrorCode, NewRole}
            end;
        #rr_act_data{} ->
            {true, Role, Result, []}
    end.

% check_wish_cost(Status,1,ActData,_ActId,_Auto)->
%     Cost = data_race_rank:get_race_rank_config(wish_cost_coin),
%     #rr_act_data{coin_draw=OCDraw,coin_time=OCTime,coin=Coin} = ActData,
%     CDraw = case utime:is_today(OCTime) of
%         true->
%             OCDraw;
%         _->
%             0
%     end,
%     case Cost=<Coin of
%         true->
%             {true,Cost,ActData#rr_act_data{coin_draw=CDraw+1,coin_time=utime:unixtime(),coin=max(Coin-Cost,0)},Status};
%         _->
%             {false,?ERR333_NEED_ENOUGH_COIN}
%     end;
% check_wish_cost(Status,_,ActData, ActId,Auto)->
%     #rr_act_data{rune_draw=ORDraw,rune_time=ORTime} = ActData,
%     RDraw = case utime:is_today(ORTime) of
%         true->
%             ORDraw;
%         _->
%             0
%     end,
%     Item = data_race_rank:get_race_rank_config(wish_cost_rune),
%     Num  = data_race_rank:get_rune_cost(RDraw+1),
%     Cost = [{?TYPE_GOODS,Item,Num}],
%     case Num>0 of
%         true->
%             About = integer_to_list(ActId),
%             Result = case Auto of
%                 1 ->
%                     lib_goods_api:cost_objects_with_auto_buy(Status, Cost, wish_draw, About);
%                 _ ->
%                     case lib_goods_api:cost_object_list_with_check(Status, Cost, wish_draw, About) of
%                         {true, TmpStatus} ->
%                             {true, TmpStatus};
%                         Other ->
%                             Other
%                     end
%             end,
%             case Result of
%                 {true, NewStatus,_} ->
%                     {true,Cost,ActData#rr_act_data{rune_draw=RDraw+1,rune_time=utime:unixtime()},NewStatus};
%                 {false, ErrorCode,_} ->
%                     {false, ErrorCode}
%             end;
%         _->
%             {false,?ERR333_HAS_NO_TIMES}
%     end.

% %% 抽奖Times次获得的奖励以及相关结果
% wish_goods(ActData,OpTy, ActId, Stage, Ids, MultiResult, NowTime)->
%     case OpTy of
%         1->
%             #rr_act_data{coin_draw=NewTimes}=ActData;
%         _->
%             #rr_act_data{rune_draw=NewTimes}=ActData
%     end,
%     CandidateList = candidate_list(ActId, Stage, NewTimes, Ids),
%     #race_rank_draw_pool{
%         award_id = Id, award = Award, is_broadcast = IsBroadcast
%     } = urand:rand_with_weight(CandidateList),
%     #multi_result{
%         award_id_list = GetAwardIdList, award_list = AwardList
%         ,broadcast_list = BroadcastList
%     } = MultiResult,
%     NewBroadcastList = case IsBroadcast of
%         1 -> award_to_tv_data(Award, NowTime, []) ++ BroadcastList;
%         _ -> BroadcastList
%     end,
%     NMultiResult = MultiResult#multi_result{
%         award_id_list = [Id | GetAwardIdList], award_list = Award ++ AwardList
%         ,broadcast_list = NewBroadcastList
%     },
%     NMultiResult.

%% 抽奖Times次获得的奖励以及相关结果
draw_goods(
    ActData, DoTimes, ActId, Stage, Ids, MultiResult, NowTime
) when DoTimes >= 1 ->
    #rr_act_data{times = Times} = ActData,
    NewTimes = Times + 1,
    CandidateList = candidate_list(ActId, Stage, NewTimes, Ids),
    #race_rank_draw_pool{
        award_id = Id, award = Award, is_broadcast = IsBroadcast
    } = urand:rand_with_weight(CandidateList),
    #multi_result{
        award_id_list = GetAwardIdList, award_list = AwardList
        ,broadcast_list = BroadcastList
    } = MultiResult,
    NewBroadcastList = case IsBroadcast of
        1 -> award_to_tv_data(Award, NowTime, []) ++ BroadcastList;
        _ -> BroadcastList
    end,
    MultiResult2 = MultiResult#multi_result{
        award_id_list = [Id | GetAwardIdList], award_list = Award ++ AwardList
        ,broadcast_list = NewBroadcastList
    },
    ActData2 = ActData#rr_act_data{times = NewTimes},
    draw_goods(ActData2, DoTimes - 1, ActId, Stage, Ids, MultiResult2, NowTime);
draw_goods(ActData, _DoTimes, _ActId, _Stage, _Ids, MultiResult, _NowTime) ->
    {ActData, MultiResult}.

%% 记录数据
award_to_tv_data([], _Time, TvDataList) ->
    TvDataList;
award_to_tv_data([{_AwardType, GoodsId, Num} | T], Time, TvDataList) ->
    TvData = #custom_tv_data{goods_id = GoodsId, num = Num, time = Time},
    TvDataList2 = [TvData | TvDataList],
    award_to_tv_data(T, Time, TvDataList2);
award_to_tv_data([_Miss | T], Time, TvDataList) ->
    award_to_tv_data(T, Time, TvDataList).

%% ---------------------------------------------------------------------------
%% @doc 候选奖励物品列表
-spec candidate_list(ActId, Stage, Times, AwardIdList) -> Return when
    ActId       :: integer(),
    Stage       :: integer(),
    Times       :: integer(),
    AwardIdList :: [AwardId :: integer()],
    Return      :: [{Weight, #race_rank_draw_pool{}}],
    Weight      :: integer().
%% ---------------------------------------------------------------------------
candidate_list(ActId, Stage, Times, AwardIdList) ->
    candidate_list_do(ActId, Stage, Times, AwardIdList, []).
candidate_list_do(_ActId, _Stage, _Times, [], CandidateList) ->
    CandidateList;
candidate_list_do(ActId, Stage, Times, [AwardId | T], CandidateList) ->
    NewCandidateList =
    case data_race_rank:get_race_rank_draw_pool(ActId, Stage, AwardId) of
        #race_rank_draw_pool{
            loading_times = LTimes, weight = Weight
        } = AwardConfig when LTimes =< Times ->
            [{Weight, AwardConfig} | CandidateList];
        _ ->
            CandidateList
    end,
    candidate_list_do(ActId, Stage, Times, T, NewCandidateList).


%% ---------------------------------------------------------------------------
%% @doc 累计抽奖次数奖励领取
-spec times_award(Role, ActId, AwardId) ->
    {false, Reason} | {ok, NewRole} when
    Role        :: #player_status{},
    ActId       :: integer(),
    AwardId     :: integer(),
    Reason      :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
times_award(Role, ActId, AwardId) ->
    IsOpen = race_rank_act_local:is_race_rank_act_icon_opening(ActId),
    Stage = get_act_world_lev_stage(ActId),
    AwardIdList = data_race_rank:get_times_award_list(ActId, Stage),
    IsMember = lists:member(AwardId, AwardIdList),
    ActData = role_race_rank_act_data(Role, ActId),
    #rr_act_data{score = CurScore, award_log = AwardLog} = ActData,
    Status = times_award_status(ActData, ActId, AwardId),
    Config = data_race_rank:get_race_rank_times_award(ActId, Stage, AwardId),
    #player_status{
        id=RoleId,
        figure = #figure{lv = Lev,name=RName,vip=Vip}
    } = Role,
    NeedLev = case data_race_rank:get_race_rank_act(ActId) of
        #race_rank_act{lv = NLev} -> NLev;
        _ -> false
    end,
    #race_rank_times_award{need_score=NeedS,award = Award} = Config,
    if
        IsOpen =:= false ->
            {false, ?ERRCODE(err333_act_closed)};
        not IsMember ->
            {false, ?ERRCODE(err333_no_times_reward_cfg)};
        Status =:= ?ACT_REWARD_HAS_GET ->
            {false, ?ERRCODE(err333_already_get_reward)};
        Status =:= ?ACT_REWARD_CAN_NOT_GET ->
            {false, ?ERRCODE(err333_can_not_get)};
        Lev < NeedLev ->
            {false, ?ERRCODE(err333_role_lev_less)};
        true ->
            case lib_goods_api:can_give_goods(Role, Award) of
                false ->
                    {false, ?ERRCODE(err150_no_cell)};
                true ->
                    NewAwardLog = [AwardId | AwardLog],
                    ValidTime = act_show_end_time(ActId),
                    ActData2 = ActData#rr_act_data{
                        award_log = NewAwardLog, valid_time = ValidTime
                    },
                    %% 保存数据
                    Role2 = save_race_rank_act_data(Role, ActData2),

                    %% 发奖励
                    Produce = #produce{
                        type = race_rank_times, subtype = ActId, reward = Award
                    },
                    Role3 = lib_goods_api:send_reward(Role2, Produce),
                    % 传闻
                    #player_status{
                        id = RoleId, figure = #figure{name = Name}
                    } = Role3,
                    TvDataList = award_to_tv_data(Award, utime:unixtime(), []),
                    BroadcastList = [
                        TvData#custom_tv_data{role_id = RoleId, name = Name} ||
                        TvData <- TvDataList
                    ],
                    #race_rank_act{
                        name = ActName
                    } = data_race_rank:get_race_rank_act(ActId),
                    TvType = case data_race_rank:get_race_rank_act(ActId) of
                        #race_rank_act{type=?TYPE_RACE}->
                            2;
                        _->
                            4
                    end,
                    send_broadcast(ActName, ActId, TvType, BroadcastList),
                    lib_log_api:log_race_rank_times_award(RoleId,RName,ActId,AwardId,Award,NeedS,CurScore,Vip),
                    {ok, Role3}
            end
    end.


%% ---------------------------------------------------------------------------
%% @doc 活动世界等级阶段数据
% -spec get_act_world_lev_stage(ActId) -> Stage when
%     ActId       :: integer(),
%     Stage       :: integer().
% 20200716 世界等级不再锁定
% %% ---------------------------------------------------------------------------
get_act_world_lev_stage(_ActId) ->
    % case game_env:get(rr_act_world_lev_stage) of
    %     StageList when is_list(StageList) ->
    %         case lists:keyfind(ActId, #rr_act_stage.act_id, StageList) of
    %             #rr_act_stage{stage = Stage} ->
    %                 Stage;
    %             _ ->
    %                 WorldLev = util:get_world_lv(),
    %                 Stage = data_race_rank:get_world_lev_stage(WorldLev),
    %                 Stage
    %         end;
    %     _ ->
    WorldLev = util:get_world_lv(),
    Stage = data_race_rank:get_world_lev_stage(WorldLev),
    Stage.
    % end.


%% ---------------------------------------------------------------------------
%% @doc 删除角色竞榜活动数据
-spec del_role_race_rank_act_data(ActId) -> ok when
    ActId       :: integer().
%% ---------------------------------------------------------------------------
del_role_race_rank_act_data(ActId) ->
    db_delete_role_race_rank_data(ActId),
    % db_delete_server_score(ActId),
    % race_rank_act_local:cast({clear_server_score,ActId}),
    lib_player:cast_online_role_apply(
        ?APPLY_CAST_SAVE, ?MODULE, role_del_race_rank_act_data, [ActId]
    ),
    ok.

%% ---------------------------------------------------------------------------
%% @doc 角色删除竞榜活动数据
-spec role_del_race_rank_act_data(Role, ActId) -> NewRole when
    Role        :: #player_status{},
    ActId       :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
role_del_race_rank_act_data(Role, ActId) ->
    #player_status{race_rank = RaceRankData} = Role,
    #role_race_rank{act_data = DataList} = RaceRankData,
    DataList2 = lists:keydelete(ActId, #rr_act_data.act_id, DataList),
    RaceRankData2 = RaceRankData#role_race_rank{act_data = DataList2},
    Role#player_status{race_rank = RaceRankData2}.


%% ---------------------------------------------------------------------------
%% @doc 合服处理(在跨服中执行)
-spec handle_merge(ToServerId, ToServerNum, MergeIdList) -> ok when
    ToServerId  :: integer(),   %% 合服后的ServerId
    ToServerNum :: integer(),   %% 合服后的ServerNum
    MergeIdList :: [ServerId :: integer()].
%% ---------------------------------------------------------------------------
handle_merge(ToServerId, ToServerNum, MergeIdList) ->
    race_rank_mgr:cast({handle_merge, ToServerId, ToServerNum, MergeIdList}),
    ok.


%%许愿池道具需过滤相应金币和符文
filter_wish_ids(Ids,ActId,Stage,OpTy)->
    filter_wish_ids(Ids,ActId,Stage,OpTy,[]).
filter_wish_ids([],_ActId,_Stage,_OpTy,Ids)->
    Ids;
filter_wish_ids([H|T],ActId,Stage,OpTy,Ids)->
    case data_race_rank:get_race_rank_draw_pool(ActId,Stage,H) of
        #race_rank_draw_pool{type=?TYPE_POOL_COIN} when OpTy =:= 1 ->
            filter_wish_ids(T,ActId,Stage,OpTy,[H|Ids]);
        #race_rank_draw_pool{type=?TYPE_POOL_RUNE} when OpTy =:= 2 ->
            filter_wish_ids(T,ActId,Stage,OpTy,[H|Ids]);
        _->
            filter_wish_ids(T,ActId,Stage,OpTy,Ids)
    end.
