%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     靓丽大赛跨服逻辑
%%% @end
%%%-------------------------------------------------------------------
-module(beauty_contest_center).
-author("liushl").

-include("beauty_contest.hrl").
-include("common.hrl").
-include("def_fun.hrl").
-include("figure.hrl").

-export([
    handle/3                        %% 跨服逻辑处理
    , init_center_act/1             %% 跨服中心初始化
    , end_center_act/1              %% 跨服模式活动结束
    , db_save_act_role_batch/1      %% 批量保存参赛数据
    , merge_zone_data/3             %% 合并两个分区的数据
]).

%% 跨服中心初始化
init_center_act(State) ->
    %% 跨服中心，保持活动常开，先初始化当前活动阶段
    State1 = beauty_contest:init_act_stage(State),
    case State1#beauty_contest.stage >= ?BC_STAGE_PRE of
        true  ->
            %% 比赛期间则加载参赛数据
            ActData = load_act_roles(),
            State1#beauty_contest{act_data = ActData};
        false ->
            State1
    end.

%% 跨服模式活动结束
end_center_act(_State) ->
    %% 清理参赛人员
    spawn(
        fun() ->
            db:execute(?SQL_BC_CLEAN_ACT_ROLE)
        end
    ),
    #beauty_contest{
        cls_type = ?NODE_CENTER
    }.

%% 收到游戏服发来的参赛玩家:合并取前几个
handle({sync_member, UploadRoles}, State, ActData) ->
    #beauty_contest_act{
        members = Members
    } = ActData,
    Members1 = UploadRoles ++ Members,
    Members2 = lists:sort(fun beauty_contest:compare_fashion_func/2, Members1),
    N = ?BC_RANK_LENGTH,
    Members3 = lists:sublist(Members2, N),
    NewActData = ActData#beauty_contest_act{members = Members3},
    {State, NewActData};

%% 游戏服合服处理
handle({merge, ServerId, ServerNum, ServerList}, State, ActData)
    when State#beauty_contest.stage >= ?BC_STAGE_PRE ->
    NewMembers =
        [
            case lists:member(One#beauty_contest_role.server_id, ServerList) of
                true  ->
                    One#beauty_contest_role{server_id = ServerId, server_num = ServerNum, update = true};
                false ->
                    One
            end
            || One <- ActData#beauty_contest_act.members
        ],
    {State, ActData#beauty_contest_act{members = NewMembers}};

handle(_Msg, State, ActData) ->
    {State, ActData}.

%%%-------------------------------------------------------------------
%%% @doc
%%%     数据库数据处理
%%% @end
%%%-------------------------------------------------------------------
%% 加载参赛人员
load_act_roles() ->
    ALL = db:get_all(?SQL_BC_LOAD_ACT_ROLE),
    lists:foldl(
        fun([RoleId, ZoneId, ServerId, ServerNum, Name, Lv, Sex, Vip, SupVip, Career, Turn, Picture, PicDress, ContestDataBin, Vote, PickCounts, Time, Stage, Rank, OriginRank, FashionNum], Temp) ->
            Role = #beauty_contest_role{
                role_id         = RoleId
                , zone_id       = ZoneId
                , server_id     = ServerId
                , 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  = util:bitstring_to_term(ContestDataBin)
                , vote          = Vote
                , pick_counts   = PickCounts
                , stage         = Stage
                , rank          = Rank
                , time          = Time
                , origin_rank   = OriginRank
                , fashion_num   = FashionNum
            },
            #beauty_contest_act{members = Members} =
                ZoneData = maps:get(ZoneId, Temp, #beauty_contest_act{zone_id = ZoneId}),
            NewZoneData = ZoneData#beauty_contest_act{members = [Role|Members]},
            maps:put(ZoneId, NewZoneData, Temp)
        end, #{}, ALL
    ).

%% 批量保存参赛数据
db_save_act_role_batch(AllUpdateList) ->
    SqlList =
        [
            io_lib:format(<<"(~p,~p,~p,~p,'~ts',~p,~p,~p,~p,~p,~p,~p,~p,'~ts',~p,~p,~p,~p,~p,~p,~p)">>,
                [RoleId, ZoneId, ServerId, ServerNum, Name, Lv, Sex, Vip, SupVip, Career, Turn, Picture, PicDress,
                    util:term_to_bitstring(CData), Vote, PickCounts, Time, Stage, Rank, OriginRank, FashionNum])
            || #beauty_contest_role{
            role_id = RoleId
            , zone_id       = ZoneId
            , server_id     = ServerId
            , 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
            , vote          = Vote
            , pick_counts   = PickCounts
            , stage         = Stage
            , rank          = Rank
            , time          = Time
            , origin_rank   = OriginRank
            , fashion_num   = FashionNum
        } <- AllUpdateList
        ],
    case SqlList =/= [] of
        true  ->
            SQL = io_lib:format(?SQL_BC_SAVE_ACT_ROLE_BATCH, [string:join(SqlList, ",")]),
            db:execute(SQL);
        false ->
            ok
    end.
%%%-------------------------------------------------------------------
%%% @doc
%%%     分区合并处理
%%% @end
%%%-------------------------------------------------------------------
%% 合并两个分区的数据
merge_zone_data(State, OldZoneId, NewZoneId) ->
    #beauty_contest{act_data = ActData, stage = Stage} = State,
    case maps:find(OldZoneId, ActData) of
        {ok, #beauty_contest_act{members = OldMembers} = OldZoneData} ->
            case maps:find(NewZoneId, ActData) of
                {ok, #beauty_contest_act{members = NewMembers} = ZoneData} ->
                    %% 直接合并，排序，
                    MergeMembers = OldMembers ++ NewMembers,
                    %% 海选赛，按时装数量排序/淘汰赛，按票数排序
                    SortMembers =
                    case Stage =:= ?BC_STAGE_PRE of
                        true  ->
                            lists:sort(fun beauty_contest:compare_fashion_func/2, MergeMembers);
                        false ->
                            lists:sort(fun beauty_contest:compare_vote_func/2, MergeMembers)
                    end,
                    %% 取前N个
                    N = ?BC_RANK_LENGTH,
                    CutMembers = lists:sublist(SortMembers, N),
                    %% 将玩家数据重置
                    ResetMembers =
                        [
                            One#beauty_contest_role{
                                zone_id = NewZoneId,
                                origin_rank = 0,
                                rank = 0,
                                stage = ?BC_STAGE_PRE,
                                update = true
                            }|| One <- CutMembers
                        ],
                    ZoneData1 = ZoneData#beauty_contest_act{members = ResetMembers},
                    %% 清除两个分区的数据，定时流程会写入新的数据
                    SQL = io_lib:format(?SQL_BC_DELETE_ACT_ROLE, [string:join([integer_to_list(OldZoneId), integer_to_list(NewZoneId)], ",")]),
                    spawn(fun() -> db:execute(SQL) end),
                    %% 模拟流程带当前阶段
                    NewZoneData = beauty_contest:simulate_stage(?NODE_CENTER, Stage, ZoneData1, ?BC_STAGE_PRE);
                _ ->
                    %% 新的分区没有数据，那就只需要改变玩家分区数据即可
                    MergeMembers =
                        [
                            One#beauty_contest_role{
                                zone_id = NewZoneId,
                                update = true
                            }|| One <- OldMembers
                        ],
                    NewZoneData = OldZoneData#beauty_contest_act{
                        zone_id = NewZoneId,
                        members = MergeMembers
                    }
            end,
            %% 清除旧分区的数据
            ActData1 = maps:remove(OldZoneId, ActData),
            ActData2 = ActData1#{NewZoneId => NewZoneData},
            ?INFO("BC zone ~p merge to new zone ~p", [OldZoneId, NewZoneId]),
            State#beauty_contest{act_data = ActData2};
        _ ->
            %% 旧分区没有数据，那就什么都不用管
            State
    end.
