%%%-----------------------------------
%%% module  : lib_server_send
%%% @author  : xyao
%%% email   : jiexiaowen@gmail.com
%%% Created : 2011.07.18
%%% description: 发送消息
%%%-----------------------------------
-module(lib_server_send).
-include("scene.hrl").
-include("server.hrl").
-include("common.hrl").
-include("predefine.hrl").

-export([
        send_one/2
        ,send_to_sid/2
        ,send_to_sid/3
        ,send_to_sid/4
        ,send_to_uid/2
        ,send_to_uid/3
        ,send_to_uid/4
        ,send_to_uid/5
        ,send_to_scene/3
        ,send_to_scene/4
        ,send_to_area_scene/6
        ,send_to_local_all/1
        ,send_to_all/1
        ,send_to_all/3
        ,send_to_guild/2
        ,send_to_all_guild/1
        ,send_to_team/2
        ,send_to_realm/2
        ,send_to_career/2
        ,send_to_rela/2
        ,unpack_cmd/1
       ]).

%% 发送信息给指定socket玩家.
%% S:socket
%% Bin:二进制数据.
send_one(S, Bin) when is_binary(Bin) ->
    gen_tcp:send(S, Bin);
send_one(_S, _Bin) ->
    ok.

%% 发送信息给指定sid玩家.
%% sid:发送进程组
%% Bin:二进制数据.
%% PS: Socket通讯进程修改过，Bin支持BinList，即[Bin1,Bin2,...]
-spec send_to_sid(Sid, BinData) -> term() when
    Sid :: pid(),
    BinData :: close | [ Bin ] | Bin,
    Bin :: binary().
send_to_sid(Sid, close) when is_pid(Sid) ->
    Sid ! close_conn;
send_to_sid(Sid, Bin)   when is_pid(Sid) ->
    Sid ! {send_data, Bin};
send_to_sid(_Sid, _Bin) ->
    ?ERR("send_to_sid/2: error sid:~w, CmdInfo:~w,~nerror:~p",
        [_Sid, unpack_cmd(_Bin), ?CURR_STACK()]
    ).

%% 在指定的节点执行send_to_sid方法
send_to_sid(none, Sid, Bin) ->
    send_to_sid(Sid, Bin);
send_to_sid(Node, Sid, Bin) ->
    Local = node(),
    case Node of
        Local -> send_to_sid(Sid, Bin);
        _ when is_pid(Sid) ->
            rpc:cast(Node, ?MODULE, send_to_sid, [Sid, Bin]);
        _ ->
            ?ERR("send_to_sid/3: error sid:~w, CmdInfo:~w,~nerror:~w",
                [Sid, unpack_cmd(Bin), ?CURR_STACK()]
            )
    end.

%% 发送信息给指定sid玩家.
%% sid:发送进程组, Mod:模块名字, Pro:协议号， Args:参数
send_to_sid(Sid, Mod, Pro, Args)->
    {ok, Bin} = Mod:write(Pro, Args),
    send_to_sid(Sid, Bin).

%% 给id玩家信息
%% id：玩家id
%% Bin：二进制数据.
send_to_uid(Id, Bin) when is_integer(Id) ->
    case rand_to_process_by_id(Id) of
        undefined ->
            skip;
        Pid ->
            send_to_sid(Pid, Bin)
    end;
send_to_uid([], _Bin) ->
    skip;
send_to_uid([Id | Tail], Bin) ->
    send_to_uid(Id, Bin),
    send_to_uid(Tail, Bin).

%% 在指定的节点执行send_to_uid方法
send_to_uid(none, Id, BinData) ->
    send_to_uid(Id, BinData);
send_to_uid(Node, Id, BinData) ->
    Local = node(),
    case Node of
        Local -> send_to_uid(Id, BinData);
        _     -> rpc:cast(Node, lib_server_send, send_to_uid, [Id, BinData])
    end.

%% 给id玩家信息
%% id：玩家id, Mod:模块名字, Pro:协议号， Args:参数list,列表型
send_to_uid(Id, Mod, Pro, Args)->
    case rand_to_process_by_id(Id) of
        undefined ->
            skip;
        Pid ->
            {ok, Bin} = Mod:write(Pro, Args),
            send_to_sid(Pid, Bin)
    end.

%% 给id玩家信息
%% id：玩家id, Mod:模块名字, Pro:协议号， Args:参数list,列表型
send_to_uid(Node, Id, Mod, Pro, Args)->
    case Node =/= none of
        true ->
            rpc:cast(Node, ?MODULE, send_to_uid, [Id, Mod, Pro, Args]);
        false ->
            send_to_uid(Id, Mod, Pro, Args)
    end.

%% 给（任意节点）的场景发送信息
%% Sid:场景id
%% CopyId:副本ID
%% Bin:二进制数据.
send_to_scene(Sid, PoolId, CopyId, Bin) ->
    mod_scene_agent:send_to_scene(Sid, PoolId, CopyId, Bin).

%% 给（任意节点）的场景发送信息
%% Sid:场景id
%% Bin:二进制数据.
send_to_scene(Sid, PoolId, Bin) ->
    mod_scene_agent:send_to_scene(Sid, PoolId, Bin).

%% 给（任意节点）场景区域(9宫格区域)发信息
%% Sid:场景ID
%% CopyId:副本ID
%% X,Y坐标
%% Bin:数据
send_to_area_scene(Sid, PoolId, CopyId, X, Y, Bin) ->
    case lib_scene:is_broadcast_scene(Sid) of
        true ->
            mod_scene_agent:send_to_scene(Sid, PoolId, CopyId, Bin);
        false ->
            mod_scene_agent:send_to_area_scene(Sid, PoolId, CopyId, X, Y, Bin)
    end.

%% 给当前节点的玩家发信息
send_to_local_all(Bin) ->
    L = ets:match(?ETS_ONLINE, #ets_online{sid='$1', _='_'}),
    do_broadcast(L, Bin).

%% 给所有游戏节点的玩家发信息
send_to_all(Bin) ->
    mod_chat_agent:send_msg([{all, Bin}]).

%% 类型发送：发送消息
%% 需要告知客户端在公共线监听此协议包
send_to_all(Type, Value, Bin) ->
    mod_chat_agent:send_msg([{Type, Value, Bin}]).

%% 对列表中的所有socket进行广播
do_broadcast(L, Bin) ->
    [send_to_sid(S, Bin) || [S] <- L].

%% 帮派
send_to_guild(GuildId, Bin) ->
    case GuildId of
        0 -> skip;
        _ -> mod_chat_agent:send_msg([{guild, GuildId, Bin}])
    end.

%% 所有帮派
send_to_all_guild(Bin) ->
    mod_chat_agent:send_msg([{all_guild, Bin}]).

%% 阵营
send_to_realm(Realm, Bin) ->
    case Realm of
        0 -> skip;
        _ -> mod_chat_agent:send_msg([{realm, Realm, Bin}])
    end.

%% 队伍
send_to_team(TeamId, Bin) ->
    case TeamId of
        0 -> skip;
        _ -> lib_team_api:send_to_team(TeamId, Bin)
    end.

%% 职业
send_to_career(Career, Bin) ->
    case Career of
        0 -> skip;
        _ -> mod_chat_agent:send_msg([{career, Career, Bin}])
    end.

%% 发送好友(在线玩家使用)
%% @param Id 在线的玩家
send_to_rela(RoleId, BinData) ->
    Pid = misc:get_player_process(RoleId),
    case self() =:= Pid of
        true ->
            IdList = lib_relationship:get_friend_id_on_dict(RoleId),
            [send_to_uid(Id, BinData)||Id<-IdList];
        false ->
            lib_player:apply_cast(RoleId, ?APPLY_CAST, ?NOT_HAND_OFFLINE, ?MODULE, send_to_rela, [RoleId, BinData])
    end.

rand_to_process_by_id(Id) ->
    misc:whereis_name(global, misc:player_send_process_name(Id)).

%% 从服务端发送给前端的bin中读取出Cmd
%% 主要用于错误检测，在出错的情况下，把Cmd找出来，便于开发发现异常
%% 如果无法解出CMD，则将原数据加上false标签，返回 {false, BinData}
unpack_cmd(<<_:32, _Cmd:16, _/binary>>) -> _Cmd;
unpack_cmd(Data) -> {false, Data}.

