%%%------------------------------------
%%% module  : mod_server_info
%%% @author  : xyao
%%% email   : jiexiaowen@gmail.com
%%% Created : 2011.12.16
%%% description: 角色info处理
%%%------------------------------------
-module(mod_server_info).
-export([handle_info/2]).
-include("server.hrl").
-include("common.hrl").
-include("battle.hrl").
-include("attr.hrl").
-include("predefine.hrl").
-include("def_fun.hrl").
-include("scene.hrl").
-include("def_event.hrl").
-include("def_goods.hrl").
-include("def_module.hrl").
-include("vip.hrl").
-include("figure.hrl").
-include("language.hrl").
%% 更新一般战斗信息
handle_info({'battle_info', BattleReturn}, Status) ->
    #player_status{battle_attr = BA, last_be_att_time = _LastBeAttTime} = Status,
    case BA#battle_attr.hp > 0 of
        true ->
            #battle_return{
                hp = Hp,
                hurt = FullHurt,
                move_x = MoveX,
                move_y = MoveY,
                anger = _Anger,
                attr_buff_list = AttrBuffList,
                other_buff_list = OtherBuffList,
                atter = Atter,
                sign  = AtterSign,
                god_id = GodId
            } = BattleReturn,
            % NewBeAttTime = ?IF(AtterSign==?BATTLE_SIGN_PLAYER, utime:longunixtime(), LastBeAttTime),
            NewBeAttTime = utime:longunixtime(),
            NewStatus = Status#player_status{
                x = case MoveX == 0 of
                    true -> Status#player_status.x;
                    false -> MoveX
                end,
                y = case MoveY == 0 of
                    true -> Status#player_status.y;
                    false -> MoveY
                end,
                battle_attr = BA#battle_attr{
                    attr_buff_list=AttrBuffList, other_buff_list=OtherBuffList
                    },
                last_be_att_time = NewBeAttTime
            },
            %% 更新唤神&人物血量
            NewStatus1 = case GodId > 0 of
                true  -> god_api:role_after_be_attack(NewStatus, GodId, Hp);
                false ->
                    NewBA = NewStatus#player_status.battle_attr,
                    NewStatus#player_status{battle_attr = NewBA#battle_attr{hp = Hp}}
            end,
            if
                %% 处理本服受到的伤害
                true ->
                    Hurt = min(FullHurt, BA#battle_attr.hp),
                    NewStatus2 = lib_player:be_hurted(NewStatus1, Atter, AtterSign, Hurt)
            end,
            {noreply, NewStatus2};
        false ->
            {noreply, Status}
    end;

%% 死亡信息处理
handle_info({'battle_info_die', BattleReturn}, Status) ->
    #player_status{battle_attr = BA, last_be_att_time = _LastBeAttTime, online = Online} = Status,
    case BA#battle_attr.hp > 0 of
        true ->
            #battle_return{
                hurt = FullHurt,
                move_x = MoveX,
                move_y = MoveY,
                anger = _Anger,
                attr_buff_list = AttrBuffList,
                other_buff_list = OtherBuffList,
                hit_list = HitList,
                atter = Atter,
                sign = Sign
            } = BattleReturn,
            NewX = case MoveX == 0 of
                    true -> Status#player_status.x;
                    false -> MoveX
            end,
            NewY = case MoveY == 0 of
                    true -> Status#player_status.y;
                    false -> MoveY
            end,
            if Online == ?ONLINE_OFF_HOSTING -> Ghost = 0, KilledPos = [];
                true -> Ghost = 1, KilledPos = [{NewX, NewY}]
            end,
            Status1 = Status#player_status{
                x = NewX,
                y = NewY,
                battle_attr = BA#battle_attr{hp=0, ghost=Ghost, attr_buff_list=AttrBuffList, other_buff_list=OtherBuffList},
                last_be_att_time = utime:longunixtime(), killed_pos = KilledPos
                % last_be_att_time = ?IF(Sign==?BATTLE_SIGN_PLAYER, utime:longunixtime(), LastBeAttTime)
            },
            IsClustersScene = lib_scene:is_clusters_scene(Status1#player_status.scene),
            Maps = #{attersign => Sign, atter => Atter, hit => HitList},
            #battle_return_atter{id = AtterId} = Atter,
            {ok, NewStatus} = lib_player_event:dispatch(Status1, ?EVENT_PLAYER_DIE, Maps),
            lib_achievement_api:player_die_event(NewStatus, []),
            if
                BA#battle_attr.pk#pk.pk_value > 0 ->
                    lib_achievement_api:async_event(AtterId, lib_achievement_api, kill_red_name_event, []);
                true -> skip
            end,
            DieStatus = if
                IsClustersScene == true -> %% 跨服死亡处理
                    CalcDieStatus = lib_player:clusters_player_die(NewStatus, Sign, Atter, HitList),
                    %% return
                    CalcDieStatus;
                true -> %% 本服死亡处理
                    CalcDieStatus = lib_player:player_die(NewStatus, Sign, Atter, HitList),
                    Hurt = min(FullHurt, BA#battle_attr.hp),
                    lib_player:be_hurted(CalcDieStatus, Atter, Sign, Hurt),
                    %% return
                    CalcDieStatus
            end,
            %% 判断是否自动买活
            % #battle_return_atter{real_name = AttName} = Atter,
            % if
            %     Online == ?ONLINE_OFF -> ReviveRef = erlang:send_after(10000, self(), {'online_revive', Sign, AttName});
            %     true -> ReviveRef = erlang:send_after(5000, self(), {'online_revive', Sign, AttName})
            % end,
            % DieStatus#player_status{revive_ref = ReviveRef},
            {ok, HostingPlayer} = offline_hosting:host_behavior_die(DieStatus),
            LastStatus = HostingPlayer,
            {noreply, LastStatus};
        false ->
            {noreply, Status}
    end;

%% 设置战斗状态
handle_info({'battle_attr', BA}, Status) ->
    {noreply, Status#player_status{battle_attr = BA}};

%% 设置buff
handle_info({'buff', AttrBuffL, OtherBuffL}, Status) ->
    #player_status{battle_attr=BA} = Status,
    {noreply, Status#player_status{battle_attr=BA#battle_attr{attr_buff_list=AttrBuffL, other_buff_list=OtherBuffL}}};

%% 更新罪恶值+1,定时器自动-1
handle_info({'change_pk_value', Name, Value}, Status) when is_integer(Value) ->
    #player_status{id = RoleId, sid = Sid, battle_attr = #battle_attr{pk = Pk} = BA,
        scene = SceneId, scene_pool_id = ScenePId, copy_id = CopyId, x = X, y = Y} = Status,
    case Value of
        0 -> {noreply, Status};
        _ ->
            NowTime = utime:unixtime(),
            #pk{pk_value = OPkValue, pk_value_ref = PkValueRef, pk_value_change_time = PVChangeTime} = Pk,
            NewPkValue = max(0, OPkValue + Value),
            if
                Value == 1 -> %% 增加一点红名
                    lib_mail_api:send_sys_mail([RoleId], ?MOD_BATTLE, utext:get(?LAN_TITLE_BATTLE_KILLING), utext:get(?LAN_CONTENT_BATTLE_KILLING, [Name]), []);
                true ->
                    skip
            end,
            if
                NewPkValue == 0 ->
                    util:cancel_timer(PkValueRef),
                    NewPk = Pk#pk{pk_value = NewPkValue, pk_value_change_time = 0, pk_value_ref = undefined};
                (OPkValue =< 0 andalso NewPkValue >= 1) orelse PkValueRef == undefined -> %% 第一次开始
                    util:cancel_timer(PkValueRef),
                    NPkValueRef = erlang:send_after(?RemovePkValue*1000, self(), {'change_pk_value', pk_value, -1}),
                    NewPk = Pk#pk{pk_value = NewPkValue, pk_value_change_time = NowTime, pk_value_ref = NPkValueRef};
                NowTime - PVChangeTime >= ?RemovePkValue -> %% 中间变更罪恶值 -- 定时减少罪恶值
                    util:cancel_timer(PkValueRef),
                    NPkValueRef = erlang:send_after(?RemovePkValue*1000, self(), {'change_pk_value', pk_value, -1}),
                    NewPk = Pk#pk{pk_value = NewPkValue, pk_value_change_time = NowTime, pk_value_ref = NPkValueRef};
                true ->
                    NewPk = Pk#pk{pk_value = NewPkValue}
            end,
            NewStatus = Status#player_status{battle_attr = BA#battle_attr{pk = NewPk}},
            lib_player:send_attribute_change_notify(NewStatus, ?NOTIFY_PK),
            mod_scene_agent:update(NewStatus, [{battle_attr, NewStatus#player_status.battle_attr}]),
            %% 告诉自己的pk值
            Args = ?IF(Name == pk_value, ["", 3, NewPkValue], [Name, 1, NewPkValue]),
            {ok, BinData} = pt_200:write(20014, Args),
            lib_server_send:send_to_sid(Sid, BinData),
            %% 广播pk值,让玩家场景变成红名
            if
                NewPkValue > 1 -> skip;
                true ->
                    {ok, Bin} = pt_200:write(20015, [RoleId, NewPkValue]),
                    lib_server_send:send_to_area_scene(SceneId, ScenePId, CopyId, X, Y, Bin)
            end,
            {noreply, NewStatus}
    end;

%% 自动挂机复活
handle_info({'online_revive', _AtterSign, RealName}, Status) ->
    case Status#player_status.online of
        ?ONLINE_OFF -> %% 离线下被杀回到出生点
            case lib_revive:revive(Status, 3) of
                {0, ReviveTmpStatus} -> {noreply, ReviveTmpStatus};
                {_, ReviveTmpStatus} ->
                    {ok, LoadSceneStatus} = pp_scene:handle(12002, ReviveTmpStatus, ok),
                    {noreply, LoadSceneStatus}
            end;
        _ -> %% 在线野外挂机自动买活
            #ets_scene{type = SceneType} = data_scene:get(Status#player_status.scene),
            IsInDungeon = lib_dungeon:is_common_revive_type(Status),
            if
                SceneType =/= ?SCENE_TYPE_NORMAL andalso SceneType =/= ?SCENE_TYPE_OUTSIDE andalso IsInDungeon == false->
                    {noreply, Status};
                true ->
                    if
                        Status#player_status.battle_attr#battle_attr.hp > 0 -> {noreply, Status};
                        true ->
                            case lib_revive:revive(Status, 1) of
                                {0, ReviveTmpStatus} -> {noreply, ReviveTmpStatus};
                                {_, ReviveTmpStatus} ->
                                    #player_status{id = RoleId, bgold = OBGold, gold = OGold} = Status,
                                    #player_status{bgold = NBGold, gold = NGold} = ReviveTmpStatus,
                                    C = utext:get(202, [RealName, OBGold-NBGold, OGold-NGold]),
                                    lib_mail_api:send_sys_mail([RoleId], ?MOD_ONHOOK, utext:get(201), C, []),
                                    {ok, LoadSceneStatus} = pp_scene:handle(12002, ReviveTmpStatus, ok),
                                    {noreply, LoadSceneStatus}
                            end
                    end
            end
    end;

%% 功能超时处理
handle_info({'func_timer_out', Type, Args}, Status) ->
    case Type of
        ?FUNC_TYPE_DESIGNATION -> %% 称号过期
            F = fun(DsgtId, PSTmp) -> {ok, PsTmp} = lib_designation:cancel_dsgt(PSTmp, DsgtId), PsTmp end,
            NewStatus = lists:foldl(F, Status, Args),
            lib_player:log_operation_combat_power(Status, NewStatus, ?MOD_DESIGNATION, 0, ""),
            {noreply, NewStatus};
        ?FUNC_TYPE_FASHION -> %% 时装过期
            NewStatus = lib_fashion:fashion_time_out(Status, Args),
            lib_player:log_operation_combat_power(Status, NewStatus, ?MOD_FASHION, 0, ""),
            {noreply, NewStatus};
        ?FUNC_TYPE_VIP ->
            NewStatus = lib_vip:trial_vip_timeout(Status),
            {noreply, NewStatus};
        ?FUNC_TYPE_SUP_VIP ->
            NewStatus = lib_sup_vip:sup_vip_time_out(Status, Args),
            {noreply, NewStatus};
        ?FUNC_TYPE_MIDNIGHT_LOG ->
            NewStatus = lib_player:trigger_midnight_online_time_log(Status),
            {noreply, NewStatus};
        ?FUNC_TYPE_BUFF ->
            {ok, NewPS} = lib_goods_buff:refresh_player_buff(Status),
            {noreply, NewPS};
        _Type ->
            ?ERR("func_timer_out error type:~p~n", [_Type]),
            {noreply, Status}
    end;

%% 模拟一个协议请求，通常用于特殊情况，需要仿前端请求且需要异步调用的情形
handle_info({'proto', Cmd, Args}, Status) ->
    ProtoRet =
    case protocol_routing:map(Cmd div 100) of
        {role, _ProtoMod, Mod} ->
            Mod:handle(Cmd, Status, Args);
        _ -> ignore
    end,
    {_HandleRet, NewPlayer} = mod_server:after_proto(Cmd, Status, ProtoRet),
    {noreply, NewPlayer};

%% 托管玩家触发行为
handle_info({offline_hosting_behavior, Behavior, Arg}, Status) ->
    case apply(offline_hosting, host_behavior, [Behavior, Status, Arg]) of
        ok ->
            {noreply, Status};
        {ok, NewStatus} when is_record(NewStatus, player_status) ->
            {noreply, NewStatus};
        {stop, Reason, NewStatus} when is_record(NewStatus, player_status) ->
            {stop, Reason, NewStatus};
        _ ->
            {noreply, Status}
    end;

%% 通用回调
handle_info({'mod', Module, Method, Args},Status)->
    case apply(Module, Method, [Status|Args]) of
        NewStatus when is_record(NewStatus, player_status) ->
            lib_player:log_operation_combat_power(Status, NewStatus, 0, 0, {Module, Method}),
            {noreply, NewStatus};
        {ok, NewStatus} when is_record(NewStatus, player_status) ->
            lib_player:log_operation_combat_power(Status, NewStatus, 0, 0, {Module, Method}),
            {noreply, NewStatus};
        {stop, normal, NewStatus} when is_record(NewStatus, player_status) ->
            {stop, normal, NewStatus};
        {noreply, NewStatus} when is_record(NewStatus, player_status) ->
            lib_player:log_operation_combat_power(Status, NewStatus, 0, 0, {Module, Method}),
            {noreply, NewStatus};
        _ ->
            ?ERR("mod_server_info:mod error  Moudle = ~p, Method = ~p, Args = ~p ~n", [Module, Method, Args]),
            throw(error_player_status),
            {noreply, Status}
    end;

%% 封号下线
handle_info({'limit_login', _Id}, Status) ->
    case catch mod_login:logout(Status, ?LOGOUT_LOG_LIMIT_LOGIN) of
        {'EXIT', _Reason} ->
            catch ?ERR("mod_server_login:logout ERROR== LINE:~p Reason=~p~n", [?LINE, _Reason]);
        _ -> skip
    end,
    Status#player_status.socket =/= none andalso
        gen_tcp:close(Status#player_status.socket),
    {stop, normal, Status};

%% 内存检查
% handle_info('check_memory', Status) ->
%     case erlang:process_info(self(), memory) of
%         {memory, Mem} -> Mem1 = util:float_sub(Mem/(1024*1024), 3);
%         _ -> Mem = 0, Mem1 = 0
%     end,
%     util:send_after([], 30*60*1000, self(), check_memory),
%     if
%         Mem1 >= 6 ->
%             ?INFO("check_memory:id:~p, lv:~p, ~pBytes, ~pM~n", [Status#player_status.id,
%                     Status#player_status.figure#figure.lv, Mem, Mem1]);
%         true -> skip
%     end,
%     {noreply, Status};

%% 默认匹配
handle_info(_Info, Status) ->
    ?PRINT("not match info:~p~n", [_Info]),
    {noreply, Status}.
