%%-----------------------------------------------------------------------------
%% @Module  :       lib_top_pk.erl
%% @Author  :       J
%% @Email   :       j-som@foxmail.com
%% @Created :       2017-12-05
%% @Description:    巅峰竞技
%%-----------------------------------------------------------------------------

-module(lib_top_pk).
-include("top_pk.hrl").
-include("server.hrl").
-include("language.hrl").
-include("def_module.hrl").
-include("errcode.hrl").
-include("def_event.hrl").
-include("rec_event.hrl").
-include("predefine.hrl").
-include("attr.hrl").
-include("goods.hrl").
-include("common.hrl").
-include("figure.hrl").
-include("skill.hrl").
-include("guild.hrl").
-include("def_fun.hrl").
-include("scene.hrl").
-include("battle_field.hrl").

-export([
    handle_event/2,             %% 更新排行榜信息
    login/1,                    %% 玩家登录
    daily_reset/1,              %% 每日重置
    start_new_season/2,         %% 通知在线玩家开启新赛季
    set_player_match_start/2,   %% 开始匹配
    match_break/2,              %% 下线处理 从匹配队列中移除
    cancel_match/1,             %% 取消匹配状态
    battle_field_create_done/4, %% 创建战场成功对玩家进程的处理
    battle_field_close/2,       %% 战场关闭
    calc_battle_result/3,       %% 计算挑战结果
    reconnect/2,                %% 重连
    send_match_reward/2,        %% 领取每日参与次数奖励
    send_grade_reward/2         %% 领取段位奖励
]).

%% 更新排行榜职业、姓名信息
handle_event(PS, #event_callback{type_id = ?EVENT_RENAME}) when is_record(PS, player_status) ->
    Figure = PS#player_status.figure,
    mod_top_pk_local:apply_cast({'update_role', PS#player_status.id, [{role_name, Figure#figure.name}]}),
    {ok, PS};
handle_event(PS, _) -> {ok, PS}.

%% 玩家登录
login(PS) ->
    case lib_top_pk_util:db_select_player_top_pk(PS#player_status.id) of
        [] -> TopPkStatus = #top_pk_status{score = ?TOP_PK_KV_INIT_SCORE};
        TopPkStatus -> skip
    end,
    NewPS = PS#player_status{top_pk = TopPkStatus},
    LastPS = daily_reset(NewPS),
    LastPS.

%% 每日重置
daily_reset(PS) ->
    NowTime = utime:unixtime(),
    #player_status{id = _PlayerId, top_pk = TopPkStatus} = PS,
    #top_pk_status{pk_time = PkTime} = TopPkStatus,
    case {PkTime>0, utime:is_same_day(PkTime, NowTime)} of
        {true, false} ->
            NewTopPkStatus = TopPkStatus#top_pk_status{
                match_reward_list = [], continued_win = 0, continued_fail = 0,
                enter_count = 0},
            PS#player_status{top_pk = NewTopPkStatus};
        _ -> PS
    end.

%% 通知在线玩家开启新赛季
start_new_season(PS, _CurSeasonId) ->
    TopPkStatus = #top_pk_status{score = ?TOP_PK_KV_INIT_SCORE},
    NewPS = PS#player_status{top_pk = TopPkStatus},
    {ok, NewPS}.

 %% 开始匹配
set_player_match_start(Player, Node) ->
    #player_status{action_lock = ActionLock, top_pk = PKStatus, id = RoleId} = Player,
    Lock = ?ERRCODE(err281_on_matching_state),
    if
        ActionLock =:= free ->
            lib_player_event:add_listener(?EVENT_DISCONNECT, ?MODULE, match_break, []),
            lib_player_event:add_listener(?EVENT_PLAYER_DIE, ?MODULE, match_break, []),
            {ok, lib_player:setup_action_lock(Player#player_status{top_pk = PKStatus#top_pk_status{pk_state = {match, Node}}}, Lock)};
        true ->
            if
                ActionLock =:= Lock ->
                    ok;
                true ->
                    MyNode = config:get_cls_node(),
                    lib_top_pk_util:apply_cast(Node, mod_top_pk_match, cancel_match, [MyNode, RoleId]),
                    {ok, Player#player_status{top_pk = PKStatus#top_pk_status{pk_state = []}}}
            end
    end.

%% 下线处理 从匹配队列中移除
match_break(Player, #event_callback{type_id = Type}) when is_record(Player, player_status), Type =:= ?EVENT_DISCONNECT orelse Type =:= ?EVENT_PLAYER_DIE ->
    PKStatus = Player#player_status.top_pk,
    case PKStatus of
        #top_pk_status{pk_state = {match, Node}} ->
            lib_player_event:remove_listener(?EVENT_DISCONNECT, ?MODULE, match_break),
            lib_player_event:remove_listener(?EVENT_PLAYER_DIE, ?MODULE, match_break),
            MyNode = config:get_cls_node(),
            lib_top_pk_util:apply_cast(Node, mod_top_pk_match, cancel_match, [MyNode, Player#player_status.id]),
            {ok, lib_player:break_action_lock(Player#player_status{top_pk = PKStatus#top_pk_status{pk_state = []}}, ?ERRCODE(err281_on_matching_state))};
        _ ->
            {ok, Player}
    end;
match_break(Player, _) ->
    {ok, Player}.

%% 取消匹配状态
cancel_match(PS) ->
    TopPkStatus = PS#player_status.top_pk,
    lib_player_event:remove_listener(?EVENT_DISCONNECT, ?MODULE, match_break),
    lib_player_event:remove_listener(?EVENT_PLAYER_DIE, ?MODULE, match_break),
    NewPS = PS#player_status{top_pk = TopPkStatus#top_pk_status{pk_state = []}},
    LastPS = lib_player:break_action_lock(NewPS, ?ERRCODE(err281_on_matching_state)),
    {ok, LastPS}.

%% 创建战场成功对玩家进程的处理
battle_field_create_done(Player0, BattleNode, BattlePid, RoleKey) ->
    lib_player_event:remove_listener(?EVENT_DISCONNECT, ?MODULE, match_break),
    lib_player_event:remove_listener(?EVENT_PLAYER_DIE, ?MODULE, match_break),
    Player = lib_player:break_action_lock(Player0, ?ERRCODE(err281_on_matching_state)),
    #player_status{
        sid = Sid,
        top_pk = PKStatus,
        % id = RoleId,
        scene = OSceneId,
        scene_pool_id = OPoolId,
        copy_id = OCopyId,
        x = X,
        y = Y,
        battle_attr = #battle_attr{hp = Hp, hp_lim = HpLim}
    } = Player,
    {ok, BinData} = pt_281:write(28111, [?SUCCESS]),
    lib_server_send:send_to_sid(Sid, BinData),
    Lock = ?ERRCODE(err281_on_battle_state),
    Args = [OSceneId, OPoolId, OCopyId, X, Y, Hp, HpLim],
    lib_top_pk_util:apply_cast(BattleNode, mod_battle_field, player_enter, [BattlePid, RoleKey, Args]),
    NewPlayer = lib_player:setup_action_lock(Player#player_status{top_pk = PKStatus#top_pk_status{pk_state = {battle, BattleNode, BattlePid}}}, Lock),
    {ok, NewPlayer}.

%% 战场关闭
battle_field_close(Player, Resaon) ->
    lib_player_event:remove_listener(?EVENT_DISCONNECT, ?MODULE, match_break),
    lib_player_event:remove_listener(?EVENT_PLAYER_DIE, ?MODULE, match_break),
    #player_status{sid = Sid} = Player,
    case Resaon of
        init_error ->
            {ok, BinData} = pt_281:write(28111, [?FAIL]),
            lib_server_send:send_to_sid(Sid, BinData),
            TmpPlayer = Player;
        _ ->
            TmpPlayer = Player
    end,
    {ok, lib_player:break_action_lock(TmpPlayer, ?ERRCODE(err281_on_matching_state))}.

%% 重连
reconnect(PS, _LoginType) ->
    SceneId = PS#player_status.scene,
    [Type, _Subtype, _ClsType] = lib_scene:get_scene_type(SceneId),
    if
        Type == ?SCENE_TYPE_TOPPK -> do_reconnect(PS);
        true -> {next, PS}
    end.

do_reconnect(PS) ->
    case ets:lookup(?ETS_ROLE_BATTLE_PID, PS#player_status.id) of
        [#role_battle_pid{pid = BattlePid}] -> skip;
        _ -> BattlePid = none
    end,
    RoleKey = {node(), PS#player_status.id},
    case misc:is_process_alive(BattlePid) of
        true ->
            Args = [],
            BattleNode = node(BattlePid),
            lib_top_pk_util:apply_cast(BattleNode, mod_battle_field, player_reconnect, [BattlePid, RoleKey, Args]),
            {ok, PS};
        _ -> {next, PS}
    end.

%% 计算挑战结果
calc_battle_result(RoleId, Res, OtherData) when is_integer(RoleId) ->
    lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?HAND_OFFLINE, ?MODULE, calc_battle_result, [Res, OtherData]);

calc_battle_result(Player, Res, [OtherData, QuitTime]) when is_record(Player, player_status) ->
    NowTime = utime:unixtime(),
    case OtherData of
        [OtherRoleId, OtherPlatform, OtherServerNum, OtherRoleName] -> IsFake = 0;
        _ -> IsFake = 1, OtherRoleId = 0, OtherPlatform = "", OtherServerNum = 0, OtherRoleName = ""
    end,
    #player_status{
        id = RoleId, sid = Sid, figure = Figure, platform = Platform, server_num = ServerNum,
        server_id = ServerId, hightest_combat_power = Power, top_pk = TopPkStatus} = Player,
    #figure{name = RoleName, career = Career} = Figure,
    #top_pk_status{score = OldScore, continued_fail = ContinuedFail, continued_win = ContinuedWin, enter_count = EnterCount} = TopPkStatus,

    InitOldGradeInfo = data_top_pk:get_grade_info(OldScore),
    #base_top_pk_grade_info{win_score = WinScore, lose_score = LoseScore} = ?IF(InitOldGradeInfo =/= [], InitOldGradeInfo, #base_top_pk_grade_info{}),
    AddScore = ?IF(Res == 0, LoseScore, WinScore),
    NewScore = OldScore + AddScore,
    NewContinuedFail = ?IF(Res == 0 andalso IsFake == 0, ContinuedFail + 1, 0),
    NewContinuedWin = ?IF(Res == 0, 0, ContinuedWin + 1),
    NewTopPkStatus = TopPkStatus#top_pk_status{
        score = NewScore, continued_fail = NewContinuedFail, continued_win = NewContinuedWin, pk_time = NowTime, enter_count = EnterCount + 1},
    lib_top_pk_util:db_replace_into_player_top_pk(RoleId, NewTopPkStatus),
    NewPlayer = Player#player_status{top_pk = NewTopPkStatus},
    mod_hi_point:success_end(RoleId, ?MOD_TOPPK, 0),
    lib_activitycalen_api:role_success_end_activity(RoleId, ?MOD_TOPPK, 0),
    if
        is_pid(Sid) ->
            {ok, BinData} = pt_281:write(28113, [Res, AddScore]),
            lib_server_send:send_to_sid(Sid, BinData),
            {ok, BinData1} = pt_281:write(28112, [3, QuitTime]),
            lib_server_send:send_to_sid(Sid, BinData1);
        true ->
            ok
    end,
    lib_log_api:log_top_pk(RoleId, OtherRoleId, OtherRoleName, IsFake, OtherPlatform, OtherServerNum, Res, OldScore, NewScore),
    RankRole = #rank_role{
        role_id = RoleId, role_name = RoleName, career = Career, power = Power, platform = Platform,
        server_num = ServerNum, server_id = ServerId, score = NewScore, pk_time = NowTime},
    mod_top_pk_local:apply_cast({'refresh_rank', RankRole}),
    send_result_tv(Figure, OldScore, NewScore, NewContinuedWin),

    InitGradeInfo = data_top_pk:get_grade_info(NewScore),
    #base_top_pk_grade_info{grade_num = NewGradeNum, stage = _NewStage}
            =?IF(InitGradeInfo =/= [], InitGradeInfo, #base_top_pk_grade_info{}),
    lib_achievement_api:top_pk_win_event(NewPlayer, NewGradeNum),
    % NewPlayer1
    % = if
    %     Res =:= 1 ->
    %         if
    %             GradeNum1 > GradeNum0 ->
    %                 lib_server_send:send_to_sid(Sid, pt_281, 28117, [GradeNum0, GradeNum1]);
    %             true ->
    %                 ok
    %         end,
    %         {ok, TmpPlayer} = lib_achievement_api:top_pk_win_event(NewPlayer, GradeNum1),
    %         TmpPlayer;
    %         % lib_player_event:dispatch(NewPlayer, ?EVENT_TOP_PK_WIN, GradeNum1);
    %     true ->
    %         NewPlayer
    % end,
    {ok, NewPlayer}.

%% 连胜传闻、段位传闻
send_result_tv(Figure, OldScore, NewScore, ContinuedWin) ->
    InitOldGradeInfo = data_top_pk:get_grade_info(OldScore),
    OldGradeInfo =?IF(InitOldGradeInfo =/= [], InitOldGradeInfo, #base_top_pk_grade_info{}),
    #base_top_pk_grade_info{grade_num = OldGradeNum, stage = OldStage} = OldGradeInfo,
    InitNewGradeInfo = data_top_pk:get_grade_info(NewScore),
    NewGradeInfo = ?IF(InitNewGradeInfo =/= [], InitNewGradeInfo, #base_top_pk_grade_info{}),
    #base_top_pk_grade_info{grade_num = NewGradeNum, stage = NewStage} = NewGradeInfo,

    F1 = fun({WinCount, TvId}) when WinCount == ContinuedWin->
            lib_chat:send_TV({all}, ?MOD_TOPPK, TvId, [Figure#figure.name, WinCount]);
        (_) -> skip
    end,
    lists:foreach(F1, ?TOP_PK_KV_CONTINUED_WIN_TV),
    F2 = fun({Grade, Stage, TvId}) when {NewGradeNum, NewStage} >= {Grade, Stage}
                andalso {OldGradeNum, OldStage} < {Grade, Stage} ->
            lib_chat:send_TV({all}, ?MOD_TOPPK, TvId, [Figure#figure.name]);
        (_) -> skip
    end,
    lists:foreach(F2, ?TOP_PK_KV_NEW_GRADE_TV),
    ok.

%% 领取每日参与次数奖励
send_match_reward(PS, MatchCount) ->
    #player_status{top_pk = TopPkStatus} = PS,
    #top_pk_status{match_reward_list = MatchRewardList, enter_count = EnterCount} = TopPkStatus,
    SeasonWorldLv = lib_top_pk_util:get_season_world_lv(),
    RewardList = data_top_pk:get_match_reward(SeasonWorldLv, MatchCount),
    IsGet = lists:member(MatchCount, MatchRewardList),
    CanGive = lib_goods_api:can_give_goods(PS, lib_goods_api:make_reward_unique(RewardList)),
    if
        EnterCount < MatchCount -> {false, ?ERRCODE(err281_daily_enter_count_error)};
        RewardList == [] -> {false, ?ERRCODE(missing_config)};
        IsGet =:= true -> {false, ?ERRCODE(err281_award_is_got)};
        CanGive =:=false -> {false, ?ERRCODE(err150_no_cell)};
        true ->
            NewMatchRewardList = [MatchCount|MatchRewardList],
            NewTopPkStatus = TopPkStatus#top_pk_status{match_reward_list = NewMatchRewardList},
            lib_top_pk_util:db_replace_into_player_top_pk(PS#player_status.id, NewTopPkStatus),
            NewPS = PS#player_status{top_pk = NewTopPkStatus},
            Produce = #produce{
                    type = top_pk_daily_count_reward, reward = RewardList,
                    remark = integer_to_list(MatchCount), show_tips = ?SHOW_TIPS_3},
            LastPS = lib_goods_api:send_reward(NewPS, Produce),
            {true, LastPS, RewardList}
    end.

%% 领取赛季段位奖励
send_grade_reward(PS, GradeNum) ->
    #player_status{top_pk = TopPkStatus} = PS,
    #top_pk_status{score = Score, grade_reward_list = GradeRewardList} = TopPkStatus,
    InitScore = ?TOP_PK_KV_INIT_SCORE,
    InitGradeInfo = data_top_pk:get_grade_info(Score),
    GradeInfo = ?IF(InitGradeInfo =/= [], InitGradeInfo, #base_top_pk_grade_info{}),
    SeasonWorldLv = lib_top_pk_util:get_season_world_lv(),
    RewardList = data_top_pk:get_grade_reward(SeasonWorldLv, GradeNum),
    IsGet = lists:member(GradeNum, GradeRewardList),
    CanGive = lib_goods_api:can_give_goods(PS, lib_goods_api:make_reward_unique(RewardList)),
    if
        InitGradeInfo == [] -> {false, ?ERRCODE(missing_config)};
        RewardList == [] -> {false, ?ERRCODE(missing_config)};
        GradeInfo#base_top_pk_grade_info.grade_num < GradeNum
            orelse Score=<InitScore -> {false, ?ERRCODE(err281_grade_limit)};
        IsGet =:= true -> {false, ?ERRCODE(err281_award_is_got)};
        CanGive =:=false -> {false, ?ERRCODE(err150_no_cell)};
        true ->
            NewGradeRewardList = [GradeNum|GradeRewardList],
            NewTopPkStatus = TopPkStatus#top_pk_status{grade_reward_list = NewGradeRewardList},
            lib_top_pk_util:db_replace_into_player_top_pk(PS#player_status.id, NewTopPkStatus),
            NewPS = PS#player_status{top_pk = NewTopPkStatus},
            Produce = #produce{
                    type = top_pk_grade_reward, reward = RewardList,
                    remark = integer_to_list(GradeNum), show_tips = ?SHOW_TIPS_3},
            LastPS = lib_goods_api:send_reward(NewPS, Produce),
            {true, LastPS, RewardList}
    end.
