%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     活动流程逻辑处理
%%% rr("../include/*.hrl").
%%% sys:get_state(guild_war_mgr).
%%% scene_mgr:get_scene_pool_ids(4100).
%%% erlang:process_info(whereis(guild_war_mgr)).
%%% @end
%%%-------------------------------------------------------------------
-module(guild_war_logic).
-author("liushl").

-include("guild_war.hrl").
-include("scene.hrl").
-include("figure.hrl").
-include("attr.hrl").
-include("def_module.hrl").
-include("language.hrl").
-include("server_kv.hrl").
-include("goods.hrl").
-include("server.hrl").
-include("predefine.hrl").
-include("common.hrl").
%% API
-export([
    cycle_add_score/2,              %% 循环给场景内玩家加积分
    role_cycle_add_score/0,         %% 场景进程处理加分
    cycle_boss_reborn/1,            %% 每条分线循环重生BOSS
    when_mon_born/3,                %% 出生监听
    when_mon_die/5,                 %% 死亡监听
    when_mon_be_hurt/4,             %% 受击监听器
    check_send_score_reward/3,      %% 加分的时候判断是否给突破奖励
    send_score_reward/3,
    act_settlement/1,               %% 结算
    send_reward/4,
    settlement_top_guild_in_role/2,
    send_personal_settlement_in_role/4,
    send_settlement_page/2,
    
    check_continue_kill_TV/1,       %% 判断连杀传闻
    check_end_kill_TV/2             %% 判断终结传闻
]).

%% 循环给场景内玩家加积分
cycle_add_score(SceneId, PoolId) ->
    mod_scene_agent:apply_cast(SceneId, PoolId, ?MODULE, role_cycle_add_score, []).

%% 场景进程处理加分
role_cycle_add_score() ->
    AllUser = lib_scene_agent:get_scene_user(),
    Score = ?GUILD_WAR_CYCLE_ADD_SCORE,
    [
        guild_war_mgr:add_score(RoleId, Score, cycle_auto)
        || #ets_scene_user{id = RoleId} <- AllUser
    ],
    ok.

%% 每条分线循环重生BOSS
cycle_boss_reborn(PoolInfo) ->
    #guild_war_pool{boss_alive = Alive, pool_id = PoolId} = PoolInfo,
    case Alive of
        true  ->
            ignore;
        false ->
            SceneId = ?GUILD_WAR_SCENE,
            MonId = ?GUILD_WAR_CYCLE_BOSS_ID,
            {X, Y} = ?GUILD_WAR_CYCLE_BOSS_REBORN_XY,
            Arg = [
                %% 出生监听
                {born_handler,        {?MODULE, when_mon_born, []}},
                %% 死亡监听器
                {die_handler,        {?MODULE, when_mon_die, []}},
                %% 受击监听器
                {be_hurt_handler,     {?MODULE, when_mon_be_hurt, []}},
                %% 怪物携带参数
                {mod_args, #{}}
            ],
            lib_mon:async_create_mon(MonId, SceneId, PoolId, X, Y, 0, ?GUILD_WAR_ROOM, 1, Arg)
    end.

%% 出生监听
when_mon_born(init, Object, []) ->
    #scene_object{
        id = Id,
        scene = SceneId,
        scene_pool_id = PoolId
    } = Object,
    guild_war_mgr:boss_birth(PoolId),
    %% 通知玩家，BOSS刷新
    {ok, Bin} = pt_405:write(40510, [Id]),
    lib_server_send:send_to_scene(SceneId, PoolId, Bin),
    ok.

%% 死亡监听
when_mon_die(#ob_act{object = Minfo}, _NewKlist, _Atter, _AtterSign, _DArgs) ->
    guild_war_mgr:boss_die(Minfo#scene_object.scene_pool_id),
    ok.

%% 受击监听器
when_mon_be_hurt(MonState, MonAtter, IsFirstAttack, _) ->
    #ob_act{
       object = #scene_object{
           scene = SceneId,
           scene_pool_id = PoolId,
           battle_attr = #battle_attr{hp = Hp}
       } = Object
    } = MonState,
    #mon_atter{
        id = RoleId,
        name = Name
    } = MonAtter,
    if
        IsFirstAttack ->
            %% 首刀
            AddScore = ?GUILD_WAR_BOSS_FIRST_ATTACK_SCORE,
            guild_war_mgr:add_score(RoleId, AddScore, boss_first_attack),
            lib_chat:send_TV({scene, SceneId, PoolId}, ?MOD_GUILD_WAR, 5, [Name, RoleId, AddScore]),
            ok;
        Hp =:= 0 ->
            %% 最后一刀
            AddScore = ?GUILD_WAR_BOSS_LAST_ATTACK_SCORE,
            guild_war_mgr:add_score(RoleId, AddScore, boss_last_attack),
            lib_chat:send_TV({scene, SceneId, PoolId}, ?MOD_GUILD_WAR, 6, [Name, RoleId, AddScore]),
            %% 通知玩家,BOSS死亡
            {ok, Bin} = pt_405:write(40511, []),
            lib_server_send:send_to_scene(SceneId, PoolId, Bin),
            ok;
        true ->
            %% 平常攻击 每X刀加Y分
            {X, Y} = ?GUILD_WAR_BOSS_NORMAL_ATTACK_SCORE,
            Count = get_attack_count(Object, RoleId),
            NewObject =
            case Count >= X of
                true  ->
                    guild_war_mgr:add_score(RoleId, Y, boss_normal_attack),
                    set_attack_count(Object, RoleId, 0);
                false ->
                    set_attack_count(Object, RoleId, Count + 1)
            end,
            MonState#ob_act{object = NewObject}
    end.

get_attack_count(#scene_object{mod_args = ModArgs}, RoleId) ->
    maps:get(RoleId, ModArgs, 0).

set_attack_count(#scene_object{mod_args = ModArgs} = Object, RoleId, Count) ->
    Object#scene_object{mod_args = ModArgs#{RoleId => Count}}.

%% 加分的时候判断是否给突破奖励
check_send_score_reward(_, Score, Score) -> ok;
check_send_score_reward(RoleId, NewScore, OldScore) ->
    LevelOld = data_guild_war:get_score_reward_level(OldScore),
    LevelNew = data_guild_war:get_score_reward_level(NewScore),
    LevelNew > LevelOld andalso
        begin
            Reward = lists:foldl(
                fun(LevelTemp, AwardTemp) ->
                    data_guild_war:get_score_level_reward(LevelTemp) ++ AwardTemp
                end, [], lists:seq(LevelOld+1, LevelNew)
            ),
            Produce = #produce{type = guild_war_score, reward = Reward, remark = lists:concat(["score=", NewScore])},
            lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, send_score_reward, [Produce, NewScore])
        end.

send_score_reward(Player, Produce, NewScore) ->
    case offline_hosting_api:is_offline_hosting(Player) of
        true  ->
            WithMail = Produce#produce{
                off_title = ?LAN_MSG(4050007),
                off_content = uio:format(?LAN_MSG(4050008), [NewScore])
            },
            lib_goods_api:do_send_mail(Player#player_status.id, WithMail, ?MOD_GUILD_WAR, offline),
            Player1 = offline_hosting:host_behavior_reward(Player, Produce#produce.reward),
            Player1;
        false ->
            lib_goods_api:send_reward(Player, Produce)
    end.

%% 结算
act_settlement(State) ->
    #guild_war{
        role_grade = RoleGrade            %% 公会成绩 #guild_war_guild{}
        , guild_grade = GuildGrade        %% 玩家成绩 #guild_war_role{}
        , ref_cycle_add_score = RefScore
        , ref_boss_reborn = RefBoss
    } = State,
    util:cancel_timer(RefScore),
    util:cancel_timer(RefBoss),
    %% 公会总体排名
    RankGuilds = sort_guild_rank(GuildGrade),
    case RankGuilds of
        [#guild_war_guild{guild_id = TopGuildId, guild_name = TopGuildName}|_] ->
            Now = utime:unixtime(),
            {TopChiefId, TopChiefName} = lib_guild_api:get_guild_chief(TopGuildId),
            %% 保存排名数据
            F = fun() ->
                db:execute("DELETE FROM guild_war_guild_rank"),
                SqlList = [io_lib:format(<<"(~p,~p,~p,~p)">>, [Rank, GuildId, Score, Now]) || #guild_war_guild{rank = Rank, guild_id = GuildId, score = Score} <- RankGuilds],
                SQL = io_lib:format(<<"INSERT INTO guild_war_guild_rank(rank,guild_id,score,time) VALUES ~ts">>, [string:join(SqlList, ",")]),
                db:execute(SQL),
                ServerKv = #server_kv{key = ?SERVER_KV_GUILD_WAR_TOP_GUILD, value = TopGuildId, time = Now},
                lib_server_kv:update_server_kv(ServerKv),
                ok
                end,
            case db:transaction(F) of
                ok ->
                    ok;
                Error ->
                    ?ERR("Error:~p", [Error])
            end,
            %% 上传百团大战结果
            camp_war_api:upload_guild_war_result(RankGuilds),
            lib_chat:send_TV({all}, ?MOD_GUILD_WAR, 2, [TopGuildName]),
            %% 玩家排名
            RankRoles  = sort_role_rank(dict:to_list(RoleGrade)),
            %% 给前端推结算
            PersonalSendAward = settlement_role(RankRoles, RankGuilds, TopChiefName, 1, []),
            %% 个人排名日志
            {_, RoleLogData} = lists:foldl(
                fun(One, {RoleRank, Temp}) ->
                    {
                        RoleRank + 1,
                        [[One#guild_war_role.role_id, One#guild_war_role.picture#rec_picture.name, RoleRank, One#guild_war_role.score,
                            One#guild_war_role.kill_rank, One#guild_war_role.max_kill, Now]|Temp]
                    }
                end, {1, []}, RankRoles
            ),
            RoleLogData =/= [] andalso lib_log_api:log_guild_war_role_rank(RoleLogData),
            %% 社团排名日志
            GuildLogData = [[One#guild_war_guild.guild_id, One#guild_war_guild.guild_name, One#guild_war_guild.rank, One#guild_war_guild.score, Now] || One <- RankGuilds],
            GuildLogData =/=[] andalso lib_log_api:log_guild_war_guild_rank(GuildLogData),
            %% 防止意外，发奖放到最后
            %% 分批发邮件
            spawn(
                fun() ->
                    %% 明星社团团长发奖励
                    IsIn = lists:keymember(TopChiefId, 1, PersonalSendAward),
                    settlement_top_guild(TopChiefId, IsIn),
                    send_personal_settlement(PersonalSendAward, 0)
                end
            );
        _ ->
            ok
    end,
    %% 15秒后清人
    guild_war_mgr:close_act().

%% 玩家结算
settlement_role(
    [#guild_war_role{role_id = RoleId, score = Score, guild_id = GuildId, kill_rank = KillRank}|T],
    [#guild_war_guild{guild_name = GuildName}|_] = RankGuilds,
    TopChiefName,
    Rank,
    Return
) ->
    MyGuildRank = case lists:keyfind(GuildId, #guild_war_guild.guild_id, RankGuilds) of
                      #guild_war_guild{rank = GuildRank} -> GuildRank;
                      _ -> 0
                  end,
    Info = [GuildName, TopChiefName, MyGuildRank, Rank, Score, KillRank],
    {ok, Bin} = pt_405:write(40508, Info),
    lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, send_settlement_page, [Bin]),
    settlement_role(T, RankGuilds, TopChiefName, Rank+1, [{RoleId, Rank, MyGuildRank, KillRank}|Return]);
settlement_role([], _, _, _, Return) -> Return.

%% 给玩家发界面界面
send_settlement_page(Player, Bin) ->
    #player_status{scene = Scene, sid = Sid} = Player,
    guild_war_api:is_guild_war_scene(Scene) andalso
        lib_server_send:send_to_sid(Sid, Bin),
    Player.

%% 给会长发额外奖励
settlement_top_guild(ChiefId, IsIn) ->
    case lib_player:apply_cast(ChiefId, ?APPLY_CAST_STATUS, ?MODULE, settlement_top_guild_in_role, [IsIn]) of
        skip ->
            settlement_top_guild_mail(ChiefId);
        _ ->
            ok
    end.
settlement_top_guild_mail(ChiefId) ->
    Title = ?LAN_MSG(4050005),
    Content = ?LAN_MSG(4050006),
    ChiefAward = ?GUILD_WAR_SETTLEMENT_TOP_GUILD_REWARD,
    lib_mail_api:send_sys_mail([ChiefId], ?MOD_GUILD_WAR, Title, Content, ChiefAward).
settlement_top_guild_in_role(Player, IsIn) ->
    settlement_top_guild_mail(Player#player_status.id),
    case offline_hosting_api:is_offline_hosting(Player) of
        true ->
            ChiefAward = ?GUILD_WAR_SETTLEMENT_TOP_GUILD_REWARD,
            Player1 = offline_hosting:host_behavior_reward(Player, ChiefAward),
            not IsIn andalso
                offline_hosting:notice_logout(?MOD_GUILD_WAR, 1, Player#player_status.id),
            Player1;
        false ->
            Player
    end.

%% 发个人排名奖励
send_personal_settlement([], _) -> ok;
send_personal_settlement([{RoleId, RoleRank, MyGuildRank, KillRank}|T], Count) ->
    case lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, send_personal_settlement_in_role, [RoleRank, MyGuildRank, KillRank]) of
        skip ->
            send_personal_settlement_mail(RoleId, RoleRank, MyGuildRank, KillRank);
        _ ->
            ok
    end,
    send_personal_settlement(T, Count + 1).

%% 在玩家进程结算
send_personal_settlement_in_role(Player, RoleRank, MyGuildRank, KillRank) ->
    case offline_hosting_api:is_offline_hosting(Player) of
        true  ->
            send_personal_settlement_mail(Player#player_status.id, RoleRank, MyGuildRank, KillRank),
            Reward = calc_reward(RoleRank, MyGuildRank, KillRank),
            Player1 = offline_hosting:host_behavior_reward(Player, Reward),
            %% 结算完下线
            offline_hosting:notice_logout(?MOD_GUILD_WAR, 1, Player#player_status.id),
            Player1;
        false ->
            case guild_war_api:is_guild_war_scene(Player#player_status.scene) of
                true  ->
                    %% 随机延迟一个时间发奖
                    spawn(
                        fun() ->
                            util:multiserver_delay(2, ?MODULE, send_reward, [Player#player_status.id, RoleRank, MyGuildRank, KillRank])
                        end
                    );
                false ->
                    send_personal_settlement_mail(Player#player_status.id, RoleRank, MyGuildRank, KillRank)
            end,
            Player
    end.

send_reward(RoleId, RoleRank, MyGuildRank, KillRank) ->
    RolePid = misc:get_player_process(RoleId),
    case misc:is_process_alive(RolePid) of
        true  ->
            Reward = calc_reward(RoleRank, MyGuildRank, KillRank),
            lib_goods_api:send_reward_by_id(Reward, guild_war, RoleId);
        false ->
            send_personal_settlement_mail(RoleId, RoleRank, MyGuildRank, KillRank)
    end.

send_personal_settlement_mail(RoleId, RoleRank, MyGuildRank, KillRank) ->
    Title = utext:get(4050001),
    case data_guild_war:get_kill_rank_award(KillRank) of
        [] ->
            Content = utext:get(4050010, [MyGuildRank, RoleRank]);
        _ ->
            Content = utext:get(4050002, [MyGuildRank, RoleRank, KillRank])
    end,
    Reward = calc_reward(RoleRank, MyGuildRank, KillRank),
    lib_mail_api:send_sys_mail([RoleId], ?MOD_GUILD_WAR, Title, Content, Reward).

calc_reward(RoleRank, MyGuildRank, KillRank) ->
    RoleAward  = data_guild_war:get_guild_war_role_reward(RoleRank),
    GuildAward = data_guild_war:get_guild_war_guild_reward(MyGuildRank),
    KillAward  = data_guild_war:get_kill_rank_award(KillRank),
    KillAward ++ GuildAward ++ RoleAward.
%% 榜单排序
sort_guild_rank(GuildGrade) ->
    F = fun(
        #guild_war_guild{score = ScoreA, time = TimeA},
        #guild_war_guild{score = ScoreB, time = TimeB}
    ) ->
        if
            ScoreA > ScoreB -> true;
            ScoreA < ScoreB -> false;
            true -> TimeA >= TimeB
        end
        end,
    List = lists:sort(F, GuildGrade),
    set_rank(List, [], 1).
set_rank([H|T], RankInfo, Rank) ->
    set_rank(T, [H#guild_war_guild{rank = Rank}|RankInfo], Rank+1);
set_rank([], RankInfo, _) ->
    lists:reverse(RankInfo).

sort_role_rank(RanksKV) ->
    Ranks = [Value || {_Key, Value} <- RanksKV],
    KillRank = sort_kill_rank(Ranks),
    F = fun(
        #guild_war_role{score = ScoreA, time = TimeA},
        #guild_war_role{score = ScoreB, time = TimeB}
    ) ->
        if
            ScoreA > ScoreB -> true;
            ScoreA < ScoreB -> false;
            true -> TimeA >= TimeB
        end
        end,
    lists:sort(F, KillRank).

sort_kill_rank(Roles) ->
    F = fun(
        #guild_war_role{max_kill = ScoreA, kill_time = TimeA},
        #guild_war_role{max_kill = ScoreB, kill_time = TimeB}
    ) ->
        if
            ScoreA > ScoreB -> true;
            ScoreA < ScoreB -> false;
            true -> TimeA >= TimeB
        end
        end,
    SortRoles = lists:sort(F, Roles),
    {SetRankRoles, _} =
    lists:foldl(
        fun(Role, {Temp, Rank}) ->
            %% 击杀人数大于0才能上榜
            case Role#guild_war_role.max_kill > 0 of
                true  ->
                    {
                        [Role#guild_war_role{kill_rank = Rank}|Temp],
                        Rank + 1
                    };
                false ->
                    {
                        [Role#guild_war_role{kill_rank = 0}|Temp],
                        Rank
                    }
            end
        end, {[], 1}, SortRoles
    ),
    SetRankRoles.

%% 判断连杀传闻
check_continue_kill_TV(Killer) ->
    #guild_war_role{
        continue_kill = ContinueKill,
        picture = #rec_picture{name = RoleName}
    } = Killer,
    case ContinueKill of
        10 ->
            lib_chat:send_TV({scene, ?GUILD_WAR_SCENE}, ?MOD_GUILD_WAR, 7, [RoleName]);
        20 ->
            lib_chat:send_TV({scene, ?GUILD_WAR_SCENE}, ?MOD_GUILD_WAR, 8, [RoleName]);
        50 ->
            lib_chat:send_TV({scene, ?GUILD_WAR_SCENE}, ?MOD_GUILD_WAR, 9, [RoleName]);
        100 ->
            lib_chat:send_TV({scene, ?GUILD_WAR_SCENE}, ?MOD_GUILD_WAR, 10, [RoleName]);
        _ ->
            ok
    end.

%% 判断终结传闻
check_end_kill_TV(Killer, Dead) ->
    #guild_war_role{
        picture = #rec_picture{name = DeadName},
        continue_kill = ContinueKill
    } = Dead,
    case ContinueKill >= 10 of
        true  ->
            #guild_war_role{picture = #rec_picture{name = KillerName}} = Killer,
            lib_chat:send_TV({scene, ?GUILD_WAR_SCENE}, ?MOD_GUILD_WAR, 11, [KillerName, DeadName, ContinueKill]);
        false ->
            ok
    end.