%% ---------------------------------------------
%% desc:跨服团战相关逻辑接口
%% moudle:camp_war_api.erl
%% author:hh
%% time:20/07/21
%% ---------------------------------------------

-module(camp_war_api).

% -compile([export_all]).

-export([
    enter_scene/1                   %% 主动进入活动场景
    ,cls_enter_scene/1              %% 进入跨服活动场景
    ,exit_scene/1                   %% 主动退出活动场景
    ,leave_scene/4                  %% 其他原因退出场景
    ,relogin/3                      %% 托管上线

    ,cast_to_worker/3
    ,cast_to_role/5
    ,send_to_role/4

    ,list_head_n/2

    ,upload_guild_war_result/1      %% 上传百团大战结果
    ,collect_mon/4                  %% 采集怪物
    ,mon_be_hurt/3                  %% 击杀怪物

    ,is_during_camp_war/0           %% 是否处于团战中
    ,is_on_camp_war/1               %% 是否处于团战场景               
    ,get_revive_xy/1                %% 复活坐标
    ,host_end/1                     %% 托管结束
    ,role_host_end/1                %% 托管结束-人物进程执行
    ,camp_war_tv/1                  %% 团战广播
    ,normal_born_coordinate/0       %% 出生坐标
    ]).

-include("camp_war.hrl").
-include("cross_zone.hrl").
-include("def_module.hrl").
-include("activitycalen.hrl").
-include("czone.hrl").
-include("scene.hrl").
-include("common.hrl").
-include("errcode.hrl").
-include("server.hrl").
-include("guild.hrl").
-include("predefine.hrl").
-include("def_act_label.hrl").
-include("def_fun.hrl").
-include("figure.hrl").
-include("clusters.hrl").
-include("guild_war.hrl").

%% 进场判断-游戏节点执行
enter_scene([ServId, _SerNum, GuildId, _GuildFlag, RoleId|_] = Args) ->
    LocRet = camp_war_mgr:is_loc_camp_war(),
    if LocRet ->
            Ret = check_enter_scene(ServId, GuildId),
            case Ret of
                true ->
                     cast_to_worker(ServId, cast, {enter_scene, Args});
                {false, Errcode} ->
                    lib_server_send:send_to_uid(RoleId, pt_410, 41002, [Errcode])
            end;
        true ->
            clusters_node:apply_cast(?MODULE, cls_enter_scene, [Args])
    end.

%% 进场判断-跨服节点执行
cls_enter_scene([ServId, _SerNum, GuildId, _GuildFlag, RoleId|_] = Args) ->
    CheckRet = check_enter_scene(ServId, GuildId),
    case CheckRet of
        true ->
            cast_to_worker(ServId, cast, {enter_scene, Args});
        {false, Errcode} ->
            clusters_center:apply_cast(ServId, lib_server_send, send_to_uid, [RoleId, pt_410, 41002, [Errcode]])
    end.

%% 进场判断
check_enter_scene(ServId, GuildId) ->
    case ets:lookup(?ETS_FAC_SRV_ZONE, ServId) of
        [{_, {ZoneId, FacId}}|_] ->
            case ets:lookup(?ETS_FAC_LOCAL_GWAR_INFO, {ZoneId, FacId}) of
                [{_, GuildList}|_] ->
                    case lists:keyfind(GuildId, #local_gwar_info.guild_id, GuildList) of
                        #local_gwar_info{} -> true;
                        _ -> {false, ?ERRCODE(err410_need_guild_war)}
                    end;
                _ -> {false, ?ERRCODE(err410_need_guild_war)}
            end;
        _ -> {false, ?ERRCODE(err410_serv_zone_error)}
    end.

%% 退出活动场景
exit_scene([ServId, SerNum, GuildId, RoleId, PoolId|_]) ->
    LocRet = camp_war_mgr:is_loc_camp_war(),
    ExitArgs   = {exit_scene, ServId, SerNum, GuildId, RoleId, PoolId},
    if
        LocRet ->
            cast_to_worker(ServId, cast, ExitArgs);
        true ->
            clusters_node:apply_cast(?MODULE, cast_to_worker, [ServId, cast, ExitArgs])
    end.

%% 托管重登
relogin(ServId, GuildId, RoleId) ->
    LocRet = camp_war_mgr:is_loc_camp_war(),
    Msg    = {relogin, ServId, GuildId, RoleId},
    if
        LocRet ->
            cast_to_worker(ServId, cast, Msg);
        true ->
            clusters_node:apply_cast(?MODULE, cast_to_worker, [ServId, cast, Msg]) 
    end.

%% 跨服中心执行
%% 转发阵营管理进程
-spec cast_to_worker(ServId, CastType, Msg) -> ReturnType when
    ServId :: integer() | {ZoneId, FacId},
    ZoneId :: integer(),
    FacId :: integer(),
    CastType :: cast,
    Msg :: term(),
    ReturnType :: ok|skip.
cast_to_worker({ZoneId, FacId}, cast, Msg) ->
    camp_war_worker:cast({ZoneId, FacId}, Msg);
cast_to_worker(ServId, cast, Msg) ->
    camp_war_worker:cast(ServId, Msg);
cast_to_worker(_Dest, _, _Msg) ->
    ?ERR("unkown dest ~p", [_Dest]),
    skip.

%% 发送到指定服指定角色-跨服节点执行
cast_to_role(ServerId, Dest, ProtoMod, Cmd, Data) when is_pid(Dest) ->
    clusters_center:apply_cast(ServerId, lib_server_send, send_to_sid, [Dest, ProtoMod, Cmd, Data]);
cast_to_role(ServerId, Dest, ProtoMod, Cmd, Data) when is_integer(Dest) ->
    clusters_center:apply_cast(ServerId, lib_server_send, send_to_uid, [Dest, ProtoMod, Cmd, Data]);
cast_to_role(_ServerId, _Dest, _ProtoMod, _Cmd, _Data) ->
    skip.

list_head_n(List, N) ->
    list_head_n(List, N, []).

list_head_n([], _N, HeadList) ->
    {lists:reverse(HeadList), []};
list_head_n([H | T], N, HeadList) when N > 0 ->
    list_head_n(T, N - 1, [H | HeadList]);
list_head_n(List, _N, HeadList) ->
    {lists:reverse(HeadList), List}.

send_to_role(RoleIds, ProtoMod, Proto, Args) ->
    {ok, Bin} = ProtoMod:write(Proto, Args),
    NowSvr = config:get_server_id(),
    lists:map(fun({ServId, RoleId}) ->
        if ServId == NowSvr -> lib_server_send:send_to_uid(RoleId, Bin);
            true            -> clusters_center:apply_cast(ServId, lib_server_send, send_to_uid, [RoleId, Bin])
        end
    end, RoleIds).


%% @上传本地百团大战结果
%% 百团大战活动开始时间 开服第二天 及 开服第三天开始的每周二
%% 跨服团战活动开始时间 开服第四天本服一场 后续由跨服开启,一般是周四开启

%% 以下说明暂废弃
%% 游戏服在参与本服活动后一周内再参与跨服的活动,即本周本服开了一场,则7天内后续即使有跨服活动也无法参加
%% 但如果7天内有百团大战的数据,7天后有跨服活动,此时应上报这一次百团大战的数据
upload_guild_war_result(GuildInfo0) ->
    GuildInfo = get_guild_chief_info(GuildInfo0),
    OpenDay = util:get_open_day(),
    ServId  = config:get_server_id(),
    SerNum  = config:get_server_num(),
    CampWarOpenDay = data_camp_war:get_kv(open_day),
    % LogicWeekLimit = CampWarOpenDay + 7,
    if
        OpenDay =< CampWarOpenDay -> %%
            camp_war_mgr:upload_local_guild_info({?LOC_ZONE, ?LOC_FAC, ServId}, SerNum, GuildInfo);
        true ->
            % Week = utime:day_of_week(),
            % #base_ac{week = CfgWeeks} = data_activitycalen:get_ac(?MOD_CAMP_WAR, ?CLS_ACT_MOD, 1),
            % %% 距离下次活动天数
            % NextDay = get_next_day(Week, CfgWeeks),
            % ThenServOpenDay = OpenDay + NextDay,
            % if OpenDay > LogicWeekLimit orelse (OpenDay =< LogicWeekLimit andalso ThenServOpenDay > LogicWeekLimit) ->
                    LocCache = czone_api:env_get_zone(),
                    if
                        not is_record(LocCache, czone_svr_local) ->
                            ?ERR("svr czone info error ~p", [LocCache]);
                        true ->
                            #czone_svr_local{zone_id = ZoneId, faction = FacId} = LocCache,
                            clusters_node:apply_cast(camp_war_mgr, upload_local_guild_info, [{ZoneId, FacId, ServId}, SerNum, GuildInfo])
                    end
            %     true ->
            %         skip
            % end
    end.

% get_next_day(Week, CfgWeeks) ->
%     SortedWeeks = [
%         begin 
%             if Week > Cw -> {Cw, abs(Cw+7-Week)};
%                 true     -> {Cw, abs(Cw-Week)}
%             end 
%         end || Cw <- CfgWeeks
%     ],
%     case lists:keysort(2, SortedWeeks) of
%         [{_TarWeekDay, LastDay}|_] ->
%             LastDay;
%         _ ->
%             0
%     end.

collect_mon(CollectorId, RecFigure, GuildId, Minfo) ->
    #scene_object{
        config_id = Mid,
        scene = SceneId,
        mon=#scene_mon{mid = Mid, create_key = CreateKey}
    } = Minfo,
    TarScene = local_get_camp_scene(),
    if
        SceneId =/= TarScene -> skip;
        true ->
            case CreateKey of
                {?MOD_CAMP_WAR, ZoneId, FacId, ScenePool, TempResId} ->
                    CollectMsg  = {collect_mon, Mid, TempResId, ScenePool, GuildId, CollectorId, RecFigure},
                    LocRet = camp_war_mgr:is_loc_camp_war(),
                    if LocRet   -> cast_to_worker({ZoneId, FacId}, cast, CollectMsg);
                        true    ->
                            clusters_node:apply_cast(?MODULE, cast_to_worker, [{ZoneId, FacId}, cast, CollectMsg])
                    end;
                _ -> skip
            end
    end.

%% 增加本服场景,现在只有跨服场景
mon_be_hurt([SceneId, ScenePoolId, AtterId, ServId, GuildId, GuildFlag], Mon, Hurt) ->
    CfgSceneId  = data_camp_war:get_kv(scene),
    CfgSceneId1 = data_camp_war:get_kv(cls_scene),
    BossId      = data_camp_war:get_kv(boss_id),
    ClusterType = clusters_api:get_cluster_type(),
    TarScene    = ?IF(ClusterType == ?NODE_GAME, CfgSceneId, CfgSceneId1),
    if
        TarScene =/= SceneId ->
            ok;
        not is_record(Mon, scene_mon) ->
            ok;
        Mon#scene_mon.mid =/= BossId ->
            ok;
        true ->
            HurtMsg = {hurt_mon, [ScenePoolId, AtterId, ServId, GuildId, GuildFlag], Hurt},
            if ClusterType =/= ?CLUSTER_CENTER ->
                    LocRet = camp_war_mgr:is_loc_camp_war(),
                    if LocRet ->
                            cast_to_worker(ServId, cast, HurtMsg);
                        true -> %% 忽略
                            skip
                            % clusters_node:apply_cast(?MODULE, cast_to_worker, [ServId, cast, HurtMsg])
                    end;
                true ->
                    cast_to_worker(ServId, cast, HurtMsg)
            end
    end.

%% 退出场景(关闭客户端|退出登陆)
leave_scene(RoleId, SceneId, PoolId, GuildId) ->
    TarScene = local_get_camp_scene(),
    if
        TarScene == SceneId ->
            ServId = config:get_server_id(),
            camp_war_mgr:cast_to_mgr({leave_scene_when_logout, ServId, RoleId, SceneId, PoolId, GuildId});
        true ->
            skip
    end.

%% 是否处于团战中
is_during_camp_war() ->
    LocRet = camp_war_mgr:is_loc_camp_war(),
    if LocRet ->
            case catch camp_war_mgr:is_during_camp_war() of
                true  -> true;
                false -> false;
                _     -> true
            end;
        true ->
            case clusters_api:is_connected_center() of
                true ->
                    case catch clusters_node:apply_call(camp_war_mgr, is_during_camp_war, []) of
                        true  -> true;
                        false -> false;
                        _     -> true
                    end;
                _ ->
                    true
            end
    end.

%% 本服跨服区分
is_on_camp_war(SceneId) ->
    CampWarScene  = data_camp_war:get_kv(scene),
    CampWarScene1 = data_camp_war:get_kv(cls_scene),
    if
        SceneId == CampWarScene orelse SceneId == CampWarScene1 ->
            true;
        true ->
            false
    end.

get_revive_xy(Player) ->
    #player_status{guild = #status_guild{id = GuildId}, role_camp_war = RoleCampWar} = Player,
    case RoleCampWar of
        #role_camp_war{coordinate = {X, Y}} -> 
            {X, Y};
        _ ->
            BornList = data_camp_war:get_kv(born_position),
            Len = length(BornList),
            Index = GuildId rem Len + 1,
            born_element(Index, 1, BornList)
    end.

born_element(_, _, []) ->
    normal_born_coordinate();
born_element(Index, Index, [{X, Y}|_]) ->
    if Index rem 2 == 0 -> {urand:rand(X-350, X), urand:rand(Y-350, Y)}; 
        true -> {urand:rand(X, X+350), urand:rand(Y, Y+350)}
    end;
born_element(Index, TempIndex, [_|Rest]) ->
    born_element(Index, TempIndex+1, Rest).

%% 这里只取坐标,理论上本服和跨服的坐标配置是一样的
normal_born_coordinate() ->
    SceneId = data_camp_war:get_kv(scene),
    case data_scene:get(SceneId) of
        #ets_scene{x = X, y = Y} -> ok;
        _ -> X = 2594, Y = 3444
    end,
    {X, Y}.

host_end(RoleId) ->
    lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, role_host_end, []).

role_host_end(Player) ->
    NewPlayer = lib_scene:change_default_scene(Player, [{action_free, ?ERRCODE(err410_on_camp_war)}, {act_label, [{?ACT_LABEL_CAMP_WAR_TOP, 0}]}]),
    {ok, NewPlayer1} = offline_hosting:host_behavior_logout(NewPlayer, ?MOD_CAMP_WAR, 1),
    {ok, NewPlayer1}.


camp_war_tv(Player) ->
    #player_status{id = RoleId, sid = Sid, scene = SceneId, figure = #figure{name = RoleName}, guild = #status_guild{id = GuildId}} = Player,
    ActScene = local_get_camp_scene(),
    if
        ActScene == SceneId andalso GuildId =/= 0 ->
            lib_chat:send_TV({guild, GuildId}, ?MOD_CAMP_WAR, 5, [RoleName, RoleId]),
            lib_server_send:send_to_sid(Sid, pt_400, 40064, [?SUCCESS, ?MOD_CAMP_WAR]);
        true ->
            lib_server_send:send_to_sid(Sid, pt_400, 40064, [?FAIL, ?MOD_CAMP_WAR])
    end.

get_guild_chief_info(GuildInfos) ->
    lists:map(fun(GuildInfo) ->
        #guild_war_guild{
            guild_id = GuildId, guild_name = GuildName, score = Score, 
            guild_flag = GuildFlag, guild_power = GuildPower, chief_id = ChiefId
        } = GuildInfo,
        ChiefFigure = lib_role:get_role_figure(ChiefId),
        ChiefRecPic = lib_role:figure_to_rec_picture(ChiefFigure),
        #guild_war_ret{
            guild_id = GuildId, guild_name = GuildName, score = Score,
            guild_flag = GuildFlag, guild_power = GuildPower, chief_info = ChiefRecPic
        }
    end, GuildInfos).

local_get_camp_scene() ->
    case camp_war_mgr:is_loc_camp_war() of
        true ->
            data_camp_war:get_kv(scene);
        _ ->
            data_camp_war:get_kv(cls_scene)
    end.