%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     靓丽大赛本服处理
%%% @end
%%%-------------------------------------------------------------------
-module(beauty_contest_local).
-author("liushl").

-include("beauty_contest.hrl").
-include("figure.hrl").
-include("errcode.hrl").
-include("rec_dress_up.hrl").
-include("def_module.hrl").
-include("server_kv.hrl").
-include("common.hrl").

-define(SERVER, ?MODULE).


-export([
    handle/3                            %% 本服逻辑处理
    , init_local_act/1                  %% 游戏节点初始化
    , end_local_act/1                   %% 本服活动结束
    , record_new_round/1                %% 记录新的一轮活动
    , broadcast_act_stage/1             %% 广播活动状态
    , broad_cast_local_champion/1       %% 广播冠军
    , db_save_act_role_batch/1          %% 批量保存参赛数据
]).

%% 游戏节点初始化
init_local_act(State) ->
    %% 查看当前轮次
    [{_, Round}] = lib_server_kv:get_value([{?SERVER_KV_LOCAL_BC_ROUND, 0}]),
    case Round =:= 0 of
        true  ->
            %% 活动还没开启过
            State;
        false ->
            %% 活动进行中，则先初始化比赛
            State0 = State#beauty_contest{round = Round},
            State1 = init_local_stage(State0),
            %% 加载上期活动数据
            State2 = State1#beauty_contest{act_log = load_act_log()},
            case State2#beauty_contest.stage =:= ?BC_STAGE_SIGN of
                true  -> %% 报名阶段-加载玩家报名数据
                    SignData = db:get_all(?SQL_BC_LOAD_SIGN),
                    List =
                        [
                            {
                                RoleId,
                                #beauty_contest_sign{
                                    role_id = RoleId
                                    , contest_data  = util:bitstring_to_term(ContestDataBin)
                                    , sign_award = SignAward
                                }
                            }
                            || [RoleId, ContestDataBin, SignAward] <- SignData
                        ],
                    State2#beauty_contest{sign_roles = maps:from_list(List)};
                false -> %% 比赛阶段-加载比赛数据
                    State2#beauty_contest{act_data = #{0 => load_act_data_local()}}
            end
    end.

%% 本服活动结束
end_local_act(State) ->
    %% 清理参赛人员/投票记录
    spawn(
        fun() ->
            db:execute(?SQL_BC_CLEAN_ACT_ROLE_LOCAL)
        end
    ),
    State1 = #beauty_contest{
        cls_type = ?NODE_GAME,
        round = State#beauty_contest.round,
        act_log = State#beauty_contest.act_log
    },
    broadcast_act_stage(State1),
    State1.

%% 游戏服每天0点检测是否达到开服天数，开启本服活动
%% 如果已经开启过本服活动，那就类似跨服，每周一开启活动流程
handle(zero_clock, State, ActData) ->
    NeedDay = ?BC_OPEN_TIME,
    OpenDay = util:get_open_day(),
    case OpenDay < NeedDay of
        true  ->
            %% 开服天数未达标
            {State, ActData};
        false ->
            [{_, Round}] = lib_server_kv:get_value([{?SERVER_KV_LOCAL_BC_ROUND, 0}]),
            case Round > 0 of
                true  ->
                    %% 已经开启过了。那就在周一的时候单独启动活动流程
                    Week = utime:day_of_week(),
                    case Week =:= 1 of
                        true  ->
                            State1 = init_new_act(State),
                            {State1, ActData};
                        false ->
                            %% 海选赛期间，广播一次活动状态
                            Week =:= 3 andalso notice_stage(State),
                            {State, ActData}
                    end;
                false ->
                    TopRoles  = mod_common_rank:get_fashion_rank_top_role(2),
                    case length(TopRoles) < 2 of
                        true  ->
                            %% 可参与人数不足
                            {State, ActData};
                        false ->
                            %% 开启活动
                            NewState = init_new_act(State),
                            %% 检查是否中途加入活动
                            case NewState#beauty_contest.stage =/= ?BC_STAGE_SIGN of
                                true  ->
                                    %% 中途参赛直接加载玩家
                                    handle({load_member, true}, NewState, ActData);
                                false ->
                                    {NewState, ActData}
                            end
                    end
            end
    end;

%% 淘汰赛开始，加载参加玩家
handle({load_member, Midway}, State, ActData) ->
    Members = load_members(),
    case beauty_contest:is_cluster_pattern(State) of
        true  ->
            beauty_contest:member_sync_to_center(Members),
            {State, ActData};
        false ->
            ActData1 = ActData#beauty_contest_act{members = Members},
            ActData2 = check_midway_members(?NODE_GAME, State#beauty_contest.stage, ActData1, Midway),
            {State, ActData2}
    end;

%% 判断参赛人员是否已经报名。如果未报名，会给玩家发一份报名奖励
handle({check_sign_reward, Target}, State, ActData) ->
    #beauty_contest{sign_roles = SignRoles} = State,
    #beauty_contest_act{members = Members} = ActData,
    case Target of
        all ->
            [
                check_is_sign(RoleId, SignRoles) || #beauty_contest_sign{role_id = RoleId} <- Members
            ];
        _ ->
            check_is_sign(Target, SignRoles)
    end,
    {State, ActData};

%% 清理报名数据
handle(clean_sign_data, State, ActData) ->
    catch db_clean_sign_info(),
    {State#beauty_contest{sign_roles = #{}}, ActData};

%% 活动状态
handle({get_act_state, Sid}, State, ActData) ->
    Bin = pack_act_state(State),
    lib_server_send:send_to_sid(Sid, Bin),
    {State, ActData};

%% 个人参赛信息-报名期间有效
handle({get_my_info, RoleId}, State, ActData) ->
    case State#beauty_contest.stage =:= ?BC_STAGE_SIGN of
        true  ->
            #beauty_contest_sign{
                contest_data = CData
            } = case maps:find(RoleId, State#beauty_contest.sign_roles) of
                    {ok, Role} ->
                        Role;
                    _ ->
                        default_role(RoleId)
                end,
            {ok, Bin} = pt_416:write(41602, [CData]),
            lib_server_send:send_to_uid(RoleId, Bin);
        false ->
            ignore
    end,
    {State, ActData};

%% 报名状态
handle({get_sign_state, RoleId}, State, ActData) ->
    #beauty_contest{
        stage         = Stage
        , sign_roles  = SignRoles
    } = State,
    if
        Stage =:= ?BC_STAGE_IDLE  ->
            IsSign = 0;
        Stage =:= ?BC_STAGE_SIGN  ->
            case maps:find(RoleId, SignRoles) of
                {ok, #beauty_contest_sign{sign_award = IsSign}} ->
                    IsSign;
                _ ->
                    IsSign = 0
            end;
        true ->
            %% 非报名阶段，默认都报名了
            IsSign = 1
    end,
    {ok, Bin} = pt_416:write(41601, [IsSign]),
    lib_server_send:send_to_uid(RoleId, Bin),
    {State, ActData};

%% 报名
handle({sign_and_award, RoleId, Name, FashionPower}, State, ActData) ->
    #beauty_contest{
        stage         = Stage
        , sign_roles    = SignRoles
    } = State,
    Res =
        if
            Stage =:= ?BC_STAGE_IDLE -> {false, ?ERRCODE(err416_act_close)};
            Stage =/= ?BC_STAGE_SIGN -> {false, ?ERRCODE(err416_not_in_sign_stage)};
            true  ->
                case maps:find(RoleId, SignRoles) of
                    {ok, Value} ->
                        case Value#beauty_contest_sign.sign_award > 0 of
                            true  ->
                                {false, ?ERRCODE(err416_already_sign)};
                            false ->
                                {true, Value}
                        end;
                    error ->
                        {true, default_role(RoleId)}
                end
        end,
    case Res of
        {false, Error} ->
            {ok, Bin} = pt_416:write(41603, [Error]),
            lib_server_send:send_to_uid(RoleId, Bin),
            {State, ActData};
        {true, RoleInfo} ->
            %% 领完奖励及时保存一次
            NewRoleInfo = RoleInfo#beauty_contest_sign{sign_award = 1},
            db_save_sign_one(NewRoleInfo),
            NewSignRoles = SignRoles#{RoleId => NewRoleInfo},
            NewState = State#beauty_contest{sign_roles = NewSignRoles},
            
            lib_goods_api:send_reward_by_id(?BC_SIGN_AWARD, bc_sign_award, RoleId),
            {ok, Bin} = pt_416:write(41603, [1]),
            lib_server_send:send_to_uid(RoleId, Bin),
            lib_log_api:log_beauty_contest_sign([RoleId, Name, FashionPower, utime:unixtime()]),
            %% 刷新一下前端状态
            handle({get_sign_state, RoleId}, NewState, ActData)
    end;

%% 调整外观搭配
handle({update_contest_data, RoleId, Data}, State, ActData) ->
    #beauty_contest{
        stage         = Stage
        , sign_roles    = SignRoles
    } = State,
    Res =
        if
            Stage =:= ?BC_STAGE_IDLE -> {false, ?ERRCODE(err416_act_close)};
            Stage =/= ?BC_STAGE_SIGN -> {false, ?ERRCODE(err416_not_in_sign_stage)};
            true  ->
                case maps:find(RoleId, SignRoles) of
                    {ok, Value} ->
                        {true, Value};
                    error ->
                        {true, #beauty_contest_sign{role_id = RoleId}}
                end
        end,
    case Res of
        {false, Error} ->
            {ok, Bin} = pt_416:write(41610, [Error]),
            lib_server_send:send_to_uid(RoleId, Bin),
            {State, ActData};
        {true, RoleInfo} ->
            NewRoleInfo = RoleInfo#beauty_contest_sign{contest_data = Data},
            db_save_sign_one(NewRoleInfo),
            
            NewSignRoles = SignRoles#{RoleId => NewRoleInfo},
            NewState = State#beauty_contest{sign_roles = NewSignRoles},
            {ok, Bin} = pt_416:write(41610, [1]),
            lib_server_send:send_to_uid(RoleId, Bin),
            {NewState, ActData}
    end;

%% 请求投票候选
handle({get_vote_candidate, RoleId}, State, ActData) ->
    #beauty_contest{
        stage = Stage,
        vote_candidates = VoteCandidate
    } = State,
    case maps:find(RoleId, VoteCandidate) of
        {ok, Candidates} ->
            %% 上次请求结果还未处理，还是用上次的结果
            Bin = beauty_contest_api:pack_vote_candidate(Candidates),
            lib_server_send:send_to_uid(RoleId, Bin),
            {State, ActData};
        _ ->
            %% 请求新的候选
            DailyVote = case Stage =:= ?BC_STAGE_TOP_4 of
                true  ->
                    lib_daily:get_count_offline(RoleId, ?MOD_BC, ?BC_COUNTER_DAILY_VOTE);
                false ->
                    0
            end,
            case beauty_contest:is_cluster_pattern(State) of
                false ->
                    %% 本服模式
                    beauty_contest:handle({re_get_vote_candidate, none, RoleId, DailyVote}, State, ActData);
                true  ->
                    %% 跨服模式
                    beauty_contest:re_get_vote_candidate(RoleId, DailyVote),
                    {State, ActData}
            end
    end;

%% 拿到新的投票候选后，记录，保证玩家重复打开候选界面，候选列表不变
handle({after_pick_out, RoleId, Picks}, State, ActData) ->
    #beauty_contest{
        vote_candidates = VoteCandidate
    } = State,
    NewState = State#beauty_contest{vote_candidates = VoteCandidate#{RoleId => Picks}},
    Bin = beauty_contest_api:pack_vote_candidate(Picks),
    lib_server_send:send_to_uid(RoleId, Bin),
    {NewState, ActData};

%% 清理本都投票缓存
handle(notify_recalc_zone, State, ActData) ->
    case beauty_contest:is_cluster_pattern(State) of
        true  ->
            {State#beauty_contest{vote_candidates = #{}}, ActData};
        false ->
            {State, ActData}
    end;

%% 投票
handle({vote, RoleId, VoteRole, DailyVote}, State, ActData) ->
    #beauty_contest{
        stage = Stage,
        stage_begin = StageBegin,
        stage_end = StageEnd,
        vote_candidates = VoteCandidate
    } = State,
    VoteLimit = ?BC_VOTE_COUNTS(Stage),
    Candidate = maps:get(RoleId, VoteCandidate, []),
    Now = utime:unixtime(),
    Res =
        if
            Stage =:= ?BC_STAGE_IDLE -> {false, ?ERRCODE(err416_act_close)};
            Stage =:= ?BC_STAGE_SIGN -> {false, ?ERRCODE(err416_not_in_vote_stage)};
            not (Now >= StageBegin andalso Now < StageEnd) -> {false, ?ERRCODE(err416_not_in_vote_stage)};
            DailyVote >= VoteLimit -> {false, ?ERRCODE(err416_daily_vote_limit)};
            Candidate == [] -> {false, ?ERRCODE(err416_vote_overdue)};
            true ->
                case get_vote_other(Candidate, VoteRole) of
                    false ->
                        {false, ?ERRCODE(err416_vote_error)};
                    Other ->
                        {true, Other}
                end
        end,
    case Res of
        {false, Err} ->
            {ok, Bin} = pt_416:write(41607, [Err, "", 0, []]),
            lib_server_send:send_to_uid(RoleId, Bin),
            {State, ActData};
        {true, Rival}  ->
            %% 检验通过，则把候选删除
            case Stage =:= ?BC_STAGE_TOP_2 of
                true  ->
                    %% 决赛的时候一次性把票投完
                    AddVote = VoteLimit,
                    lib_daily:set_count_offline(RoleId, ?MOD_BC, ?BC_COUNTER_DAILY_VOTE, VoteLimit);
                false ->
                    AddVote = 1,
                    lib_daily:increment_offline(RoleId, ?MOD_BC, ?BC_COUNTER_DAILY_VOTE)
            end,
            NewVoteCandidate = maps:remove(RoleId, VoteCandidate),
            NewState = State#beauty_contest{vote_candidates = NewVoteCandidate},
            case beauty_contest:is_cluster_pattern(State) of
                true  ->
                    %% 跨服模式，到跨服中心去投票
                    beauty_contest:vote_center(RoleId, VoteRole, Rival, AddVote),
                    {NewState, ActData};
                false ->
                    %% 本服模式，直接投票
                    beauty_contest:handle({vote, none, RoleId, VoteRole, Rival, AddVote}, NewState, ActData)
            end
    end;

%% 查看榜单
handle({get_rank_info, Sid}, State, ActData) ->
    case beauty_contest:is_cluster_pattern(State) of
        true  ->
            beauty_contest:get_rank_info_center(Sid);
        false ->
            beauty_contest:handle({get_rank_info, Sid}, State, ActData)
    end,
    {State, ActData};

%% 记录16强作为上期日志
handle({set_log, ActLogs, Source}, State, ActData) ->
    IsRecord =
        case Source of
            ?NODE_CENTER ->
                beauty_contest:is_cluster_pattern(State);
            ?NODE_GAME ->
                true
        end,
    %% 跨服来的记录，只有在跨服模式开的时候需要记录数据
    case IsRecord of
        true  ->
            spawn(
                fun() ->
                    db_clean_act_log(),
                    db_save_act_log_batch(ActLogs)
                end
            ),
            {State#beauty_contest{act_log = ActLogs}, ActData};
        false ->
            {State, ActData}
    end;

%% 上期排名
handle({get_log_info, Sid}, State, ActData) ->
    Info =
        [
            {ServerNum, Picture, CData, Stage, OriginRank, Rank}
            ||#beauty_contest_role{
                rank          = Rank
                , server_num    = ServerNum
                , picture       = Picture
                , stage         = Stage
                , contest_data  = CData
                , origin_rank   = OriginRank
            } <- State#beauty_contest.act_log
        ],
    {ok, Bin} = pt_416:write(41609, [Info]),
    lib_server_send:send_to_sid(Sid, Bin),
    {State, ActData};

%% 广播跨服冠军传闻
handle({notice_champion, ServerNum, Name}, State, ActData) ->
    case beauty_contest:is_cluster_pattern(State) of
        true  ->
            broad_cast_cluster_champion([ServerNum, Name]);
        false ->
            ignore
    end,
    {State, ActData};

%% 玩家数据更新
handle({update_role_info, RoleId, Key, Value}, State, ActData) ->
    case beauty_contest:is_cluster_pattern(State) of
        true  ->
            beauty_contest:update_role_info_center(RoleId, Key, Value),
            {State, ActData};
        false when State#beauty_contest.round > 0 ->
            beauty_contest:handle({update_role_info, RoleId, Key, Value}, State, ActData);
        _ ->
            %% 活动未开
            {State, ActData}
    end;

handle(_Msg, State, ActData) ->
    {State, ActData}.
%%%-------------------------------------------------------------------
%%% @doc
%%%     本服活动流程
%%% @end
%%%-------------------------------------------------------------------
%% 开启一轮新的活动
init_new_act(State) ->
    NextRound = State#beauty_contest.round + 1,
    record_new_round(NextRound),
    init_local_stage(State#beauty_contest{round = NextRound}).

%% 记录新的一轮活动
record_new_round(NextRound) ->
    ServerKv = #server_kv{key = ?SERVER_KV_LOCAL_BC_ROUND, value = NextRound, time = utime:unixtime()},
    lib_server_kv:update_server_kv(ServerKv).

init_local_stage(State) ->
    State1 = beauty_contest:init_act_stage(State),
    broadcast_act_stage(State1),
    State1.

%% 广播活动状态
broadcast_act_stage(#beauty_contest{cls_type = ?NODE_GAME} = State) ->
    notice_stage(State),
    case State#beauty_contest.stage of
        ?BC_STAGE_SIGN ->
            lib_chat:send_TV({all}, ?MOD_BC, 1, []);
        ?BC_STAGE_PRE ->
            lib_chat:send_TV({all}, ?MOD_BC, 2, []);
        ?BC_STAGE_TOP_16 ->
            lib_chat:send_TV({all}, ?MOD_BC, 3, []);
        ?BC_STAGE_TOP_8 ->
            lib_chat:send_TV({all}, ?MOD_BC, 4, []);
        ?BC_STAGE_TOP_4 ->
            lib_chat:send_TV({all}, ?MOD_BC, 5, []);
        ?BC_STAGE_TOP_2 ->
            lib_chat:send_TV({all}, ?MOD_BC, 6, []);
        _ ->
            ok
    end;
broadcast_act_stage(_) -> ok.

broad_cast_cluster_champion(Arg) ->
    lib_chat:send_TV({all}, ?MOD_BC, 7, Arg).

%% 广播冠军
broad_cast_local_champion(Arg) ->
    lib_chat:send_TV({all}, ?MOD_BC, 8, Arg).

%% 前端刷新一下阶段
notice_stage(State) ->
    Bin = pack_act_state(State),
    lib_server_send:send_to_all(Bin).

%%%-------------------------------------------------------------------
%%% @doc
%%%     从排行榜那里加载参赛玩家
%%% @end
%%%-------------------------------------------------------------------
load_members() ->
    %% 加载装扮排行榜前N位
    N = ?BC_RANK_LENGTH,
    TopRoles  = mod_common_rank:get_fashion_rank_top_role(N),
    ServerId  = config:get_server_id(),
    ServerNum = config:get_server_num(),
    ZoneId    = czone_api:env_get_zone_id(),
    lists:foldl(
        fun({RoleId, FashionNum, FashionPower}, Temp) ->
            try
                Figure = lib_role:get_role_figure(RoleId),
                Picture = lib_player:trans_to_picture(Figure),
                ContestData = beauty_contest_api:statistics_contest_data(Figure),
                Role = #beauty_contest_role{
                    role_id         = RoleId
                    , zone_id       = ZoneId
                    , server_id     = ServerId
                    , server_num    = ServerNum
                    , picture       = Picture#rec_picture{power = FashionPower}  %% 用装扮战力替换玩家战力
                    , contest_data  = ContestData
                    , fashion_num   = FashionNum
                },
                [Role|Temp]
            catch Class:Reason:Stacktrace ->
                ?ERR("~p~n,~p~n,~p", [Class, Reason, Stacktrace]),
                Temp
            end
        end, [], TopRoles
    ).

%%%-------------------------------------------------------------------
%%% @doc
%%%     邮件发送报名奖励：进入海选赛的未报名的玩家
%%% @end
%%%-------------------------------------------------------------------
check_is_sign(RoleId, SignRoles) ->
    case maps:find(RoleId, SignRoles) of
        {ok, #beauty_contest_sign{sign_award = SignAward}} ->
            case SignAward =:= 1 of
                true  ->
                    ignore;
                false ->
                    send_sign_award_by_mail(RoleId)
            end;
        _ ->
            send_sign_award_by_mail(RoleId)
    end.

send_sign_award_by_mail(RoleId) ->
    Title = utext:get(4160007),
    Content = utext:get(4160008),
    Award = ?BC_SIGN_AWARD,
    lib_mail_api:send_sys_mail([RoleId], Title, Content, Award).

%%%-------------------------------------------------------------------
%%% @doc
%%%    中途参赛的玩家处理:如果是中途参赛的玩家，那么模拟一遍结算流程
%%% @end
%%%-------------------------------------------------------------------
%% 只有淘汰赛期间需要模拟
check_midway_members(ClsType, Stage, ActData, true)
    when Stage >= ?BC_STAGE_TOP_16 andalso Stage =< ?BC_STAGE_TOP_2 ->
    beauty_contest:simulate_stage(ClsType, Stage, ActData, ?BC_STAGE_PRE);
check_midway_members(_ClsType, _Stage, ActData, _) ->
    ActData.


%%%-------------------------------------------------------------------
%%% @doc
%%%     本服数据处理
%%% @end
%%%-------------------------------------------------------------------
%% 加载比赛数据
load_act_data_local() ->
    ALL = db:get_all(?SQL_BC_LOAD_ACT_ROLE_LOCAL),
    ServerNum = config:get_server_num(),
    Members = lists:foldl(
        fun([RoleId, ContestDataBin, Vote, PickCounts, Time, Stage, Rank, OriginRank], Temp) ->
            Role = #beauty_contest_role{
                role_id         = RoleId
                , contest_data  = util:bitstring_to_term(ContestDataBin)
                , vote          = Vote
                , pick_counts   = PickCounts
                , stage         = Stage
                , rank          = Rank
                , time          = Time
                , origin_rank   = OriginRank
                , picture       = lib_player:get_role_picture(RoleId)
                , server_num    = ServerNum
            },
            [Role|Temp]
        end, [], ALL
    ),
    #beauty_contest_act{members = Members}.

%% 批量保存参赛数据
db_save_act_role_batch(AllUpdateList) ->
    SqlList =
        [
            io_lib:format(<<"(~p,'~ts',~p,~p,~p,~p,~p,~p)">>,
                [RoleId, util:term_to_bitstring(CData), Vote, PickCounts, Time, Stage, Rank, OriginRank])
            || #beauty_contest_role{
            role_id = RoleId
            , contest_data  = CData
            , vote          = Vote
            , pick_counts   = PickCounts
            , stage         = Stage
            , rank          = Rank
            , time          = Time
            , origin_rank   = OriginRank
        } <- AllUpdateList
        ],
    case SqlList =/= [] of
        true  ->
            SQL = io_lib:format(?SQL_BC_SAVE_ACT_ROLE_BATCH_LOCAL, [string:join(SqlList, ",")]),
            db:execute(SQL);
        false ->
            ok
    end.

%% 加载上期日志
load_act_log() ->
    ALL = db:get_all(?SQL_BC_LOAD_ACT_LOG),
    lists:foldl(
        fun([Rank, ServerNum, Name, Lv, Sex, Vip, SupVip, Career, Turn, Picture, PicDress, ContestDataBin, OriginRank, Stage], Temp) ->
            Role = #beauty_contest_role{
                rank         = Rank
                , server_num    = ServerNum
                , picture       = #rec_picture{
                    name          = Name
                    , lv            = Lv
                    , sex           = Sex
                    , vip           = Vip
                    , sup_vip       = SupVip
                    , career        = Career
                    , turn          = Turn
                    , profile_photo_id = Picture
                    , picture_frame = PicDress
                }
                , contest_data  = util:bitstring_to_term(ContestDataBin)
                , origin_rank = OriginRank
                , stage = Stage
            },
            [Role|Temp]
        end, [], ALL
    ).

%% 清理上期日志
db_clean_act_log() ->
    db:execute(?SQL_BC_CLEAN_ACT_LOG).

%% 批量保存参赛日志
db_save_act_log_batch(LogRoles) ->
    SqlList =
        [
            io_lib:format(<<"(~p,~p,'~ts',~p,~p,~p,~p,~p,~p,~p,~p,'~ts',~p,~p)">>,
                [Rank, ServerNum, Name, Lv, Sex, Vip, SupVip, Career, Turn, Picture, PicDress, util:term_to_bitstring(CData), OriginRank, Stage])
            || #beauty_contest_role{
                server_num    = ServerNum
                , picture       = #rec_picture{
                    name          = Name
                    , sex           = Sex
                    , vip           = Vip
                    , sup_vip       = SupVip
                    , career        = Career
                    , turn          = Turn
                    , profile_photo_id = Picture
                    , picture_frame = PicDress
                    , lv            = Lv
                }
                , contest_data  = CData
                , rank = Rank
                , origin_rank = OriginRank
                , stage = Stage
            } <- LogRoles
        ],
    case SqlList =/= [] of
        true  ->
            SQL = io_lib:format(?SQL_BC_SAVE_ACT_LOG, [string:join(SqlList, ",")]),
            db:execute(SQL);
        false ->
            ok
    end.

%% 清空报名信息
db_clean_sign_info() ->
    db:execute(?SQL_BC_CLEAN_SIGN).

%% 保存一条报名数据
db_save_sign_one(SignRole) ->
    #beauty_contest_sign{
        role_id = RoleId
        , contest_data  = CData
        , sign_award = SignAward
    } = SignRole,
    SQL = io_lib:format(?SQL_BC_SAVE_SIGN_ONE, [RoleId, util:term_to_bitstring(CData), SignAward]),
    db:execute(SQL).

%%%-------------------------------------------------------------------
%%% @doc
%%%     internal function
%%% @end
%%%-------------------------------------------------------------------
%%% 找出未投票的另外一位
get_vote_other([#beauty_contest_role{role_id = VoteRole}, #beauty_contest_role{role_id = Other}], VoteRole) -> Other;
get_vote_other([#beauty_contest_role{role_id = Other}, #beauty_contest_role{role_id = VoteRole}], VoteRole) -> Other;
get_vote_other(_, _) -> false.

%% 构造一份默认数据
default_role(RoleId) ->
    #beauty_contest_sign{
        role_id = RoleId
        , contest_data  = beauty_contest_api:statistics_contest_data(RoleId)
    }.

%% 打包活动状态
pack_act_state(State) ->
    #beauty_contest{
        round = Round
        , begin_time    = BeginTime
        , end_time      = EndTime
        , stage         = Stage
        , stage_begin   = StageBegin
        , stage_end     = StageEnd
    } = State,
    {ok, Bin} = pt_416:write(41600, [Round, BeginTime, EndTime, Stage, StageBegin, StageEnd]),
    Bin.