%% ---------------------------------------------------------------------------
%% @doc 跨服帮战战场进程
%% @author zhaoyu
%% @since  2018-08-30
%% ---------------------------------------------------------------------------
-module(cross_guild_war_battle).


-behaviour(gen_server).

%% API
-export([
        start_link/3
        ,call/2                     %% call本进程
        ,cast/2                     %% cast本进程
        ,info/2                     %% 给进程发送消息
        ,hurt_check/3               %% 检查玩家是否可以攻击主城怪物和战旗
        ,send_occupy_award/3        %% 发送占领城池奖励
        ,give_support_award_local/1 %% 游戏服执行发送应援奖励
        ,player_do_dirction/3
        ,host_behavior_reward/2
    ]).

-export([
        n_times_award/2             %% 奖励增加N倍
]).

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

-include("server.hrl").
-include("common.hrl").
-include("cross_guild_war.hrl").
-include("scene.hrl").
-include("attr.hrl").
-include("figure.hrl").
-include("errcode.hrl").
-include("battle.hrl").
-include("guild.hrl").
-include("def_module.hrl").
-include("predefine.hrl").


%% ---------------------------------------------------------------------------
%% @doc Starts the server
-spec start_link(ZoneId, GuildList, EndTime) ->
    {ok, Pid} | ignore | {error, Error} when
    ZoneId      :: integer(),       %% 分区Id
    GuildList   :: [#cgw_guild_info{}],
    EndTime     :: integer(),
    Pid         :: pid(),
    Error       :: term().
%% ---------------------------------------------------------------------------
start_link(ZoneId, GuildList, EndTime) ->
    gen_server:start_link(?MODULE, {ZoneId, GuildList, EndTime}, []).


%% ---------------------------------------------------------------------------
%% @doc call进程
-spec call(Pid, Msg) -> term() | {error, Reason} when
    Pid     :: pid(),
    Msg     :: term(),
    Reason  :: any().
%% ---------------------------------------------------------------------------
call(Pid, Msg) ->
    try gen_server:call(Pid, Msg) of
    Reply -> Reply
    catch Class:Reason ->
        Error = {Class, Reason, erlang:get_stacktrace()},
        case Reason of
            {noproc, {gen_server, call, [Pid, {get_revive_pos, _Group}]}} ->
                ok;
            _ ->
                ?ERR("~w:handle_cast error:~p~n", [?MODULE, Error])
        end,
        {error, Reason}
    end.


%% ---------------------------------------------------------------------------
%% @doc cast进程
-spec cast(Pid, Msg) -> ok when
    Pid     :: pid(),
    Msg     :: term().
%% ---------------------------------------------------------------------------
cast(Pid, Msg) ->
    gen_server:cast(Pid, Msg).


%% ---------------------------------------------------------------------------
%% @doc 给进程发送消息
-spec info(Pid, Msg) -> ok when
    Pid     :: pid(),
    Msg     :: term().
%% ---------------------------------------------------------------------------
info(Pid, Msg) ->
    erlang:send(Pid, Msg).


%% ---------------------------------------------------------------------------
%% @doc Initializes the server
-spec init(Args) ->
    {ok, State}
    | {ok, State, Timeout}
    | ignore
    | {stop, Reason} when
    Args    :: term() ,
    State   :: #cgw_battlefield_state{},
    Timeout :: non_neg_integer() | infinity,
    Reason  :: term().
%% ---------------------------------------------------------------------------
init({ZoneId, GuildList, EndTime}) ->
    process_flag(trap_exit, true),
    NowTime = utime:unixtime(),
    Timeout = max(EndTime - NowTime, 0) * 1000,
    Self = self(),
    Ref = erlang:send_after(Timeout, Self, {time_is_over}),
    PoolId = ZoneId,
    %% 创建场景进程
    SceneId = data_cross_guild_war:get_config(scene_id),
    mod_scene_agent:get_scene_pid(SceneId, PoolId),
    Cities = init_cities(PoolId),
    %% 初始化从参战帮派信息
    GroupMap = init_guild_info(GuildList),

    GuildIdList = [
        Guild#cgw_guild_info.guild_id || Guild <- GuildList
    ],
    BroadcastRef = erlang:send_after(
        ?BROADCAST_BATTLEFIELD_TIMEOUT, Self, {broadcast_battlefield_info}
    ),
    State = #cgw_battlefield_state{
        zone_id = ZoneId, pool_id = PoolId
        ,cities = Cities, guild_list = GuildIdList, ref = Ref
        ,broadcast_ref = BroadcastRef,group = GroupMap
    },
    {ok, State}.


%% ---------------------------------------------------------------------------
%% @doc Handling call messages
-spec handle_call(Request, From, State) ->
    {reply, Reply, State}
    | {reply, Reply, State, Timeout}
    | {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, Reply, State}
    | {stop, Reason, State} when
    Request :: term(),
    From :: pid(),
    State :: #cgw_battlefield_state{},
    Reply :: term(),
    State :: #cgw_battlefield_state{},
    Timeout :: non_neg_integer() | infinity.
%% ---------------------------------------------------------------------------
handle_call(Request, From, State) ->
    try handle_call2(Request, From, State) of
        Result ->
            Result
    catch Class:Reason ->
        Error = {Class, Reason, erlang:get_stacktrace()},
        ?ERR("~w:handle_cast error:~p~n", [?MODULE, Error]),
        {reply, error, State}
    end.


%% ---------------------------------------------------------------------------
%% @doc Handling cast messages
-spec handle_cast(Msg, State) ->
    {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, State} when
    Msg :: term(),
    State :: #cgw_battlefield_state{},
    Timeout :: non_neg_integer() | infinity,
    Reason :: term().
%% ---------------------------------------------------------------------------
handle_cast(Msg, State) ->
    try handle_cast2(Msg, State) of
        Result ->
            Result
    catch Class:Reason ->
        Error = {Class, Reason, erlang:get_stacktrace()},
        ?ERR("~w:handle_cast error:~p~n", [?MODULE, Error]),
        {noreply, State}
    end.


%% ---------------------------------------------------------------------------
%% @doc Handling all non call/cast messages
-spec handle_info(Info, State) ->
    {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, State} when
    Info :: term(),
    State :: #cgw_battlefield_state{},
    Timeout :: non_neg_integer() | infinity,
    Reason :: term().
%% ---------------------------------------------------------------------------
handle_info(Info, State) ->
    try handle_info2(Info, State) of
        Result ->
            Result
    catch Class:Reason ->
        Error = {Class, Reason, erlang:get_stacktrace()},
        ?ERR("~w:handle_cast error:~p~n", [?MODULE, Error]),
        {noreply, State}
    end.



%% ---------------------------------------------------------------------------
%% @doc Handling call messages
-spec handle_call2(Request, From, State) ->
    {reply, Reply, State}
    | {reply, Reply, State, Timeout}
    | {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, Reply, State}
    | {stop, Reason, State} when
    Request :: term(),
    From :: pid(),
    State :: #cgw_battlefield_state{},
    Reply :: term(),
    State :: #cgw_battlefield_state{},
    Timeout :: non_neg_integer() | infinity.
%% ---------------------------------------------------------------------------
handle_call2({get_born_xy, Group}, _From, State) ->
    Reply = case get_guild_info(Group) of
        #cgw_guild_info{revive_pos = RevivePos, born_pos = Pos} ->
            case Pos of
                {0, 0} ->
                    RevivePos;
                _ ->
                    Pos
            end;
        _ -> {0, 0}
    end,
    {reply, Reply, State};
handle_call2({get_revive_xy, Group},_From,State)->
    Reply = case get_guild_info(Group) of
        #cgw_guild_info{revive_pos = Pos, born_pos = BornPos} ->
            case Pos of
                {0, 0} ->
                    BornPos;
                _ ->
                    Pos
            end;
        _ ->
            {0, 0}
    end,
    {reply, Reply, State};
handle_call2({get_battle_xy, Group},_From,State)->
    Reply = case get_guild_info(Group) of
        #cgw_guild_info{battle_pos = Pos, revive_pos = RevivePos} ->
            case Pos of
                {0, 0} ->
                    RevivePos;
                _ ->
                    Pos
            end;
        _ ->
            {0, 0}
    end,
    {reply, Reply, State};
handle_call2(_Request, _From, State) ->
    {reply, ok, State}.



%% ---------------------------------------------------------------------------
%% @doc Handling cast messages
-spec handle_cast2(Msg, State) ->
    {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, State} when
    Msg :: term(),
    State :: #cgw_battlefield_state{},
    Timeout :: non_neg_integer() | infinity,
    Reason :: term().
%% ---------------------------------------------------------------------------
%% 进入场景
handle_cast2({enter_scene, RoleSingUpInfo, GuildId}, State) ->
    #cgw_battlefield_state{group=GroupMap}=State,
    #cgw_role_info{
        role_id = RoleId, name = RoleName, server_id = ServerId,power=Power
    } = RoleSingUpInfo,
    Group = maps:get(GuildId,GroupMap,0),
    case get_guild_info(Group) of
        Guild = #cgw_guild_info{guild_id = GuildId,init_city=CityId} ->
            #cgw_guild_info{born_pos = Born} = Guild,
            #cgw_battlefield_state{
                pool_id = PoolId
            } = State,
            Groups = maps:values(GroupMap),
            SceneId = data_cross_guild_war:get_config(scene_id),
            {_IsNewJoin, RoleInfo} = case get_role_info(RoleId) of
                RInfo = #cgw_role_battle_info{} ->
                    {false, RInfo};
                _ ->
                    RInfo = #cgw_role_battle_info{
                        role_id = RoleId, guild_id = GuildId, name = RoleName
                        ,server_id = ServerId
                    },
                    {true, RInfo}
            end,
            NewRoleInfo = RoleInfo#cgw_role_battle_info{scene = SceneId,group=Group},
            put_role_info(RoleId, NewRoleInfo),
            {BornX, BornY} = Born,
            GroupsBin = pack_guilds_group(Groups),
            CopyId = self(),
            Args = [RoleId, PoolId, CopyId, BornX, BornY, Group, GroupsBin],
            MFA = {cross_guild_war_api, enter_scene_function, Args},
            clusters_center:apply_cast(ServerId, MFA),
            Args1 = [RoleId,RoleName,GuildId,Power,CityId],
            MFA1 = {cross_guild_war_api, add_log, Args1},
            clusters_center:apply_cast(ServerId, MFA1);
        _ ->
            ErrorCode = ?ERRCODE(err601_guild_donot_join),
            {ok,Bin} = pt_601:write(60108,[ErrorCode]),
            clusters_api:send_to_uid(RoleId, Bin)
    end,
    {noreply, State};
%% 离开场景
handle_cast2({leave_scene, RoleId}, State) ->
    case get_role_info(RoleId) of
        RoleInfo = #cgw_role_battle_info{server_id = ServerId} ->
            NewRoleInfo = RoleInfo#cgw_role_battle_info{scene = 0},
            put_role_info(RoleId, NewRoleInfo),
            MFA = {cross_guild_war_api, leave_scene_function, [RoleId]},
            clusters_center:apply_cast(ServerId, MFA);
        _ ->
            ok
    end,
    {noreply, State};

%%更新团长信息
handle_cast2({update_chief_info, Group, RecFigure},State)->
    case get_guild_info(Group) of
        Guild = #cgw_guild_info{} ->
            NewGuild = Guild#cgw_guild_info{chief_info=RecFigure},
            put_guild_info(NewGuild);
        _->
            skip
    end,
    {noreply,State};


%% 攻击怪
handle_cast2(
    {attack_mon, _RoleId, _TemGuildId, AttGroup, MonId, Hp, MonGroup, IsFirstAttack}, State
) ->
    NewState = case get_guild_info(AttGroup) of
        #cgw_guild_info{} ->
            {CityId, MonType} = data_cross_guild_war:get_mon_config(MonId),
            #cgw_battlefield_state{cities = Cities} = State,
            City = lists:keyfind(CityId, #cgw_city_info.city_id, Cities),
            State2 = case MonType of
                ?MON_TYPE_FLAG ->
                    #cgw_city_info{owner = Owner,group=OwnGroup} = City,
                    City2 = City#cgw_city_info{flag_hp = Hp},
                    NewCities = lists:keystore(
                        CityId, #cgw_city_info.city_id, Cities, City2
                    ),
                    #cross_guild_war_city{
                        name = CityName
                    } = data_cross_guild_war:get_city_config(CityId),
                    %% 发送城池被攻击传闻
                    case IsFirstAttack andalso MonGroup > 0 of
                        true ->
                            send_city_be_attacked_notice(OwnGroup,Owner, CityName, CityId);
                        _ ->
                            ok
                    end,
                    State#cgw_battlefield_state{cities = NewCities};
                ?MON_TYPE_MON ->
                    case IsFirstAttack andalso MonGroup > 0 of
                        true ->
                            ok;
                        _ ->
                            ok
                    end,
                    State
            end,
            State2;
        _ ->
            State
    end,
    {noreply, NewState};
%% 击杀怪
handle_cast2({kill_mon, Attacker, MonInfo}, State) ->
    #battle_return_atter{real_id=RealId,real_name = Name, group = AttGroup} = Attacker,
    #scene_object{mon = #scene_mon{mid = MonId}, id = MonUid} = MonInfo,
    case get_guild_info(AttGroup) of
        GuildInfo = #cgw_guild_info{} ->
            #cgw_guild_info{
                own_cities = OwnCities, group = Group, revive_pos = RevivePos
                ,server_num = ServerNum, guild_name = GuildName,guild_id = GuildId,fact_id=FactId
            } = GuildInfo,
            {CityId, MonType} = data_cross_guild_war:get_mon_config(MonId),
            #cross_guild_war_city{
                type_id = CityType, revive_point = RevivePoint, name = CityName,flag={_,Bx,By}
            } = data_cross_guild_war:get_city_config(CityId),

            #cgw_battlefield_state{cities = Cities, pool_id = PoolId} = State,
            {value, City, LessCities} = lists:keytake(
                CityId, #cgw_city_info.city_id, Cities
            ),
            SceneId = data_cross_guild_war:get_config(scene_id),
            MainCityId = data_cross_guild_war:get_config(main_city),
            NewCity = if
                MonType =:= ?MON_TYPE_FLAG -> % 战旗
                    %% 更新帮派占有的城池和复活点
                    NewRevivePos = case RevivePoint of
                        {X, Y} -> {X, Y};
                        _ -> RevivePos
                    end,
                    GuildInfo2 = GuildInfo#cgw_guild_info{
                        own_cities = [CityId | OwnCities], revive_pos = NewRevivePos,battle_pos={Bx,By}
                    },
                    put_guild_info(GuildInfo2),
                    %% 更新原占领该城池的帮派占有的城池和复活点
                    case get_guild_info(City#cgw_city_info.group) of
                        OwnG = #cgw_guild_info{
                            own_cities = OwnCities2, born_pos = BornPos
                        } ->
                            OwnCities3 = lists:delete(CityId, OwnCities2),
                            NewRPos = case OwnCities2 -- [CityId, MainCityId] of
                                [LessCityId | _] ->
                                    CityConfig =
                                    data_cross_guild_war:get_city_config(LessCityId),
                                    CityConfig#cross_guild_war_city.revive_point;
                                _ ->
                                    BornPos
                            end,
                            OwnGuild = OwnG#cgw_guild_info{
                                own_cities = OwnCities3, revive_pos = NewRPos
                            },
                            FilterGroups = [AttGroup,City#cgw_city_info.group],
                            put_guild_info(OwnGuild);
                        _ ->
                            FilterGroups = [AttGroup]
                    end,
                    CanAttackGroupList = [Group] ++ [
                        OwnGroup || #cgw_city_info{city_id = CId, owner = OwnGId,group=OwnGroup}
                        <- LessCities, OwnGId =/= 0 andalso CId =/= MainCityId
                    ],

                    %% 更新帮派是否可以攻打主城
                    case CityType of
                        ?CITY_TYPE_SIDE ->
                            #cgw_city_info{
                                flag_uid = MainCityFlagUid, mon_uids = MainCityMonUids
                            } = lists:keyfind(MainCityId, #cgw_city_info.city_id, Cities),
                            UpdateList = [
                                {hurt_check, {?MODULE, hurt_check, [CanAttackGroupList]}}
                            ],
                            MonUidList = [MainCityFlagUid | MainCityMonUids],
                            lib_mon:change_attr_by_ids(SceneId, PoolId, MonUidList, UpdateList);
                        _ ->
                            ok
                    end,
                    %% 城池创建新的怪
                    City2 = init_city(
                        CityId, SceneId, PoolId, GuildId, Group, CanAttackGroupList, GuildName
                    ),
                    %% 发送占领城池传闻
                    send_get_city_notice(ServerNum, GuildName, Name, State, CityName, CityId,FactId,FilterGroups),
                    cross_guild_war_api:log_cross_guild_war_conquer(RealId,Name,CityId,GuildInfo2),
                    City2;
                MonType =:= ?MON_TYPE_MON -> % 守护兽
                    #cgw_city_info{mon_uids = MonUids} = City,
                    NewMonUids = lists:delete(MonUid, MonUids),
                    NewMonUids =:= [] andalso lib_mon:change_attr_by_ids(
                        SceneId, PoolId, [City#cgw_city_info.flag_uid], [{is_be_atted, 1}]
                    ),
                    City#cgw_city_info{mon_uids = NewMonUids}
            end,
            NewCities = [NewCity | LessCities],
            NewState = State#cgw_battlefield_state{cities = NewCities},
            MonType =:= ?MON_TYPE_FLAG andalso broadcast_battlefield_info(NewCities),
            {noreply, NewState};
        _ ->
            {noreply, State}
    end;
%% 战场信息
handle_cast2({send_battlefield_info, Sid}, State) ->
    #cgw_battlefield_state{cities = Cities} = State,
    BinData = pack_cities(Cities),
    lib_server_send:send_to_sid(Sid, BinData),
    {noreply, State};
%% 发出号令
handle_cast2({send_guild_direction, Sid, FactId, CityId}, State) ->
    {ok, BinData} = pt_601:write(60113, [CityId]),
    RoleInfoList = get_role_info(),
    [
        clusters_center:apply_cast(
            ServerId, {lib_server_send, send_to_uid, [RoleId, BinData]}
        ) || #cgw_role_battle_info{
            server_id = ServerId, role_id = RoleId, scene = Scene
            ,group = Group2
        } <- RoleInfoList, Scene > 0 andalso FactId =:= Group2
    ],
    {ok, BData} = pt_601:write(60112, [?SUCCESS]),
    lib_server_send:send_to_sid(Sid, BData),
    do_send_guild_direction(FactId,CityId,State),
    {noreply, State};
handle_cast2(_Msg, State) ->
    {noreply, State}.


%% ---------------------------------------------------------------------------
%% @doc Handling all non call/cast messages
-spec handle_info2(Info, State) ->
    {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, State} when
    Info :: term(),
    State :: #cgw_battlefield_state{},
    Timeout :: non_neg_integer() | infinity,
    Reason :: term().
%% ---------------------------------------------------------------------------
%% 战斗时间结束
handle_info2({time_is_over}, State) ->
    #cgw_battlefield_state{zone_id = ZoneId, guild_list = _GuildIdList,group=GroupMap,ref = ORef} = State,
    util:cancel_timer(ORef),
    BattleFieldResult = to_battlefield_result(State),
    Msg = {battlefield_result, ZoneId, self(), BattleFieldResult},
    cross_guild_war_mgr:cast(Msg),
    RoleInfoList = get_role_info(),
    Groups = maps:values(GroupMap),
    %% 发送结算
    send_settlement(RoleInfoList, Groups,BattleFieldResult),
    %% 发送占领城池奖励
    send_city_owner_award(Groups),
    %% 发送应援奖励
    give_support_award(Groups),
    %% 发送最终占领城池传闻
    send_winner_notice(State, BattleFieldResult),
    Ref = erlang:send_after(1000, self(), {clean_all}),

    RoleInfoList = get_role_info(),
    send_out_all_roles(RoleInfoList),

    NewState = State#cgw_battlefield_state{ref=Ref},
    {noreply, NewState};

handle_info2({clean_all},State)->
    % RoleInfoList = get_role_info(),
    % send_out_all_roles(RoleInfoList),
    {stop, normal, State};

%% 广播战场信息
handle_info2({broadcast_battlefield_info}, State) ->
    #cgw_battlefield_state{broadcast_ref = Ref, cities = Cities} = State,
    util:cancel_timer(Ref),
    BroadcastRef = erlang:send_after(
        ?BROADCAST_BATTLEFIELD_TIMEOUT, self(), {broadcast_battlefield_info}
    ),
    broadcast_battlefield_info(Cities),
    NewState = State#cgw_battlefield_state{broadcast_ref = BroadcastRef},
    {noreply, NewState};
handle_info2(_Info, State) ->
    {noreply, State}.


%% ---------------------------------------------------------------------------
%% @doc called by a gen_server when it is about to terminate
-spec terminate(Reason, State) -> ok when
    Reason :: term(),
    State :: #cgw_battlefield_state{}.
%% ---------------------------------------------------------------------------
terminate(_Reason, State) ->
    #cgw_battlefield_state{pool_id = PoolId} = State,
    SceneId = data_cross_guild_war:get_config(scene_id),
    %% 关闭场景进程
    mod_scene_agent:close_scene(SceneId, PoolId),
    ok.


%% ---------------------------------------------------------------------------
%% @doc Convert process state when code is changed
-spec code_change(OldVsn, State, Extra) -> {ok, NewState} when
    OldVsn :: term(),
    State :: #cgw_battlefield_state{},
    Extra :: term(),
    NewState :: #cgw_battlefield_state{}.
%% ---------------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.


%% ---------------------------------------------------------------------------
%% @doc 创建所有城池的守护兽和战旗
-spec init_cities(PoolId) -> Cities when
    PoolId      :: integer(),
    Cities      :: [#cgw_city_info{}].
%% ---------------------------------------------------------------------------
init_cities(PoolId) ->
    LimitGroups = [],
    Group = 0,
    Owner = 0,
    SceneId = data_cross_guild_war:get_config(scene_id),
    CityIds = data_cross_guild_war:city_ids(),
    Cities = [
        init_city(CityId, SceneId, PoolId, Owner, Group, LimitGroups, []) ||
        CityId <- CityIds
    ],
    Cities.


%% ---------------------------------------------------------------------------
%% @doc 初始化从参战帮派信息
init_guild_info(GuildList) ->
    CityIds = data_cross_guild_war:city_ids(),
    init_guild_info(GuildList, CityIds,#{}).

init_guild_info([], _CityIds,GroupMap) ->
    GroupMap;
init_guild_info(_, [],GroupMap) ->
    GroupMap;
init_guild_info([Guild | T], [CityId | Less],GroupMap) ->
    case data_cross_guild_war:get_city_config(CityId) of
        #cross_guild_war_city{type_id = ?CITY_TYPE_SIDE, born_point = Pos,flag={_,Bx,By}} ->
            #cgw_guild_info{server_id=ServerId,fact_id=FactId,chief_id=ChiefId} = Guild,
            NewGuild = Guild#cgw_guild_info{init_city=CityId,born_pos = Pos, group = FactId,battle_pos={Bx,By}},
            put_guild_info(NewGuild),
            #cgw_guild_info{guild_id = GuildId} = NewGuild,
            GuildBattlefieldInfo = #cgw_guild_battlefield_info{
                guild_id = GuildId, pid = self()
            },
            ets:insert(?CGW_GUILD_BATTLEFIELD_PID, GuildBattlefieldInfo),
            %%通知拿团长头像数据
            MFA = {cross_guild_war_api, sync_guild_chief, [ChiefId,FactId,self()]},
            clusters_center:apply_cast(ServerId, MFA),
            init_guild_info(T, Less,maps:put(GuildId,FactId,GroupMap));
        _ ->
            init_guild_info([Guild | T], Less, GroupMap)
    end.


%% ---------------------------------------------------------------------------
%% @doc 创建城池守护兽和战旗
-spec init_city(CityId, SceneId, PoolId, Owner, Group, LimitGuilds, GuildName) -> City when
    CityId      :: integer(),
    SceneId     :: integer(),
    PoolId      :: integer(),
    Owner       :: integer(),
    Group       :: integer(),
    LimitGuilds :: [GuildId :: integer()],
    City        :: #cgw_city_info{},
    GuildName :: list() | binary().
%% ---------------------------------------------------------------------------
init_city(CityId, SceneId, PoolId, Owner, Group, LimitGuilds, GuildName) ->
    Broadcast = 1,
    CopyId = self(),
    #cross_guild_war_city{
        flag = {FlagId, FX, FY}, mon_list = MonList, type_id = CityType
    } = data_cross_guild_war:get_city_config(CityId),
    Args = case CityType of
        ?CITY_TYPE_MAIN ->
            [{group, Group}, {hurt_check, {?MODULE, hurt_check, [LimitGuilds]}}];
        ?CITY_TYPE_SIDE ->
            [{group, Group}]
    end,
    FlagName = cross_guild_war_api:get_mon_name(GuildName, FlagId),
    FlagArgs = [{is_be_atted, 0}, {mon_name, FlagName}] ++Args,
    #mon{hp_lim = FlagHp, type = FlagType} = data_mon:get(FlagId),
    FlagUid = lib_mon:sync_create_mon(
        FlagId, SceneId, PoolId, FX, FY, FlagType, CopyId, Broadcast, FlagArgs
    ),
    MonUids = lists:foldl(
        fun({MonId, X, Y}, Acc) ->
            #mon{type = Type} = data_mon:get(MonId),
            MonName = cross_guild_war_api:get_mon_name(GuildName, MonId),
            MonUid = lib_mon:sync_create_mon(
                MonId, SceneId, PoolId, X, Y, Type, CopyId, Broadcast, [{mon_name, MonName}|Args]
            ),
            [MonUid | Acc]
        end, [], MonList
    ),
    OwerName = case get_guild_info(Group) of
        #cgw_guild_info{guild_name = Name} -> Name;
        _ -> <<>>
    end,
    #cgw_city_info{
        city_id = CityId, owner = Owner, flag_uid = FlagUid
        ,flag_hp = FlagHp, mon_uids = MonUids, type = CityType
        ,flag_total_hp = FlagHp, owner_name = OwerName, group = Group
    }.


%% ---------------------------------------------------------------------------
%% @doc 检查玩家是否可以攻击主城怪物和战旗
hurt_check(AttUser, _SceneObject, [GroupList]) ->
    case AttUser of
        #ets_scene_user{battle_attr = #battle_attr{group = Group}} ->
            lists:member(Group, GroupList);
        _ ->
            false
    end.


%% ---------------------------------------------------------------------------
%% @doc 获取所有角色战场信息
-spec get_role_info() -> RoleInfoList when
    RoleInfoList      :: [#cgw_role_battle_info{}].
%% ---------------------------------------------------------------------------
get_role_info() ->
    [RoleInfo || {{role_info, _RoleId}, RoleInfo} <- erlang:get()].


%% ---------------------------------------------------------------------------
%% @doc 获取角色战场信息
-spec get_role_info(RoleId) -> Return when
    RoleId      :: integer(),
    Return      :: #cgw_role_battle_info{} | undefined.
%% ---------------------------------------------------------------------------
get_role_info(RoleId) ->
    erlang:get({role_info, RoleId}).


%% ---------------------------------------------------------------------------
%% @doc 保存角色战场信息
-spec put_role_info(RoleId, RoleInfo) -> Return when
    RoleId      :: integer(),
    RoleInfo    :: #cgw_role_battle_info{},
    Return      :: #cgw_role_battle_info{} | undefined.
%% ---------------------------------------------------------------------------
put_role_info(RoleId, RoleInfo) ->
    erlang:put({role_info, RoleId}, RoleInfo).


%% ---------------------------------------------------------------------------
%% @doc 获取参战帮派信息
get_guild_info(Group) ->
    erlang:get({guild_info, Group}).


%% --------------------------------------------------------------------------
%% @doc 保存参战帮派信息 20200801 改用分组
put_guild_info(GuildInfo = #cgw_guild_info{group = Group}) ->
    erlang:put({guild_info, Group}, GuildInfo).


%% ---------------------------------------------------------------------------
%% @doc 把战场中的所有玩家传送出去
-spec send_out_all_roles(RoleInfoList) -> ok when
    RoleInfoList    :: [#cgw_role_battle_info{}].
%% ---------------------------------------------------------------------------
send_out_all_roles(RoleInfoList) ->
    erlang:spawn(fun() -> send_out_all_roles_do(RoleInfoList, 1) end),
    ok.

send_out_all_roles_do([], _Num) ->
    ok;
send_out_all_roles_do([RoleInfo | T], Num) ->
    #cgw_role_battle_info{
        role_id = RoleId, scene = Scene, server_id = ServerId
    } = RoleInfo,
    case Scene > 0 of
        true ->
            %% 重置玩家相关数据并把玩家踢出场景
            MFA = {cross_guild_war_api, leave_scene_function, [RoleId]},
            clusters_center:apply_cast(ServerId, MFA);
        false ->
            ok
    end,
    case Num rem 10 of
        0 ->
            timer:sleep(500);
        _ ->
            ok
    end,
    send_out_all_roles_do(T, Num + 1).



%% ---------------------------------------------------------------------------
%% @doc 城池占领情况转化为战场结果
to_battlefield_result(State) ->
    #cgw_battlefield_state{zone_id = ZoneId,cities = Cities} = State,
    CityOwnerInfo = pack_city_owner_info(ZoneId, Cities, []),
    CityOwnerInfo.

pack_city_owner_info(_ZoneId, [], CityOwnerInfo) ->
    CityOwnerInfo;
pack_city_owner_info(ZoneId, [City | T], CityOwnerInfo) ->
    #cgw_city_info{city_id = CityId, owner = GuildId,group = Group} = City,
    NewCityOwnerInfo = case get_guild_info(Group) of
        Guild = #cgw_guild_info{} ->
            #cgw_guild_info{
                server_id = ServerId, guild_id = GuildId
                ,guild_name = GuildName, guild_power = GuildPower
                ,chief_id = ChiefId, chief_name = ChiefName
                ,chief_power = ChiefPower, server_num = ServerNum,chief_info=ChiefInfo
            } = Guild,
            CityOwner = #cgw_city_owner_info{
                zone_id = ZoneId, city_id = CityId
                ,server_id = ServerId, guild_id = GuildId
                ,guild_name = GuildName, guild_power = GuildPower
                ,chief_id = ChiefId, chief_name = ChiefName
                ,chief_power = ChiefPower, server_num = ServerNum,chief_info=ChiefInfo
            },
            [CityOwner | CityOwnerInfo];
        _ ->
            CityOwnerInfo
    end,
    pack_city_owner_info(ZoneId, T, NewCityOwnerInfo).



%% ---------------------------------------------------------------------------
%% @doc 发送占领城池奖励
send_city_owner_award([]) ->
    ok;
send_city_owner_award([Group | T]) ->
    case get_guild_info(Group) of
        #cgw_guild_info{
            sign_ups = SignUps,help_ups = HelpUps, own_cities = OwnCities
            ,server_id = ServerId, guild_id = GuildId
        } when OwnCities =/= [] ->
            send_city_owner_award(SignUps++HelpUps, ServerId, GuildId, OwnCities, []);
        _ ->
            ok
    end,
    send_city_owner_award(T).

send_city_owner_award([], ServerId, GuildId, Cities, RoleList) ->
    MFA = {?MODULE, send_occupy_award, [GuildId, Cities, RoleList]},
    RoleList =/= [] andalso clusters_center:apply_cast(ServerId, MFA),
    ok;
send_city_owner_award([SignUpRole | T], ServerId, GuildId, City, RoleList) ->
    #cgw_role_info{role_id = RoleId} = SignUpRole,
    case get_role_info(RoleId) of
        #cgw_role_battle_info{} -> %% 有进场参与
            NewRoleList = [SignUpRole | RoleList],
            send_city_owner_award(T, ServerId, GuildId, City, NewRoleList);
        _ ->
            send_city_owner_award(T, ServerId, GuildId, City, RoleList)
    end.


%% ---------------------------------------------------------------------------
%% @doc 发送占领城池奖励.在游戏服执行
-spec send_occupy_award(GuildId, OwnCities, RoleList) -> ok when
    GuildId     :: integer(),
    OwnCities   :: [CityId :: integer()],
    RoleList    :: [#cgw_role_info{}].
%% ---------------------------------------------------------------------------
send_occupy_award(GuildId, OwnCities, RoleList) ->
    MainCityId = data_cross_guild_war:get_config(main_city),
    CityId = case lists:member(MainCityId, OwnCities) of
        true -> MainCityId;
        false -> erlang:hd(OwnCities)
    end,
    CitiesNameStr = cities_name_string(lists:reverse(lists:sort(OwnCities)), []),
    send_occupy_award_do(GuildId, CityId, RoleList, CitiesNameStr).

send_occupy_award_do(_GuildId, _CityId, [], _CitiesNameStr) ->
    ok;
send_occupy_award_do(GuildId, CityId, [Role | T], CitiesNameStr) ->
    send_role_occupy_award(Role, GuildId, CityId, CitiesNameStr),
    send_occupy_award_do(GuildId, CityId, T, CitiesNameStr).

send_role_occupy_award(Role, _GuildId, CityId, CitiesNameStr) ->
    #cross_guild_war_city{
        name = _Name, type_id = CityType
    } = data_cross_guild_war:get_city_config(CityId),
    #cgw_role_info{role_id = RoleId, position = Position} = Role,
    Award = get_award(CityType, Position),
    Award =/= [] andalso begin
        lib_mail_api:send_sys_mail([RoleId], utext:get(6010003), utext:get(6010004, [CitiesNameStr]), Award),
        lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, host_behavior_reward, [Award])
    end.


%% 占领奖励
get_award(CityType, Position) ->
    case {CityType, Position} of
        {?CITY_TYPE_MAIN, ?POS_CHIEF} ->
            data_cross_guild_war:get_config(mteam_centerfield_lord_rewards);
        {?CITY_TYPE_MAIN, _} ->
            data_cross_guild_war:get_config(mteam_centerfield_member_rewards);
        {_, ?POS_CHIEF} ->
            data_cross_guild_war:get_config(mteam_surroundfield_lord_rewards);
        {_, _} ->
            data_cross_guild_war:get_config(mteam_surroundfield_member_rewards)
    end.

%% 所占领的城池的名字
cities_name_string([], NameList) ->
    util:join_bitstring(NameList, <<"、"/utf8>>);
cities_name_string([CityId | T], NameList) ->
    #cross_guild_war_city{
        name = Name
    } = data_cross_guild_war:get_city_config(CityId),
    NameList2 = [Name | NameList],
    cities_name_string(T, NameList2).


%% ---------------------------------------------------------------------------
%% @doc 广播战场信息
-spec broadcast_battlefield_info(Cities) -> ok when
    Cities     :: [#cgw_city_info{}].
%% ---------------------------------------------------------------------------
broadcast_battlefield_info(Cities) ->
    BinData = pack_cities(Cities),
    RoleInfoList = get_role_info(),
    [
        clusters_center:apply_cast(
            ServerId, {lib_server_send, send_to_uid, [RoleId, BinData]}
        ) || #cgw_role_battle_info{
            server_id = ServerId, role_id = RoleId, scene = Scene
        } <- RoleInfoList, Scene > 0
    ],
    ok.



%% ---------------------------------------------------------------------------
%% @doc 打包战场信息
-spec pack_cities(Cities) -> BinData when
    Cities     :: [#cgw_city_info{}],
    BinData    :: binary().
%% ---------------------------------------------------------------------------
pack_cities(Cities) ->
    DataList = pack_cities(Cities, []),
    {ok, BinData} = pt_601:write(60110, [DataList]),
    BinData.

pack_cities([], DataList) ->
    DataList;
pack_cities([City | T], DataList) ->
    #cgw_city_info{
        city_id = CityId, flag_hp = Hp, flag_total_hp = TotalHp,group=OwnGroup
        ,owner = GuildId, owner_name = GuildName, mon_uids = MonIdList
    } = City,
    NewDataList = [
        {CityId, Hp, TotalHp, GuildId,OwnGroup,GuildName, length(MonIdList)} | DataList
    ],
    pack_cities(T, NewDataList).


%% ---------------------------------------------------------------------------
%% @doc 打包帮派战斗分组
pack_guilds_group(Groups) ->
    DataList = pack_guilds_group(Groups, []),
    {ok, BinData} = pt_601:write(60111, [DataList]),
    BinData.

pack_guilds_group([], DataList) ->
    DataList;
pack_guilds_group([Group | T], DataList) ->
    NewDataList = case get_guild_info(Group) of
        #cgw_guild_info{guild_id = GuildId} ->
            [{GuildId, Group} | DataList];
        _ ->
            DataList
    end,
    pack_guilds_group(T, NewDataList).


%% ---------------------------------------------------------------------------
%% @doc 发送结算
send_settlement(_RoleInfoList, [],_BattleFieldResult) ->
    ok;
send_settlement(RoleInfoList, [Group | T],BattleFieldResult) ->
    case get_guild_info(Group) of
        #cgw_guild_info{
            own_cities = Cities, server_id = ServerId
        } ->
            OwnersL = cross_guild_war_mgr:pack_own_city_log(BattleFieldResult,[]),
            {ok, BinData} = pt_601:write(60114, [Cities,OwnersL]),
            RoleIdList = [
                 RoleId || #cgw_role_battle_info{
                     role_id = RoleId, scene = Scene, group = Group2
                } <- RoleInfoList, Scene > 0 andalso Group2 =:= Group
            ],
            clusters_center:apply_cast(
                ServerId, {lib_server_send, send_to_uid, [RoleIdList, BinData]}
            );
        _ ->
            ok
    end,
    send_settlement(RoleInfoList, T,BattleFieldResult).


%% ---------------------------------------------------------------------------
%% @doc 发送应援奖励
give_support_award(Groups) ->
    SupportList = give_support_award(Groups, []),
    [
        clusters_center:apply_cast(
            ServerId, {?MODULE, give_support_award_local, [SerSupportList]}
        ) || {ServerId, SerSupportList} <- SupportList
    ],
    ok.


give_support_award([], SupportList) ->
    SupportList;
give_support_award([Group | T], SupportList) ->
    NewSupportList = case get_guild_info(Group) of
        Guild = #cgw_guild_info{} ->
            #cgw_guild_info{
                supports = Supports, guild_id = GuildId, guild_name = GuildName
                ,own_cities = Cities
            } = Guild,
            Settlement = #support_settlement{
                guild_id = GuildId, guild_name = GuildName, cities = Cities
            },
            give_support_award_do(Settlement, Supports, SupportList);
        _ ->
            SupportList
    end,
    give_support_award(T, NewSupportList).

%% 每个帮派的应援按ServerId分开
give_support_award_do(_Settlement, [], SupportList) ->
    SupportList;
give_support_award_do(Settlement, [Support | T], SupportList) ->
    #cgw_support{server_id = ServerId, role_id = RoleId, num = Num} = Support,
    RoleSettlement = Settlement#support_settlement{role_id = RoleId, num = Num},
    NewSupportList = case lists:keytake(ServerId, 1, SupportList) of
        {value, {ServerId, ServerSuList}, LessList} ->
            NewServerSuList = [RoleSettlement | ServerSuList],
            [{ServerId, NewServerSuList} | LessList];
        _ ->
            [{ServerId, [RoleSettlement]} | SupportList]
    end,
    give_support_award_do(Settlement, T, NewSupportList).


%% ---------------------------------------------------------------------------
%% @doc 游戏服执行发送应援奖励
-spec give_support_award_local(SupportList) -> ok when
    SupportList :: [#support_settlement{}].
%% ---------------------------------------------------------------------------
give_support_award_local([]) ->
    ok;
give_support_award_local(SupportList) ->
    erlang:spawn(fun() -> give_support_award_local_do(SupportList, 1) end),
    ok.

give_support_award_local_do([], _Num) ->
    ok;
give_support_award_local_do([Support | T], Num) ->
    #support_settlement{
        role_id = RoleId, guild_name = GuildName, cities = Cities, num = SNum
    } = Support,
    MainCityId = data_cross_guild_war:get_config(main_city),
    MainAward = data_cross_guild_war:get_config(centerfield_encourage_rewards),
    SideAward = data_cross_guild_war:get_config(surroundfield_encourage_rewards),
    {
        Title, Content, Award
    } = case (not lists:member(MainCityId, Cities)) andalso Cities of
        false -> %% 有主城
            City = data_cross_guild_war:get_city_config(MainCityId),
            CityName = City#cross_guild_war_city.name,
            Content2 = utext:get(6010006, [GuildName, CityName, SNum]),
            {utext:get(6010005), Content2, n_times_award(MainAward, SNum)};
        [CityId | _] -> %% 有占领城池
            City = data_cross_guild_war:get_city_config(CityId),
            CityName = City#cross_guild_war_city.name,
            Content2 = utext:get(6010006, [GuildName, CityName, SNum]),
            {utext:get(6010005), Content2, n_times_award(SideAward, SNum)};
        _ -> %% 没有占领城池
            {utext:get(6010007), utext:get(6010008, [GuildName]), []}
    end,
    lib_mail_api:send_sys_mail([RoleId], Title, Content, Award),
    case Num rem 20 of
        0 -> timer:sleep(100);
        _ -> ok
    end,
    give_support_award_local_do(T, Num + 1).

%% 奖励增加N倍
n_times_award(AwardList, N) ->
    n_times_award(AwardList, N, []).
n_times_award([], _N, AwardList) ->
    AwardList;
n_times_award([Award | T], N, AwardList) ->
    {Type, GoodsId, Num} = Award,
    NewAwardList = [{Type, GoodsId, Num * N} | AwardList],
    n_times_award(T, N, NewAwardList).


%% ---------------------------------------------------------------------------
%% @doc 发送占领城池横幅
send_get_city_notice(SNum, GName, Name, State, CityName, CityId,FactId,FilterGroups) ->
    #cgw_battlefield_state{pool_id=PoolId,group=GroupMap} = State,
    {ok,Bin} = pt_601:write(60116,[FactId,SNum,GName,CityId]),
    SceneId = data_cross_guild_war:get_config(scene_id),
    CopyId = self(),
    lib_server_send:send_to_scene(SceneId, PoolId, CopyId, Bin),

    RoomName = data_cross_guild_war:get_room_name(1),
    Message = [SNum, GName, Name, RoomName, CityName, CityId],
    BinData = lib_chat:make_tv(?MOD_CGWAR, 1, Message),
    Mfa = {lib_server_send, send_to_all, [BinData]},
    Groups = maps:values(GroupMap),
    broadcast_notice(Groups, Mfa),

    {ok,Bin0} = pt_601:write(60119,[GName,CityName]),
    broadcast_notice_tip(Groups--FilterGroups,Bin0),

    ok.

broadcast_notice_tip([],_)->
    ok;
broadcast_notice_tip([Group|T],Bin)->
    case get_guild_info(Group) of
        #cgw_guild_info{server_id = ServerId,guild_id=GuildId} ->
            Mfa = {lib_server_send,send_to_guild,[GuildId,Bin]},
            clusters_center:apply_cast(ServerId, Mfa);
        _ ->
            ok
    end,
    broadcast_notice_tip(T, Bin).


broadcast_notice([], _Mfa) ->
    ok;
broadcast_notice([Group | T], Mfa) ->
    case get_guild_info(Group) of
        #cgw_guild_info{server_id = ServerId} ->
            clusters_center:apply_cast(ServerId, Mfa);
        _ ->
            ok
    end,
    broadcast_notice(T, Mfa).



%% ---------------------------------------------------------------------------
%% @doc 发送城池被攻击传闻
send_city_be_attacked_notice(Group,GuildId,CityName,CityId) ->
    Mfa = {
        lib_chat, send_TV, [{guild, GuildId}, ?MOD_CGWAR, 6, [CityName, CityId]]
    },
    {ok,Bin} = pt_601:write(60118,[CityName]),
    Mfa0 = {lib_server_send,send_to_guild,[GuildId,Bin]},
    case get_guild_info(Group) of
        #cgw_guild_info{server_id = ServerId} ->
            clusters_center:apply_cast(ServerId, Mfa),
            clusters_center:apply_cast(ServerId, Mfa0);
        _ ->
            ok
    end,
    ok.


%% ---------------------------------------------------------------------------
%% @doc 发送最终占领城池传闻
send_winner_notice(State, BattleFieldResult) ->
    #cgw_battlefield_state{group=GroupMap} = State,
    Groups = maps:values(GroupMap),
    MainCityId = data_cross_guild_war:get_config(main_city),
    #cross_guild_war_city{
        name = CityName
    } = data_cross_guild_war:get_city_config(MainCityId),
    RoomName = data_cross_guild_war:get_room_name(1),
    KeyPos = #cgw_city_owner_info.city_id,
    case lists:keyfind(MainCityId, KeyPos, BattleFieldResult) of
        #cgw_city_owner_info{
            server_num = ServerNum, guild_name = GuildName
        } ->
            Message = [ServerNum, GuildName, RoomName, CityName],
            BinData = lib_chat:make_tv(?MOD_CGWAR, 2, Message),
            Mfa = {lib_server_send, send_to_all, [BinData]},
            broadcast_notice(Groups, Mfa);
        _ ->
            ok
    end,
    ok.


%%给场景玩家都广播
do_send_guild_direction(FactId,City,State)->
    #cgw_battlefield_state{pool_id=PoolId} = State,
    SceneId = data_cross_guild_war:get_config(scene_id),
    CopyId = self(),
    Msg = [CopyId, ?APPLY_CAST_STATUS, ?MODULE, player_do_dirction, [FactId,City]],
    mod_scene_agent:apply_cast(SceneId,PoolId,lib_scene_agent,player_apply_cast,Msg).

player_do_dirction(Status,FactId,City)->
    #player_status{online = OnFlag,battle_attr=BA} = Status,
    #battle_attr{group = GroupId} = BA,
    case OnFlag of
        ?ONLINE_OFF_HOSTING when GroupId =:= FactId->
            #cross_guild_war_city{flag={_,TarX,TarY}}=data_cross_guild_war:get_city_config(City),
             offline_hosting_cgwar:host_behavior(direction,Status,{TarX,TarY});
        _->
            {ok,Status}
    end.


host_behavior_reward(Status,Award)->
    #player_status{online = OnFlag} = Status,
    case OnFlag of
        ?ONLINE_OFF_HOSTING->
            {ok, NStatus} = offline_hosting:host_behavior_reward(Status, Award),
            NStatus;
        _->
            NStatus = Status
    end,
    NStatus.


