%%%------------------------------------
%%% @Author  :  fwx
%%% @Created :  2017-11-20
%%% @Description: arena
%%%------------------------------------
-module(lib_arena).

-include("server.hrl").
-include("figure.hrl").
-include("errcode.hrl").
-include("common.hrl").
-include("arena.hrl").
-include("def_module.hrl").
-include("role.hrl").
-include("def_fun.hrl").
-include("predefine.hrl").
-include("attr.hrl").
-include("skill.hrl").
-include("fake_role.hrl").
-include("def_goods.hrl").
-include("sys_setting.hrl").
-include("daily.hrl").
-include("scene.hrl").
-include("def_event.hrl").
-include("rec_event.hrl").
-include("goods.hrl").

-export([
    after_random_rival_role/2       %% 随机对手
    , challenge_image_role/7        %% 挑战对手
    , after_challenge/9             %% 挑战完成-结算
    , ready_create_fake_battle/1    %% 准备创建战场
    , get_battle_attr/2             %% 获取战斗属性
    , create_fake_battle/4          %% 正式创建战场
    , can_quick/4                   %%
    , timer_hour/1
    , handle_event/2
]).

%----------------- 竞技场挑战冷却相关功能-----------
-export([check_cooltime_and_onlinetime/2
        ,get_arenarole_challenge_cd/1
        ,put_arenarole_challenge_cd/2
        ,get_db_arena_role_cd_to_processmap/1
        ,replace_db_arena_role_cd_byid/1]).
%-------------------------------------------------

%%%------------------------------------
%% @doc 随机对手
after_random_rival_role(Player, List) ->
    %% 只保存排名情况-减少数据
    erlang:put(arena_fake_list, List),
    Player.

% ------------------竞技场挑战冷却相关功能-------------------------------------------
check_onlinetime(PlayerStatus, _RoleId, NowCoolertime) ->
    OnlineTime = welfare_api:get_now_online_time(PlayerStatus), %玩家在线时长
    [Online_Limit] = data_arena:get_arena_value(?ONLINE_LIMIT),
    % Online_Limit = 310,
    if
        % OnlineTime < 0 ->
        %     {false, 1, NowCoolertime};
        OnlineTime < Online_Limit ->
            {false, 1, NowCoolertime};
        true ->
            % {true, 0, NowCoolertime}
            {true, 0, 0}    %玩家在线时间大于等于两小时统一把冷却设置为0
    end.


    
check_cooltime_and_onlinetime(PlayerStatus, RoleId) ->
    #arena_role_cd{last_use = Last_use, rest_time = Cooler_rest_time, trigger = Trigger} = get_arenarole_challenge_cd(RoleId),
    NowTime = utime:unixtime(),
    NowCoolertime = Cooler_rest_time + Last_use - NowTime, %这个数可能为负数
    [LimitCoolTime] = data_arena:get_arena_value(?MAX_COOLER), 
    % LimitCoolTime = 300,
    %新的一天重置冷却
    MidNight = utime:unixdate(),  
    if
        MidNight > Last_use ->
            put_arenarole_challenge_cd(RoleId, #arena_role_cd{roleid = RoleId, last_use = MidNight, rest_time = 0,  trigger = 0}),
            {true, 0, 0};
        true ->
            if
                % 超过冷却上限
                NowCoolertime >= LimitCoolTime->
                    check_onlinetime(PlayerStatus, RoleId, NowCoolertime);
                % 冷却时间少于限定需要检查是否已经被限制了
                Trigger == 1 andalso NowCoolertime > 0 ->
                    {false, Trigger, NowCoolertime};
                true ->
                    {true, 0, max(0, NowCoolertime)}
            end
    end.


% 进程字典存
get_arenarole_challenge_cd(RoleId) ->
    get({RoleId, arenarole_challenge_cd}).

put_arenarole_challenge_cd(RoleId, Arena_role_cd) ->
    put({RoleId, arenarole_challenge_cd}, Arena_role_cd),
    ok. 
% ------------------------冷却数据表持久化----------------------------
% ------------------------db read   --------------------------------
get_db_arena_role_cd(RoleId) ->
    case db:get_row(io_lib:format(?sql_select_db_arenacd, [RoleId])) of
        [Last_use, Cooler_rest_time, Trigger] ->
            #arena_role_cd{
                roleid = RoleId,          
                last_use = Last_use,        
                rest_time = Cooler_rest_time,
                trigger = Trigger
            };
        _ -> #arena_role_cd{roleid = RoleId, last_use = 0, rest_time = 0, trigger = 0}
    end.
get_db_arena_role_cd_to_processmap(RoleId) ->
    Arena_role_cd = get_db_arena_role_cd(RoleId),
    put_arenarole_challenge_cd(RoleId, Arena_role_cd).

%------------------ db write    --------------------
replace_db_arena_role_cd_byid(RoleId) ->
    Arena_role_cd = get_arenarole_challenge_cd(RoleId),
    replace_db_arena_role_cd(Arena_role_cd).

replace_db_arena_role_cd(Arena_role_cd) ->
    #arena_role_cd{
        roleid              = RoleId,         
        last_use = Last_use,        
        rest_time = Cooler_rest_time,
        trigger = Trigger
    } = Arena_role_cd,
    Sql = io_lib:format(?sql_replace_db_arenacd, [RoleId, Last_use, Cooler_rest_time, Trigger]),
    db:execute(Sql).
% -----------------------------------------------------------------------------------------------------


%%%------------------------------------
%% @doc 挑战对手  后续增加场景判断
challenge_image_role(Player, SelfRank, RivalId, RivalRank, Code, IsQuick, Sp) ->
    #player_status{id = RoleId, figure = Figure, x = X, y = Y, combat_power = Power} = Player,
    case do_check_challenge(Player, SelfRank, RivalRank, Sp) of
        {true, BattleScene} ->
            %% 宠物生活技能战力加成
            PowerCrushRatio  = lib_pet_api:pet_life_skill_about_arena_power(Player),
            PowerCrushRatio1 = max(0, PowerCrushRatio),
            PowerAfterAdd = round(Power * (1+PowerCrushRatio1/100)),
            %% 生成比赛数据
            ChallengeRole = #challenge_role{
                role_id = RoleId,
                self_name = Figure#figure.name,
                self_power = PowerAfterAdd,
                self_rank = SelfRank,
                rival_id = RivalId,
                rival_rank = RivalRank,
                self_scene = BattleScene,
                self_x = X, self_y = Y,
                sp = Sp
            },
            %% 创建战场
            mod_arena:cast_to_arena(create_battle, [ChallengeRole, Code, IsQuick]);
        {false, ErrCode} ->
            lib_server_send:send_to_uid(RoleId, pt_280, 28000, [ErrCode])
    end,
    Player.

%% 检查竞技挑战所需条件
do_check_challenge(Player, SelfRank, RivalRank, Sp) ->
    #player_status{
        id = RoleId,
        figure = #figure{lv = Lv},
        scene = Scene
    } = Player,
    
    OpenLev = lib_module:get_open_lv(?MOD_ARENA),
    %% 如果是在跨服场景，将镜像场景设置为对应的本服场景
    BattleScene =
    case lib_scene:center_city_map(Scene) of
        Scene  -> ?MIRROR_IMAGE_SCENE(Scene);
        Other ->
            case data_scene:get(Scene) of
                #ets_scene{cls_type = ?SCENE_CLS_TYPE_GAME} ->
                    ?MIRROR_IMAGE_SCENE(Scene);
                _ ->
                    ?MIRROR_IMAGE_SCENE(Other)
            end
    end,
    SceneData = data_scene:get(BattleScene),
    if
        Lv < OpenLev ->
            {false, ?ERRCODE(err280_lv_not_enough)};
        not is_record(SceneData, ets_scene) ->
            {false, ?ERRCODE(cannot_transferable_scene)};
        true ->
            case Sp > 0 of
                true  ->
                    %% 大神挑战
                    RankNeed = lib_arena_util:get_arena_cfg(?ARENA_SP_MAX_RANK, 50),
                    DailySpNum = lib_daily:get_count(RoleId, ?MOD_ARENA, ?ARENA_SP_NUM),
                    SpMaxNum = lib_arena_util:get_arena_cfg(?ARENA_SP_MAX_NUM, 3),
                    if
                        SelfRank =:= 0 orelse SelfRank > RankNeed ->
                            {false, ?ERRCODE(err280_sp_rank_need)};
                        DailySpNum >= SpMaxNum ->
                            {false, ?ERRCODE(err280_arena_num_not_enough)};
                        true ->
                            {true, BattleScene}
                    end;
                false ->
                    %% 普通挑战
                    DailyList = [
                        {?MOD_ARENA, ?DEFAULT_SUB_MODULE, ?ARENA_USE_NUM}
                        ,{?MOD_ARENA, ?DEFAULT_SUB_MODULE, ?ARENA_BUY_NUM}
                    ],
                    [
                        {_, UseNum},
                        {_, BuyNum}
                    ] = lib_daily:get_count(RoleId, DailyList),
                    case data_arena:get_arena_value(?ARENA_FREE_NUM_MAX) of
                        [] -> MaxFreeNum = 0;
                        [MaxFreeNum] -> skip
                    end,
                    ARENANum = MaxFreeNum - UseNum + BuyNum,
                    %% 当前展示的页面内
                    FakeList = case erlang:get(arena_fake_list) of
                                   List when is_list(List) -> List;
                                   _ -> []
                               end,
                    IsShow = lists:member(RivalRank, FakeList),
                    if
    
                        ARENANum =< 0 ->
                            {false, ?ERRCODE(err280_arena_num_not_enough)};
                        not IsShow ->
                            {false, ?ERRCODE(err280_rank_had_changed)};
                        true ->
                            {true, BattleScene}
                    end
            end
    end.

% 挑战完成后更新冷却时间
update_arena_challenge_cd(Player, RoleId) ->
    % ------------- 竞技场挑战冷却相关功能---------------------------------------------------------------------------------
    % 挑战完成增加玩家冷却
    NewLastUse = utime:unixtime(),
    Arena_role_cd = get_arenarole_challenge_cd(RoleId),
    LastUse = Arena_role_cd#arena_role_cd.last_use,
    LastRestTime = Arena_role_cd#arena_role_cd.rest_time,

    UpToNowCoolertime = LastRestTime + LastUse - NewLastUse,
    [Maxcool] = data_arena:get_arena_value(?MAX_COOLER),
    [Onetimecool] = data_arena:get_arena_value(?TIME_COOLER),
    NowOnlineTime = welfare_api:get_now_online_time(Player),
    [Online_Limit] = data_arena:get_arena_value(?ONLINE_LIMIT),
    % Online_Limit = 310,
    {NewTrigger, NewRestTime} = 
    if
        NowOnlineTime >= Online_Limit ->
            {0, 0};
        UpToNowCoolertime > 0 ->
            N = UpToNowCoolertime + Onetimecool,
            if
                N >= Maxcool ->
                    {1, N};
                true ->
                    {0, N}
            end;
        true ->
            {0, Onetimecool}
    end,
    % 新的冷却信息存回字典
    put_arenarole_challenge_cd(RoleId, Arena_role_cd#arena_role_cd{last_use = NewLastUse, rest_time = NewRestTime, trigger = NewTrigger}),
    %玩家在线时操作进程字典即可，不需要操作数据库，玩家登出的时候把数据写入数据库,整个在线期间玩家只会写入数据库一次减小开销，mod_server.erl中调用
    {NewTrigger, NewRestTime}.
    % -----------------------------------------------------------------------------------------------------------------------

%%%------------------------------------
%% @doc 挑战完成-结算
after_challenge(Player, Result, RoleMin, RankOld, HistoryRankOld, RivalPicture, RivalRank, IsQuick, Sp) ->
    #player_status{id = RoleId, figure = Figure, sid = Sid} = Player,
    #real_role{
        rank = Rank

    } = RoleMin,

    {NewTrigger, NewRestTime} = update_arena_challenge_cd(Player, RoleId),

    %% 突破奖励
    BreakReward = case Result == ?ARENA_WIN andalso (Rank < HistoryRankOld orelse HistoryRankOld == 0) of
        true  ->
            get_break_rank_reward(?IF(HistoryRankOld =:= 0, 3000, HistoryRankOld), Rank);
        false ->
            []
    end,
    %% 记录首胜
    case Result == ?ARENA_WIN of
        true  ->
            lib_daily:get_count(RoleId, ?MOD_ARENA, ?ARENA_FIRST_WIN) =:= 0 andalso
                lib_daily:increment(RoleId, ?MOD_ARENA, ?ARENA_FIRST_WIN);
        false ->
            ok
    end,
	%% 宠物生活技能-奖励保底
    PetGuarantee = lib_pet_api:pet_life_skill_about_arena_fail_compensation(Player),
    %% 挑战奖励
    ChallengeReward = get_challenge_reward(Result, RivalRank, Figure#figure.lv, PetGuarantee),
    ShowAward = lib_goods_api:make_reward_unique(BreakReward ++ ChallengeReward),
    RolesInfo = [
        lib_player:trans_to_picture(Figure),
        RivalPicture
    ],
    lib_server_send:send_to_sid(Sid, pt_280, 28003, [IsQuick, RolesInfo, Result, RankOld, Rank, ShowAward, Sp, NewRestTime, NewTrigger]),
    %% 计数器加1
    case Sp > 0 of
        true  ->
            %% 大神挑战
            lib_daily:increment(RoleId, ?MOD_ARENA, ?ARENA_SP_NUM);
        false ->
            %% 普通挑战
            lib_daily:increment(RoleId, ?MOD_ARENA, ?ARENA_USE_NUM)
    end,
    Produce = #produce{type = arena_challenge_reward, reward = ShowAward, remark = "竞技联赛"},
    NewPlayer = lib_goods_api:send_reward(Player, Produce),
    %% 日常活跃度
    lib_activitycalen_api:role_success_end_activity(RoleId, ?MOD_ARENA, 0),
    lib_task_api:arena_join(NewPlayer),
    mod_hi_point:success_end(RoleId, ?MOD_ARENA, 0, 1),
    custom_act_adventure:do_trigger_adventure_task(RoleId, ?MOD_ARENA, 0),
    lib_role_legion:trigger_task(NewPlayer, ?MOD_ARENA, 0),
    %% 成就
    Result == ?ARENA_WIN andalso
    begin
        lib_achievement_api:arena_event(Player, 1),
        lib_achievement_api:arena_rank_event(Player, Rank)
    end,
    lib_custom_act_slices:task_event(NewPlayer,?MOD_ARENA,0),
    custom_act_journey:trigger_act_task(RoleId, arena, 1),
    NewPlayer.

%% 战后奖励
get_challenge_reward(Result, RivalRank, RoleLv, PetGuarantee) ->
    case data_arena:get_battle_reward(RivalRank) of
        [WinReward, LoseReward] -> skip;
        _ -> WinReward = [{?TYPE_HONOUR, 0, 1}], LoseReward = [{?TYPE_HONOUR, 0, 1}]
    end,
    [WinExpReward, LoseExpReward] = data_arena:get_exp_reward(RoleLv),
    case Result of
        ?ARENA_FAIL when PetGuarantee == true ->
            ?IF(WinExpReward > 0, [{?TYPE_EXP, 0, WinExpReward}|WinReward], WinReward);
        ?ARENA_FAIL ->
            ?IF(LoseExpReward > 0, [{?TYPE_EXP, 0, LoseExpReward}|LoseReward], LoseReward);
        ?ARENA_WIN ->
            ?IF(WinExpReward > 0, [{?TYPE_EXP, 0, WinExpReward}|WinReward], WinReward)
    end.

%% 计算突破奖励
get_break_rank_reward(HistoryRank, NowRank) ->
    get_break_rank_reward_do(HistoryRank - 1, NowRank, []).

get_break_rank_reward_do(HistoryRank, NowRank, AwardList) when HistoryRank < NowRank ->
    AwardList;
get_break_rank_reward_do(HistoryRank, NowRank, AwardList) ->
    AwardList2 = case data_arena:get_break_rank_reward_cfg(HistoryRank) of
                     #arena_break_reward_cfg{award = Award} ->
                         lib_goods_api:make_reward_unique(Award ++ AwardList);
                     _ ->
                         AwardList
                 end,
    get_break_rank_reward_do(HistoryRank - 1, NowRank, AwardList2).

%%%------------------------------------
%% @doc 准备创建战场-先获取对方的战斗属性
ready_create_fake_battle(ChallengeRole) ->
    #challenge_role{rival_id = RivalId, rival_rank = RivalRank, role_id = RoleId} = ChallengeRole,
    if
        RivalId =/= 0 -> %% 不是机器人
            lib_player:apply_cast(RivalId, ?APPLY_CAST_STATUS, ?HAND_OFFLINE, lib_arena, get_battle_attr, [ChallengeRole]);
        true ->
            RivalFigure = lib_arena_util:make_fake_man_figure(RivalRank),
            RivalBattleAttr = get_attr_by_power(RivalFigure#figure.power),
            lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?HAND_OFFLINE, lib_arena, create_fake_battle, [ChallengeRole, RivalBattleAttr, RivalFigure])
    end.

%% 获取战斗属性（对手的角色进程）
get_battle_attr(RivalPlayer, ChallengeRole) ->
    #player_status{combat_power = Power, figure = Figure} = RivalPlayer,
    NewRivalBattleAttr = get_attr_by_power(Power),
    %% 跳回玩家的进程开始战斗
    lib_player:apply_cast(ChallengeRole#challenge_role.role_id, ?APPLY_CAST_STATUS, lib_arena, create_fake_battle, [ChallengeRole, NewRivalBattleAttr, Figure]).

%% 正式创建战场
create_fake_battle(Player, ChallengeRole, RivalBattleAttr, RivalFigure) ->
    #player_status{id = RoleId, figure = RoleFigure, arena_battle_pid = OldBattlePid, combat_power = SelfPower} = Player,
    #figure{career = RoleCareer, sex = RoleSex} = RoleFigure,

    #challenge_role{rival_id = RivalId, self_rank = SelfRank, rival_rank = RivalRank, self_scene = SelfScene, self_x = SelfX, self_y = SelfY} = ChallengeRole,
     #figure{power = RivalCombat} = RivalFigure,
    SelfBattleAttr = get_attr_by_power(SelfPower),
    case misc:is_process_alive(OldBattlePid) of
        true ->
            BattlePid = undefined,
            NewPlayer = Player;
        false ->    %% 根据角色职业获取技能(给满技能)
            RoleSkillsTemp = lib_skill_api:get_career_active_skill_ids(RoleCareer, RoleSex),
            RivalSkillsTemp = lib_skill_api:get_career_active_skill_ids(RivalFigure#figure.career, RivalFigure#figure.sex),
            RoleSkills = delete_anger_skill(RoleSkillsTemp, []),
            RivalSkills = delete_anger_skill(RivalSkillsTemp, []),
            RoleFakeData = [RoleFigure, SelfBattleAttr, RoleSkills, SelfPower, SelfScene, SelfX, SelfY],
            %% 根据我的当前坐标，给对方创建一个坐标
            {RivalX, RivalY} = umath:calc_position_around(SelfScene, SelfX, SelfY),
            RivalFakeData = [RivalFigure, RivalBattleAttr, RivalSkills, RivalCombat, SelfScene, RivalX, RivalY],
            case catch mod_battle_field:start(lib_arena_battle, [RoleId, RivalId, fake_man, RoleFakeData, RivalFakeData, SelfRank, RivalRank]) of
                BattlePid when is_pid(BattlePid) ->
                    NewPlayer = battle_field_create_done(Player, BattlePid, RoleId),
                    lib_server_send:send_to_uid(RoleId, pt_280, 28007, [RivalId, RivalCombat]);
                _ ->
                    mod_arena:cast_to_arena(set_battle_status, [[SelfRank, RivalRank], ?NOT_BATTLE_STATUS]),
                    BattlePid = undefined,
                    NewPlayer = battle_field_close(Player, init_error)
            end
    end,
    {ok, NewPlayer#player_status{arena_battle_pid = BattlePid}}.

%% 排除怒气技能，普攻技能只要第一个普攻技能
delete_anger_skill([], SKillList) ->
    urand:list_shuffle(SKillList);
delete_anger_skill([{SkillId, SkillLev} | T], SKillList) ->
    SKillList2 = case data_skill:get(SkillId, SkillLev) of
        #skill{is_anger = 1} -> %% 排除怒气技能
            SKillList;
        #skill{is_normal = 1, skill_link = [NormalSkillId | _LinkT]} -> %% 普攻技能
            lists:keystore(NormalSkillId, 1, SKillList, {NormalSkillId, SkillLev});
        _ ->
            [{SkillId, SkillLev} | SKillList]
    end,
    delete_anger_skill(T, SKillList2).

%% 战场创建成功后，进入场景
battle_field_create_done(Player, BattlePid, RoleId) ->
    #player_status{
        sid           = _Sid,
        scene         = OSceneId,
        scene_pool_id = OPoolId,
        copy_id       = OCopyId,
        x             = X,
        y             = Y,
        battle_attr   = #battle_attr{hp = Hp, hp_lim = HpLim}
    } = Player,
    NewPlayer = lib_player:setup_action_lock(Player, ?ERRCODE(err280_on_battle_state)),
    Args = [OSceneId, OPoolId, OCopyId, X, Y, Hp, HpLim],
    mod_battle_field:player_enter(BattlePid, RoleId, Args),
    NewPlayer.

battle_field_close(Player, _Resaon) ->
    Player.
%% ---------------------------------------------------------------------------
%% @doc 根据战力计算战斗属性
-spec get_attr_by_power(Power) -> BattleAttr when
    Power       :: integer(),
    BattleAttr  :: #battle_attr{}.
%% ---------------------------------------------------------------------------
get_attr_by_power(Power) ->
    AttrRatioList = data_fake_role:get_attr_ratio_by_power(),
    AttrList = [
        {AttrId, util:ceil(Ratio * Power)} || {AttrId, Ratio} <- AttrRatioList
    ],
    AttrList2 = [{?SPEED, umath:ceil((?SPEED_VALUE) * 1)} | AttrList],
    BattleAttr = lib_player_attr:attr_to_battle_attr(AttrList2),
    BattleAttr.

can_quick(_Status, _SelfRank, _RivalRank, 0)->
    false;
%% 宠物生活技能-判断是否可以秒杀战力比自己低的人。ps：之前是秒杀排名低的人
%%can_quick(Status, 1) ->
%%    Boolean = lib_pet_api:pet_life_skill_about_arena_quick_kill(Status),
%%    Boolean;
can_quick(Status, SelfRank, RivalRank, _)->
    % false.
    %%Todo  后续你们自己加
    OpDay = util:get_open_day(),
    [[{NeedD,NeedLv}]] = data_arena:get_arena_value(?ARENA_QUICK_CONF),
    #player_status{figure=#figure{lv=RoleLv}}=Status,
    OpDay>=NeedD andalso RoleLv>=NeedLv andalso SelfRank < RivalRank.

%% 定时处理
timer_hour(5) ->
    mod_arena:cast_to_arena(clean_redundancy, []);
timer_hour(_) ->
    ok.

%% 4点事件
handle_event(Status, #event_callback{type_id = ?EVENT_FOUR_CLOCK}) when is_record(Status, player_status) ->
    lib_task_api:arena_join(Status),
    {ok, Status};
handle_event(Status, _Event) ->
    {ok, Status}.