%% ---------------------------------------------------------------------------
%% @doc 玩家事件管理器
%% @author hek
%% @since  2016-09-09
%% @deprecated 本模块管理玩家进程事件监听，移除，派发具体事件
%%
%% Note:
%% 每次都会触发的回调事件，不能进行移除操作
%% 添加暂时触发的事件，触发后可以移除
%% ---------------------------------------------------------------------------

-module(lib_player_event).
-include("def_event.hrl").
-include("rec_event.hrl").
-include("server.hrl").
-include("common.hrl").
-include("def_fun.hrl").
-include("predefine.hrl").
-define(EVENT_LISNENER, {?MODULE, event_listener}).

-export([
        add_listener/3          %% 添加事件监听，不指定监听参数
        ,add_listener/4         %% 添加事件监听，指定监听参数
        ,remove_listener/1      %% 移除某一类型事件监听
        ,remove_listener/3      %% 移除某一类型中的指定“回调模块，回调函数”的事件监听
        ,dispatch/2             %% 派发某个类型的事件，不带事件数据
        ,dispatch/3             %% 派发某个类型的事件，带事件数据
    ]).

-export([
        async_add_listener/4    %% 异步添加事件监听，不指定监听参数
        ,async_add_listener/5   %% 异步添加事件监听，指定监听参数
        ,async_remove_listener/2%% 异步移除某一类型事件监听
        ,async_remove_listener/4%% 异步移除某一类型中的指定“回调模块，回调函数”的事件监听
        ,async_dispatch/2       %% 异步派发某个类型的事件，不带事件数据
        ,async_dispatch/3       %% 异步派发某个类型的事件，带事件数据
        ,async_dispatch/4       %% 异步派发某个类型的事件，带是否处理处理离线情况参数，带事件数据

        ,brocast_dispatch/1     %% 异步广播派发某个类型的事件,不带事件数据
        ,brocast_dispatch/2     %% 异步广播派发某个类型的事件,带事件数据
    ]).

%% 添加事件监听，不指定监听参数
add_listener(EventTypeId, M, F) ->
    add_listener(EventTypeId, M, F, []).

%%--------------------------------------------------------------------
%% Descrip : 添加事件监听
%% Function: add_listener(EventTypeId, M, F, A)
%%          EventTypeId = integer(), 事件类型Id
%%          M = atom(), 事件回调模块
%%          F = atom(), 事件回调函数
%%             !!!回调函数第一个参数为#player_status
%%             !!!回调函数第二个参数使用#event_callback{}封装
%%             !!!回调函数返回值为{ok,#player_status}
%%          A = atom(), 事件监听带入参数
%%             此参数函数回调时赋值给#event_callback.param
%% Returns: ok
%%--------------------------------------------------------------------
add_listener(EventTypeId, M, F, A) ->
    do_add_listener(EventTypeId, {M, F}, {M, F, A}).

async_add_listener(PlayerId, EventTypeId, M, F) ->
    lib_player:apply_cast(PlayerId, ?APPLY_CAST, lib_player_event, add_listener, [EventTypeId, M, F]).

async_add_listener(PlayerId, EventTypeId, M, F, A) ->
    lib_player:apply_cast(PlayerId, ?APPLY_CAST, lib_player_event, add_listener, [EventTypeId, M, F, A]).

%% 移除某一类型事件监听
remove_listener(EventTypeId) ->
    do_remove_listener(EventTypeId, {}).

%%--------------------------------------------------------------------
%% Descrip : 移除事件监听
%% Function: remove_listener(EventTypeId, M, F)
%%          EventTypeId = integer(), 事件类型Id
%%          M = atom(), 事件回调模块
%%          F = atom(), 事件回调函数
%% Returns: ok
%%--------------------------------------------------------------------
remove_listener(EventTypeId, M, F) ->
    do_remove_listener(EventTypeId, {M, F}).

async_remove_listener(PlayerId, EventTypeId) ->
    lib_player:apply_cast(PlayerId, ?APPLY_CAST, lib_player_event, remove_listener, [EventTypeId]).

async_remove_listener(PlayerId, EventTypeId, M, F) ->
    lib_player:apply_cast(PlayerId, ?APPLY_CAST, lib_player_event, remove_listener, [EventTypeId, M, F]).

%%--------------------------------------------------------------------
%% Descrip : 派发某个类型的事件，不带事件数据
%% Returns: {ok, #player_status{}}
%%--------------------------------------------------------------------
dispatch(PS, EventTypeId) ->
    dispatch(PS, EventTypeId, []).

%%--------------------------------------------------------------------
%% @doc
%% 派发某个类型的事件
%% PSOrPlayerId 玩家进程状态数据或玩家Id
%% EventTypeId 事件类型Id
%% Data        事件数据    #event_callback.data
%% @end
%%--------------------------------------------------------------------
-spec dispatch(PSOrPlayerId, EventTypeId, Data) -> {ok, Player} | ok when
    PSOrPlayerId :: Player | PlayerId,
    PlayerId :: non_neg_integer(), Player :: #player_status{},
    EventTypeId :: non_neg_integer(),
    Data :: any().
dispatch(PSOrPlayerId, EventTypeId, Data) ->
    do_dispatch(PSOrPlayerId, EventTypeId, Data).

async_dispatch(PlayerId, EventTypeId) ->
    lib_player:apply_cast(PlayerId, ?APPLY_CAST_STATUS, lib_player_event, dispatch, [EventTypeId]).

async_dispatch(PlayerId, EventTypeId, Data) ->
    lib_player:apply_cast(PlayerId, ?APPLY_CAST_STATUS, lib_player_event, dispatch, [EventTypeId, Data]).

%%--------------------------------------------------------------------
%% Descrip : 异步派发某个类型的事件
%% Function: dispatch(PlayerId, EventTypeId, Data)
%%          PlayerId        = integer(),          玩家状态
%%          HandleOffline   = ?NOT_HAND_OFFLINE | ?HAND_OFFLINE
%%          EventTypeId     = integer(),          事件类型Id
%%          Data            = #event_callback.data, 事件数据
%% Returns: ok | skip.
%%
%% HandleOffline为?HAND_OFFLINE且玩家离线时，只有data_static_event
%% MFA中的A设置了{handle_offline, 1}时，才会处理回调模块
%%--------------------------------------------------------------------
async_dispatch(PlayerId, HandleOffline, EventTypeId, Data) ->
    lib_player:apply_cast(PlayerId, ?APPLY_CAST_STATUS, HandleOffline, lib_player_event, dispatch, [EventTypeId, Data]).


brocast_dispatch(EventTypeId)->
    lib_player:cast_online_role_apply(?APPLY_CAST_STATUS, ?MODULE, dispatch, [EventTypeId]).

brocast_dispatch(EventTypeId,Data)->
    lib_player:cast_online_role_apply(?APPLY_CAST_STATUS, ?MODULE, dispatch, [EventTypeId,Data]).

%% -------------------------------------------------------------------
%%  local function
%% -------------------------------------------------------------------

do_add_listener(EventTypeId, Id, MFA) ->
    {Events, Dict} = get_dynamic_event(EventTypeId),
    NewEvents = lists:keystore(Id, 1, Events, {Id, MFA}),
    set_dynamic_event(EventTypeId, NewEvents, Dict).

do_remove_listener(EventTypeId, Id) ->
    {Events, Dict} = get_dynamic_event(EventTypeId),
    NewEvents = case Id of
        {} -> [];
        _ -> lists:keydelete(Id, 1, Events)
    end,
    set_dynamic_event(EventTypeId, NewEvents, Dict).

do_dispatch(PS, EventTypeId, Data) when is_record(PS, player_status) ->
    {DynamicEvents, _Dict} = get_dynamic_event(EventTypeId),
    StaticEvents = data_static_event:get_static_event(EventTypeId),
    NewPS = do_dispatch_fun(PS, DynamicEvents, EventTypeId, Data),
    LastPS = do_dispatch_fun(NewPS, StaticEvents, EventTypeId, Data),
    {ok, LastPS};
do_dispatch(PlayerId, EventTypeId, Data) when is_integer(PlayerId) ->
    StaticEvents = data_static_event:get_static_event(EventTypeId),
    do_dispatch_fun(PlayerId, StaticEvents, EventTypeId, Data),
    ok.

do_dispatch_fun(PSOrPlayerId, [], _EventTypeId, _Data) -> PSOrPlayerId;

do_dispatch_fun(PS, Patterns, EventTypeId, Data) when is_record(PS, player_status) ->
    do_dispatch_fun_core(PS, Patterns, EventTypeId, Data);

do_dispatch_fun(PlayerId, Patterns = [N|T], EventTypeId, Data) when is_integer(PlayerId) ->
    case N of
        {_Id, {_M, _F, A}} -> skip;
        {_M, _F, A} -> skip
    end,
    V = lists:keyfind(handle_offline, 1, A),
    HandleCallBack = ?IF({handle_offline, 1}==V, true, false),
    case HandleCallBack of
        true  -> do_dispatch_fun_core(PlayerId, Patterns, EventTypeId, Data);
        false -> do_dispatch_fun(PlayerId, T, EventTypeId, Data)
    end.

do_dispatch_fun_core(Player, [EventPattern|T], EventTypeId, Data) ->
    case EventPattern of
        {_Id, {M, F, A}} -> skip;
        {M, F, A} -> skip
    end,
    ECB = #event_callback{
        id = {M, F}
        ,type_id = EventTypeId
        ,param = A
        ,data = Data
    },
    Result = (catch do_dispatch_apply(Player, M, F, ECB)),
    if
        is_record(Player, player_status) ->
            case Result of
                {ok, NewPS = #player_status{}} ->
                    do_dispatch_fun(NewPS, T, EventTypeId, Data);
                Err ->
                    ?ERR("dispatch EventTypeId:~p Data:~p CallBack:{~p, ~p, ~p} Error:~n~p",
                        [EventTypeId, Data, M, F, A, Err]
                    ),
                    do_dispatch_fun(Player, T, EventTypeId, Data)
            end;
        is_integer(Player) ->
            case Result of
                {'EXIT', Reason} ->
                    ?ERR("dispatch EventTypeId:~p Data:~p CallBack:{~p, ~p, ~p} Error:~n~p",
                        [EventTypeId, Data, M, F, A, Reason]
                    );
                _ ->
                    ignore
            end,
            do_dispatch_fun(Player, T, EventTypeId, Data)
    end.

-ifdef(DEV_SERVER).
-define(REDS_DIFF,   1200000).
-define(MEM_MAX,    15000000).
-define(MEM_DIFF,   12000000).
do_dispatch_apply(Player, M, F, ECB) ->
    Args = [Player, ECB],
    Options = [{reds_diff, ?REDS_DIFF}, {mem_max, ?MEM_MAX}, {mem_diff, ?MEM_DIFF}],
    dev_tool:func_checker(M, F, Args, Options).
-else.
do_dispatch_apply(Player, M, F, ECB) ->
    erlang:apply(M, F, [Player, ECB]).
-endif.

get_dynamic_event() ->
    case erlang:get(?EVENT_LISNENER) of
        undefined -> dict:new();
        Dict -> Dict
    end.

get_dynamic_event(EventTypeId) ->
    Dict = get_dynamic_event(),
    case dict:find(EventTypeId, Dict) of
        error -> {[], Dict};
        {ok, Events} -> {Events, Dict}
    end.

set_dynamic_event(EventTypeId, Events, Dict) ->
    NewDict = dict:store(EventTypeId, Events, Dict),
    erlang:put(?EVENT_LISNENER, NewDict),
    ok.

