%%% -------------------------------------------------------
%%% @author huangyongxing@yeah.net
%%% @doc
%%% 游戏服查询某服基础信息时，优先使用本地缓存数据，
%%% 在无法命中的情况下才向跨服中心请求，
%%% 获取数据后缓存到本地节点
%%% @end
%%% -------------------------------------------------------
-module(svr_base_local_api).
-export([
        lookup_svr_base/1
        ,update_svr_base/1
        ,game_init_ets/0
        ,unknown_svr_base/1
    ]).
%% for gm
-export([
        gm_set_server_name/1
        ,gm_set_server_name/2
        %% 逻辑流程内部使用
        ,priv_gm_set_server_name/3
    ]).

-include("center_svr_reg.hrl").
-include("server_env.hrl").

%% 在无法取得游戏服基础信息时，如有需要，生成一个默认的展示数据
unknown_svr_base(ServerId) ->
    #svr_base{
        server_id = ServerId,
        server_num = 0,
        server_name = list_to_binary([<<"Unknown S"/utf8>>, integer_to_binary(ServerId)])
    }.

%% （在游戏服节点）
%% 查询游戏服基本资料 -> [SvrBase] | [].
%% 注： 使用[SvrBase]，是为了与center_svr_reg、与ets的lookup保持一致，便于记忆
%% （由于通常不会无端请求不存在的服，
%% 所以不能命中时都直接向跨服请求，不另外缓存空结果）
lookup_svr_base(ServerId) ->
    case ets:lookup(?ETS_ALL_SVR_BASE, ServerId) of
        SvrBaseList = [_] -> SvrBaseList;
        [] ->
            Reply = clusters_node:apply_call(
                center_svr_reg, lookup_svr_base, [ServerId]
            ),
            case Reply of
                SvrBaseList = [#svr_base{}] ->
                    ets:insert(?ETS_ALL_SVR_BASE, SvrBaseList),
                    SvrBaseList;
                _ ->
                    []
            end
    end.

%% （在游戏服节点）
%% 更新游戏服基础信息
update_svr_base(SvrBase = #svr_base{}) ->
    ets:insert(?ETS_ALL_SVR_BASE, SvrBase);
update_svr_base(_) ->
    ignore.

%% 游戏服节点，初始ETS表
%% 注意：
%%   跨服中心节点使用protected表
%%   而游戏服节点通常不修改数据，为了方便才直接使用public
game_init_ets() ->
    ets:new(?ETS_ALL_SVR_BASE, [named_table, public, set,
            {keypos, #svr_base.server_id}, {read_concurrency, true}
        ]),
    ok.

%% GM - 设置并保存游戏服名称
%% 开发者本地用的游戏服，通常未完整配置游戏后台，所以缺少游戏服名称
%% 正常产品线上，游戏服名为运维统一部署，不允许调用
gm_set_server_name(ServerName) ->
    ServerId = config:get_server_id(),
    IsLocalSet = true,
    priv_gm_set_server_name(ServerId, ServerName, IsLocalSet).

gm_set_server_name(ServerId, ServerName) ->
    IsLocalSet = true,
    priv_gm_set_server_name(ServerId, ServerName, IsLocalSet).

priv_gm_set_server_name(ServerId, ServerName, IsLocalSet) ->
    ServerNameBin = util:make_sure_binary(ServerName),
    %% 通知跨服中心更改服数据
    case IsLocalSet of
        true  -> clusters_node:apply_cast(center_svr_reg, gm_update_server_name, [ServerId, ServerNameBin]);
        false -> ignore
    end,
    %% 检测通知游戏进行本地服数据设置
    IsThisServer = ServerId =:= config:get_server_id(),
    IsInMerges = lists:member(ServerId, config:get_merge_server_ids()),
    if
        IsThisServer ->
            ets:insert(?SERVER_ENV, #server_env{name = server_name, value = ServerNameBin}),
            Sql = util:fmt_bin("REPLACE INTO base_game (cf_name,cf_value) VALUES ('game_title', '~ts')", [ServerNameBin]),
            db:execute(Sql);
        IsInMerges ->
            ignore;
        IsLocalSet == true ->
            %% 尝试通知对应的服，修改其服名（如果该服开启中会更改成功）
            %% 设置 IsLocalSet 为 false，避免有些人本地服改数据错误，引发无限消息循环
            OtherSvrDoMFA = {?MODULE, priv_gm_set_server_name, [ServerId, ServerNameBin, false]},
            clusters_node:apply_cast(clusters_center, apply_cast, [ServerId, OtherSvrDoMFA]);
        true ->
            ignore
    end,
    ok.

