%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     跨服沙盘本服管理
%%% @end
%%%-------------------------------------------------------------------
-module(cross_point_fight_local).
-author("liushl").

-include("predefine.hrl").
-include("cross_point_fight.hrl").
-include("server.hrl").
-include("common.hrl").
-behaviour(gen_server).

%% API
-export([start_link/0]).

%% gen_server callbacks
-export([init/1,
    handle_call/3,
    handle_cast/2,
    handle_info/2,
    terminate/2,
    code_change/3]).

-export([
    point_subscribe/2               %% 订阅沙盘处理
    , ask_my_buff/1                 %% 查看我方激活的BUFF
    , add_buff/1                    %% 给玩家加沙盘BUFF：增加BUFF接口
    , remove_buff/1

    , sync_cross_info/2             %% 链接上跨服中心同步活动数据
    , notice_season_switch/1        %% 赛季切换
    , when_season_switch/1
    , point_change/2                %% 据点信息变更
    , active_point_buff/3           %% 激活BUFF

    , apply_cast/1
    , apply_info/1
]).
-define(SERVER, ?MODULE).

%%%===================================================================
%%% @doc 玩家逻辑处理
%%%===================================================================
%% 订阅沙盘处理
point_subscribe(RolePid, Status) ->
    apply_cast({subscribe, RolePid, Status}).
%% 查看我方激活的BUFF
ask_my_buff(Sid) ->
    apply_cast({ask_my_buff, Sid}).
%% 给玩家加沙盘BUFF：增加BUFF接口
add_buff(RoleId) ->
    apply_cast({add_buff, RoleId}).
%% 移除BUFF
remove_buff(RoleId) ->
    apply_cast({remove_buff, RoleId}).

%%%===================================================================
%%% @doc 系统逻辑处理
%%%===================================================================
%% 链接上跨服中心同步活动数据
sync_cross_info(ServerId, Info) ->
    clusters_center:apply_cast(ServerId, ?MODULE, apply_info, [{sync_cross_info, Info}]).
%% 赛季切换
notice_season_switch(ZoneId) ->
    czone:zone_apply_cast(ZoneId,       {?MODULE, apply_info, [season_switch]}).
%% 据点信息变更
point_change(ZoneId, PointId) ->
    czone:zone_apply_cast(ZoneId,       {?MODULE, apply_info, [{point_change, PointId}]}).
%% 激活BUFF
active_point_buff(ZoneId, PointBuff, BuffTime) ->
    czone:zone_apply_cast(ZoneId,       {?MODULE, apply_info, [{active_point_buff, PointBuff, BuffTime}]}).

apply_cast(Msg) ->
    gen_server:cast(?MODULE, Msg).
apply_info(Msg) ->
    ?MODULE ! Msg.
%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @end
%%--------------------------------------------------------------------
-spec(start_link() ->
    {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%% @end
%%--------------------------------------------------------------------
-spec(init(Args :: term()) ->
    {ok, State :: #point_fight_local{}} | {ok, State :: #point_fight_local{}, timeout() | hibernate} |
    {stop, Reason :: term()} | ignore).
init([]) ->
    {ok, #point_fight_local{}}.

%%%===================================================================
%%% Logic
%%%===================================================================
do_handle_call(_Request, _From, State) ->
    io:format("time:[~p], module:~p: do_handle_call unexpect message:~p~n", [erlang:localtime(), ?MODULE, _Request]),
    {reply, ok, State}.

%% 订阅状态变更
do_handle_cast({subscribe, RolePid, Status}, State) ->
    Subscribe = State#point_fight_local.subscribe,
    NewSubscribe =
        case Status =:= 1 of
            true  ->
                case lists:member(RolePid, Subscribe) of
                    true  -> Subscribe;
                    false -> [RolePid|Subscribe]
                end;
            false ->
                lists:delete(RolePid, Subscribe)
        end,
    {noreply, State#point_fight_local{subscribe = NewSubscribe}};
%% 查看我方激活的BUFF
do_handle_cast({ask_my_buff, Sid}, State) ->
    Now = utime:unixtime(),
    MyBuff =
        case Now < State#point_fight_local.buff_time of
            true  ->
                State#point_fight_local.my_buff;
            false ->
                []
        end,
    {ok, Bin} = pt_604:write(60408, [MyBuff]),
    lib_server_send:send_to_sid(Sid, Bin),
    {noreply, State};
%% 激活我方的BUFF
do_handle_cast({add_buff, RoleId}, State) ->
    Now = utime:unixtime(),
    case Now < State#point_fight_local.buff_time of
        true  ->
            [ lib_skill_api:add_buff(RoleId, SKillId, 1) || SKillId <- State#point_fight_local.my_buff];
        false ->
            []
    end,
    {noreply, State};
%% 移除BUFF
do_handle_cast({remove_buff, RoleId}, State) ->
    [ lib_skill_api:clean_buff(RoleId, SKillId) || SKillId <- State#point_fight_local.my_buff],
    {noreply, State};
do_handle_cast(_Request, State) ->
    io:format("time:[~p], module:~p: handle_cast unexpect message:~p~n", [erlang:localtime(), ?MODULE, _Request]),
    {noreply, State}.

%% 同步跨服中心的活动数据
do_handle_info({sync_cross_info, [MyBuff, BuffTime]}, State) ->
    {noreply, State#point_fight_local{my_buff = MyBuff, buff_time = BuffTime}};

%% 赛季结束-同样也是新赛季开启
do_handle_info(season_switch, State) ->
    %% 通知所有在线玩家清理内存数据
    lib_player:cast_online_role_apply(?APPLY_CAST_STATUS, ?MODULE, when_season_switch, []),
    do_handle_info({clean_data, 1}, State),
    {noreply, State};

%% 清理数据库数据
do_handle_info({clean_data, N}, State) when N =< 5 ->
    F = fun() ->
            db:execute(?DB_CLEAN_ROLE_CROSS_POINT_INFO),
            %% 清理军团数据
            lib_role_legion:clean_rank_score(),
            ok
        end,
    case db:transaction(F) of
        ok ->
            ok;
        Other ->
            ?ERR("~p", [Other]),
            erlang:send_after(5000, self(), {clean_data, N + 1})
    end,
    {noreply, State};

%% 数据变更-要玩家刷新某据点信息
do_handle_info({point_change, PointId}, State) ->
    Subscribe = State#point_fight_local.subscribe,
    NewSubscribe =
        lists:foldl(
            fun(RolePid, Temp) ->
                case lib_player:apply_cast(RolePid, ?APPLY_CAST_STATUS, cross_point_fight_logic, ask_point_info, [PointId]) of
                    skip ->
                        Temp;
                    _ ->
                        [RolePid|Temp]
                end
            end, [], Subscribe
        ),
    {noreply, State#point_fight_local{subscribe = NewSubscribe}};

%% BUFF激活了
do_handle_info({active_point_buff, PointBuff, BuffTime}, State) ->
    MyFaction = czone_api:env_get_faction(),
    MyBuff = lists:foldl(
        fun
            ({PointId, Faction}, Temp) when Faction =:= MyFaction ->
                case data_cross_point_fight:get_point_fight_point(PointId) of
                    #base_cross_point_fight_point{buff = Buff} ->
                        [Buff|Temp];
                    _ ->
                        Temp
                end;
            (_, Temp) ->
                Temp
        end, [], PointBuff
    ),
    {noreply, State#point_fight_local{my_buff = MyBuff, buff_time = BuffTime}};

do_handle_info(_Info, State) ->
    io:format("time:[~p], module:~p: handle_info unexpect message:~p~n", [erlang:localtime(), ?MODULE, _Info]),
    {noreply, State}.


%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling call messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
    State :: #point_fight_local{}) ->
    {reply, Reply :: term(), NewState :: #point_fight_local{}} |
    {reply, Reply :: term(), NewState :: #point_fight_local{}, timeout() | hibernate} |
    {noreply, NewState :: #point_fight_local{}} |
    {noreply, NewState :: #point_fight_local{}, timeout() | hibernate} |
    {stop, Reason :: term(), Reply :: term(), NewState :: #point_fight_local{}} |
    {stop, Reason :: term(), NewState :: #point_fight_local{}}).
handle_call(Msg, From, State) ->
    try
        do_handle_call(Msg, From, State)
    catch _:_Error:Trace ->
        io:format("[~p]~nhandle_call Msg:~p~n Error:~p~n ~p~n", [erlang:localtime(), Msg, _Error, Trace]),
        {reply, error, State}
    end.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_cast(Request :: term(), State :: #point_fight_local{}) ->
    {noreply, NewState :: #point_fight_local{}} |
    {noreply, NewState :: #point_fight_local{}, timeout() | hibernate} |
    {stop, Reason :: term(), NewState :: #point_fight_local{}}).
handle_cast(Msg, State) ->
    try
        do_handle_cast(Msg, State)
    catch _:_Error:Trace ->
        io:format("[~p]~nhandle_cast Msg:~p~n Error:~p~n ~p~n", [erlang:localtime(), Msg, _Error, Trace]),
        {noreply, State}
    end.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling all non call/cast messages
%%
%% @spec handle_info(Info, State) -> {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
-spec(handle_info(Info :: timeout() | term(), State :: #point_fight_local{}) ->
    {noreply, NewState :: #point_fight_local{}} |
    {noreply, NewState :: #point_fight_local{}, timeout() | hibernate} |
    {stop, Reason :: term(), NewState :: #point_fight_local{}}).
handle_info(Msg, State) ->
    try
        do_handle_info(Msg, State)
    catch _:_Error:Trace ->
        io:format("[~p]~nhandle_info Msg:~p~n Error:~p~n ~p~n", [erlang:localtime(), Msg, _Error, Trace]),
        {noreply, State}
    end.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any
%% necessary cleaning up. When it returns, the gen_server terminates
%% with Reason. The return value is ignored.
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
    State :: #point_fight_local{}) ->
    term()).
terminate(_Reason, _State) ->
    ok.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Convert process state when code is changed
%%
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @end
%%--------------------------------------------------------------------
-spec(code_change(OldVsn :: term() | {down, term()}, State :: #point_fight_local{},
    Extra :: term()) ->
    {ok, NewState :: #point_fight_local{}} | {error, Reason :: term()}).
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================
%% 赛季清理数据
when_season_switch(Player) ->
    NewPlayer0 = Player#player_status{role_cross_point = #role_cross_point{}},
    pp_cross_point_fight:handle(60400, NewPlayer0, []),
    NewPlayer  = lib_role_legion:reset_rank_score(NewPlayer0),
    NewPlayer.