%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     沙盘争夺战-逻辑处理
%%% @end
%%%-------------------------------------------------------------------
-module(cross_point_fight_logic).
-author("liushl").

-include("cross_point_fight.hrl").
-include("server.hrl").
-include("predefine.hrl").
-include("errcode.hrl").
-include("figure.hrl").
-include("def_module.hrl").
-include("czone.hrl").
-include("guild.hrl").
%% API
-export([
    login/1             %% 登陆初始化
    , check/1           %% 逻辑检查
    , get_point_cfg/2   %% 配置
    , get_point/2       %% 获得据点
    , default_point/1
]).

-export([
    reply_to_role/5                 %% 跨服中心回复玩家处理
    , role_callback/4

    , attack_point/3                %% 攻打据点
    , attack_point_callback/2       %% 跨服中心回调进入副本
    , sweep_point_callback/2        %% 跨服中心回调扫荡副本
    , finish_dun_callback/2         %% 跨服中心回调:完成一个据点攻打

    , ask_point_info/2              %% 查看单个据点信息

    , get_seize_award_callback/2    %% 跨服中心回调领取据点占领奖励
]).

%%%-------------------------------------------------------------------
%%% @doc
%%%     登陆初始化
%%% @end
%%%-------------------------------------------------------------------
login(RoleId) ->
    SQL = io_lib:format(?DB_GET_ROLE_CROSS_POINT_INFO, [RoleId]),
    AllPointsInfo =
        [
            #role_cross_point_info{
                point_id            = PointId,
                single_max_score    = SingleMaxScore,
                total_score         = Score,
                total_hurt          = Hurt,
                value               = Value,
                award_time          = Award_time
            }
            || [PointId, SingleMaxScore, Score, Hurt, Value, Award_time] <- db:get_all(SQL)
        ],
    #role_cross_point{
        point_info = AllPointsInfo
    }.

%%%-------------------------------------------------------------------
%%% @doc
%%%     跨服中心回复玩家处理
%%% @end
%%%-------------------------------------------------------------------
reply_to_role(Node, RoleId, Module, Func, Arg) ->
    clusters_center:apply_cast(Node, ?MODULE, role_callback, [RoleId, Module, Func, Arg]).

role_callback(RoleId, Module, Func, Arg) ->
    case lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, Module, Func, [Arg]) of
        skip ->
            role_callback_offline(Func, RoleId, Arg);
        _ ->
            ok
    end.
%%%-------------------------------------------------------------------
%%% @doc
%%%     攻打据点
%%% @end
%%%-------------------------------------------------------------------
attack_point(Player, PointId, Sweep) ->
    Config = data_cross_point_fight:get_point_fight_point(PointId),
    case cross_point_fight_logic:check(
        [
            {bool, is_record(Config, base_cross_point_fight_point), ?MISSING_CONFIG},
            {can_sweep, Sweep, PointId, Player},
            {is_cost_enough, Player, Config},
            {is_free, Player, Sweep}
        ]
    ) of
        {false, Error} ->
            {false, Error};
        true ->
            cross_point_fight_center:attack_point(Player#player_status.id, PointId, Sweep),
            ok
    end.

%% 跨服中心回调进入副本
attack_point_callback(#player_status{id = RoleId} = Player, [PointId, WorldLv, DunId, HpRatio]) ->
    #base_cross_point_fight_point{cost = Cost}
        = data_cross_point_fight:get_point_fight_point(PointId),
    case lib_goods_api:cost_object_list_with_check(Player, Cost, attack_point, "") of
        {false, ErrorCode, _} ->
            {ok, Bin} = pt_604:write(60404, [ErrorCode]),
            lib_server_send:send_to_sid(Player#player_status.sid, Bin),
            Player;
        {true, Player1, _} ->
            CrossPoint = Player1#player_status.role_cross_point,
            Player2 = Player1#player_status{
                role_cross_point = CrossPoint#role_cross_point{dun_arg = {PointId, WorldLv, HpRatio}}
            },
            case lib_daily:get_count(RoleId, ?MOD_CROSS_POINT_FIGHT, 1, 1) of
                Value when is_integer(Value), Value >= 1 -> ignore;
                _ ->
                    lib_achievement_api:cross_point_fight_event(Player, 1),
                    lib_daily:increment(RoleId, ?MOD_CROSS_POINT_FIGHT, 1, 1)
            end,
            lib_dungeon:enter_dungeon(Player2, DunId),
            Player2
    end.

%% 跨服中心回调扫荡副本
sweep_point_callback(Player, [PointId, HpRatio]) ->
    CrossPoint = Player#player_status.role_cross_point,
    #role_cross_point_info{single_max_score = MaxScore, value = Value} =
        lists:keyfind(PointId, #role_cross_point_info.point_id, CrossPoint#role_cross_point.point_info),
    #base_cross_point_fight_point{type = Type, dun_id = DunId, cost = Cost}
        = data_cross_point_fight:get_point_fight_point(PointId),
    case lib_goods_api:cost_object_list_with_check(Player, Cost, attack_point, "") of
        {false, ErrorCode, _} ->
            {ok, Bin} = pt_604:write(60404, [ErrorCode]),
            lib_server_send:send_to_sid(Player#player_status.sid, Bin),
            Player;
        {true, Player1, _} ->
            case Type of
                ?POINT_TYPE_SAFE ->
                    %% 扫荡最大也不能超过当前BOSS剩余血量
                    ValidValue = min(Value, HpRatio),
                    ValidScore = min(MaxScore, data_cross_point_fight:get_point_fight_score(DunId, ValidValue));
                _ ->
                    ValidValue = Value,
                    ValidScore = MaxScore
                    
            end,
            cross_point_fight_center:finish_dun(Player#player_status.id, PointId, ValidScore, ValidValue, utime:unixtime(), 2),
            Player1
    end.

%% 跨服中心回调:完成一个据点攻打
%% 之所以由跨服中心回调来增加玩家身上的积分数据:需要判断当次攻打是当前赛季进入的还是上个赛季进入的，
%% 如果是上赛季进入的，需要忽略攻打积分，所以就出现以下流程
%% 副本进程->场景进程(可选)->跨服玩法进程->玩家进程->跨服玩法进程
%% 在线/或者离线
finish_dun_callback(Player, [PointId, FactionData, FactionDataAdd, Score, Value, Seize, SweepOrFight]) ->
    #player_status{
        role_cross_point = #role_cross_point{point_info = PointInfo} = CrossPoint,
        figure = #figure{name = Name},
        id = RoleId,
        sid = Sid,
        guild = Guild
    } = Player,
    case data_cross_point_fight:get_point_fight_point(PointId) of
        #base_cross_point_fight_point{
            attack_reward = AttackReward,
            name = PointName,
            cost = Cost,
            type = PointType,
            dun_id = DunId
        } ->
            One = case lists:keyfind(PointId, #role_cross_point_info.point_id, PointInfo) of
                #role_cross_point_info{} = _One ->
                    _One;
                false ->
                    #role_cross_point_info{point_id = PointId}
            end,
            NewOne = update_role_point_info(RoleId, Name, PointId, One, Score, Value, PointName),
            NewPointInfo = lists:keystore(PointId, #role_cross_point_info.point_id, PointInfo, NewOne),
            {ok, Bin} = pt_604:write(60410, [PointId, FactionData, FactionDataAdd, Score, Value, Seize]),
            lib_server_send:send_to_sid(Sid, Bin),
            Player1 = Player#player_status{role_cross_point = CrossPoint#role_cross_point{point_info = NewPointInfo}},
            %% 直接给奖励
            Player2 = lib_goods_api:send_reward(Player1, AttackReward, cross_point_dun),
            %% 增加军团积分
            {ok, Player3} = lib_role_legion:add_legion_score(Player2, Score),
            ta_agent_fire:cross_point_fight(Player3, [DunId, SweepOrFight, Score]),
            AddData =
                case PointType =:= ?POINT_TYPE_SAFE of
                    true  ->
                        Value;
                    false ->
                        Score
                end,
            lib_log_api:log_cross_point_fight([
                RoleId, Name, Guild#status_guild.id, czone_api:env_get_faction(), PointId, PointName, SweepOrFight,
                util:term_to_bitstring(Cost), util:term_to_bitstring(AttackReward), AddData, lib_role_legion:get_legion(Player2) + Score,
                utime:unixtime()
            ]),
            Player3;
        _ ->
            Player
    end.

%% 更新我在这个据点的积分
update_role_point_info(RoleId, Name, PointId, One, Score, Value, PointName) ->
    #role_cross_point_info{
        single_max_score = MaxScore,
        total_score = TotalScore,
        total_hurt = TotalHurt,
        value = MaxValue
    } = One,
    NewTotalScore = TotalScore + Score,
    NewTotalHurt = TotalHurt + Value,
    NewOne = One#role_cross_point_info{
        single_max_score = max(MaxScore, Score),
        total_score = NewTotalScore,
        total_hurt = NewTotalHurt,
        value = max(MaxValue, Value)
    },
    db_save_role_point_info(RoleId, NewOne),
    %% 用我的总积分去刷新跨服中心的榜单数据
    RoleInfo = #role_data{
        role_id = RoleId,
        name = Name,
        server_id = config:get_server_id(),
        server_num = config:get_server_num(),
        faction = czone_api:env_get_faction()
    },
    cross_point_fight_center:refresh_point_score(RoleInfo, PointId, NewTotalScore, NewTotalHurt),
    %% 在某个据点累计达到一定积分，全国广播一下
    TvScore = 500,
    case TotalScore < TvScore andalso NewTotalScore >= TvScore of
        true  ->
            case czone_api:env_get_zone() of
                #czone_svr_local{zone_id = ZoneId, faction = Faction} ->
                    lib_chat:send_TV({zone, ZoneId, Faction}, ?MOD_CROSS_POINT_FIGHT, 4, [Name, PointName]);
                _ ->
                    ok
            end;
        false ->
            ok
    end,
    NewOne.

%% 完成一个据点攻打离线处理: 因为是先进入副本才会增加积分，所以就算离线也只会处理一次
role_callback_offline(finish_dun_callback, RoleId, [PointId, _FactionScore, _FactionScoreAdd, Score, Value, _Seize, _SweepOrFight]) ->
    case data_cross_point_fight:get_point_fight_point(PointId) of
        #base_cross_point_fight_point{
            attack_reward = AttackReward,
            name = PointName
        } ->
            SQL = io_lib:format(?DB_GET_ROLE_CROSS_POINT_INFO_ONE, [RoleId, PointId]),
            One = case db:get_row(SQL) of
                      [SingleMaxScore, OldScore, OldHurt, OldValue, Award_time] ->
                          #role_cross_point_info{
                              point_id            = PointId,
                              single_max_score    = SingleMaxScore,
                              total_score         = OldScore,
                              total_hurt          = OldHurt,
                              value = OldValue,
                              award_time = Award_time
                          };
                      _ ->
                          #role_cross_point_info{point_id = PointId}
                  end,
            Name = lib_role:get_role_name(RoleId),
            update_role_point_info(RoleId, Name, PointId, One, Score, Value, PointName),
            %% 增加军团积分
            lib_role_legion:add_legion_score(RoleId, Score),
            %% 直接给奖励
            lib_goods_api:send_reward_by_id(AttackReward, cross_point_dun, RoleId);
        _ ->
            ok
    end;
    
role_callback_offline(_Func, _RoleId, _Arg) -> ok.
%%%-------------------------------------------------------------------
%%% @doc
%%%     查看单个据点信息
%%% @end
%%%-------------------------------------------------------------------
ask_point_info(Player, PointId) ->
    #role_cross_point{point_info = PointInfo}
        = Player#player_status.role_cross_point,
    case lists:keyfind(PointId, #role_cross_point_info.point_id, PointInfo) of
        #role_cross_point_info{award_time = AwardTimes, single_max_score = MaxScore} ->
            AwardTimes,
            case MaxScore > 0 of
                true  -> IsSweep = 1;
                false -> IsSweep = 0
            end;
        false ->
            AwardTimes = 0,
            IsSweep = 0
    end,
    cross_point_fight_center:ask_point_info(Player#player_status.sid, PointId, IsSweep, AwardTimes).

%%%-------------------------------------------------------------------
%%% @doc
%%%    跨服中心回调领取据点占领奖励
%%% @end
%%%-------------------------------------------------------------------
get_seize_award_callback(Player, [PointId, {false, Error}]) ->
    {ok, Bin} = pt_604:write(60407, [Error, PointId, 0]),
    lib_server_send:send_to_sid(Player#player_status.sid, Bin),
    Player;
get_seize_award_callback(Player, [PointId, {true, Num, AwardTime}]) ->
    case data_cross_point_fight:get_point_fight_point(PointId) of
        #base_cross_point_fight_point{reward = Reward} ->
            #role_cross_point{point_info = PointInfo}
                = CrossPoint = Player#player_status.role_cross_point,
            NewOne = case lists:keyfind(PointId, #role_cross_point_info.point_id, PointInfo) of
                #role_cross_point_info{award_time = AwardTimes} = One ->
                    One#role_cross_point_info{award_time = AwardTimes + Num};
                false ->
                    #role_cross_point_info{point_id = PointId, award_time = Num}
            end,
            db_save_role_point_info(Player#player_status.id, NewOne),
            {ok, Bin} = pt_604:write(60407, [1, PointId, AwardTime]),
            lib_server_send:send_to_sid(Player#player_status.sid, Bin),
            NewCrossPoint = CrossPoint#role_cross_point{
                point_info = lists:keystore(PointId, #role_cross_point_info.point_id, PointInfo, NewOne)
            },
            Player1 = Player#player_status{role_cross_point = NewCrossPoint},
            MultiAward = lib_goods_util:goods_object_multiply_by(Reward, Num),
            lib_goods_api:send_reward(Player1, MultiAward, point_seize_award);
        _ ->
            Player
    end.

%% 保存玩家据点数据
db_save_role_point_info(RoleId, Point) ->
    #role_cross_point_info{
        point_id            = PointId,
        single_max_score    = MaxScore,
        total_score         = TotalScore,
        total_hurt          = TotalHurt,
        value               = Value,
        award_time          = AwardTime
    } = Point,
    SQL = io_lib:format(?DB_SAVE_ROLE_CROSS_POINT_INFO, [RoleId, PointId, MaxScore, TotalScore, TotalHurt, Value, AwardTime]),
    db:execute(SQL),
    ok.
%%%-------------------------------------------------------------------
%%% @doc
%%%     逻辑检查
%%% @end
%%%-------------------------------------------------------------------
check([]) -> true;
check([H|T]) ->
    case check_do(H) of
        true  ->
            check(T);
        Other ->
            Other
    end.
%% 判断bool
check_do({bool, Bool, Err}) ->
    case Bool of
        true -> true;
        false -> {false, Err}
    end;
%% 是否与己方据点相连
check_do({is_self_connect, Faction, PointId, PointInfo}) ->
    case data_cross_point_fight:get_point_fight_point(PointId) of
        #base_cross_point_fight_point{link_point = LinkPoint, owner = Owner} ->
            case Owner =/= 0 andalso Owner =/= Faction of
                true  ->
                    %% 不可以攻打他国安全据点
                    {false, ?ERRCODE(err604_others_point)};
                false ->
                    case LinkPoint =:= [] of
                        true  ->
                            %% 说明是起始据点
                            true;
                        false ->
                            %% 中间据点，需要该剧点与本国据点相连
                            case lists:any(
                                fun(Id) ->
                                    LinkInfo = get_point(Id, PointInfo),
                                    LinkInfo#point_info.faction =:= Faction
                                end, LinkPoint
                            ) of
                                true  ->
                                    true;
                                false ->
                                    {false, ?ERRCODE(err604_not_connect)}
                            end
                    end
            end;
        _ ->
            {false, 0}
    end;
%% 攻打消耗是否足够
check_do({is_cost_enough, Player, Config}) ->
    case lib_goods_api:check_object_list(Player, Config#base_cross_point_fight_point.cost) of
        true  -> true;
        {false, Error} -> {false, Error}
    end;
%% 扫荡可行
check_do({can_sweep, Sweep, PointId, Player}) ->
    case Sweep of
        1  ->
            CrossPoint = Player#player_status.role_cross_point,
            case lists:keyfind(PointId, #role_cross_point_info.point_id, CrossPoint#role_cross_point.point_info) of
                #role_cross_point_info{single_max_score = MaxScore} when MaxScore > 0 ->
                    true;
                _ ->
                    {false, ?ERRCODE(err604_never_attack)}
            end;
        _ ->
            true
    end;
%% 可否进入副本
check_do({is_free, Player, Sweep}) ->
    case Sweep of
        1  ->
            true;
        _  ->
            case lib_player_check:check_all(Player) of
                true  -> true;
                {false, Error} -> {false, Error}
            end
    end;
check_do(_) ->
    {false, 0}.

%%%-------------------------------------------------------------------
%%% @doc
%%%     配置
%%% @end
%%%-------------------------------------------------------------------
get_point_cfg(Key, Default) ->
    case data_cross_point_fight:get_point_fight_kv(Key) of
        [Value] -> Value;
        _ -> Default
    end.

%% 获得据点
get_point(PointId, PointInfo) ->
    case lists:keyfind(PointId, #point_info.point_id, PointInfo) of
        #point_info{} = ThePoint ->
            ThePoint;
        false ->
            default_point(PointId)
    end.

default_point(PointId) ->
    #point_info{point_id = PointId}.

