%% ---------------------------------------
%% desc  : 放置经验公共进程逻辑模块
%% module: exp_dun_drop_logic.erl
%% author: hh
%% time  :20/01/17
%% ---------------------------------------

-module(exp_dun_drop_logic).

-export([
    role_login/2
    ,role_logout/3
    ,update_exp_dun_wave_point/2
    ,role_query_exp_drop_reward_info/1
    ,role_get_exp_drop_reward/2
    ,sync_drop_role_info_after_reward/2
    
    ,get_role_drop/1
    ,save_role_drop/1
    ,delete_role_drop/1
    ,update_role_drop/2

    ,update_calc_maps/2
    ,send_new_ref/1
    ,timer_calc/1
    ]).

-export([
    get_calc_group_id/2,
    get_role_group/1
    ]).

-include("exp_dun_drop.hrl").
-include("predefine.hrl").
-include("server.hrl").
-include("figure.hrl").
-include("common.hrl").
-include("errcode.hrl").
-include("def_module.hrl").
-include("def_goods.hrl").

% ------------- dict --------------

save_role_drop(ExpDunDrop) ->
    erlang:put(?DROP_ROLE_KEY(ExpDunDrop#drop_role.id), ExpDunDrop).

get_role_drop(RoleId) ->
    erlang:get(?DROP_ROLE_KEY(RoleId)).

delete_role_drop(RoleId) ->
    erlang:erase(?DROP_ROLE_KEY(RoleId)).

update_role_drop(RoleId, KvList) ->
    DropRole = case get_role_drop(RoleId) of
        D = #drop_role{} -> D;
        _                -> #drop_role{id = RoleId}
    end,
    NewDropRule = lib_exp_dun_drop:do_update_role_drop(KvList, DropRole),
    save_role_drop(NewDropRule),
    NewDropRule.

% ---------------------------------

%% 玩家上线主动发起同步检查
role_login(ExpDunDrop, #exp_dun_drop_state{calc_maps = CalcMaps} = State) ->
    case get_role_drop(ExpDunDrop#drop_role.id) of
        DropRole = #drop_role{off_point = OffPoints} ->
            Now = utime:unixtime(),
            {NewOffPoints, _NewDBOffPoints} =
             lists:foldl(fun({Start, End}, {AccOffPoints, DBOffPoints}) -> 
                if End == 0 -> 
                        NewAccOffPoints = lists:keystore(Start, 1, AccOffPoints, {Start, Now}),
                        {NewAccOffPoints, [{Start, Now}|DBOffPoints]};
                    true -> 
                        {AccOffPoints, DBOffPoints}
                end
            end, {OffPoints, []}, OffPoints),
            NewDropRole = DropRole#drop_role{off_point = lists:keysort(1, NewOffPoints)},
            % NewDBOffPoints =/= [] andalso lib_exp_dun_drop:db_update_drop_role(RoleId, [{off_points, NewDBOffPoints}]),
            save_role_drop(NewDropRole);
        _ -> save_role_drop(ExpDunDrop)
    end,
    %% 更新玩家组别
    NewCalcMaps = update_calc_maps(ExpDunDrop#drop_role.id, CalcMaps),
    State#exp_dun_drop_state{calc_maps = NewCalcMaps}.

%% 玩家下线更新
role_logout(RoleId, NowTime, State) ->
    case get_role_drop(RoleId) of
        DropRole = #drop_role{} ->
            lib_exp_dun_drop:db_update_drop_role(RoleId, [{off_points, [{NowTime, 0}]}]),
            NewDropRole = lib_exp_dun_drop:do_update_role_drop([{off_points, [{NowTime, 0}]}], DropRole),
            save_role_drop(NewDropRole);
        _ -> skip
    end,
    State.


%% 更新玩家最佳波数时间信息
%% 达到经验副本开放等级默认玩家可开启放置经验玩法
update_exp_dun_wave_point(RoleId, Wave) ->
    Time = utime:unixtime(),
    if 
        Wave == 0 ->
            InitRestBuffTime = data_exp_dun_drop:get_kv(14),
            lib_exp_dun_drop:db_update_role_wave(RoleId, Wave, Time, Time, [{{?TYPE_GOODS, ?EXP_DROP_GOODS_SUB_TYPE}, InitRestBuffTime}]),
            KvList = [{add_wave, {Wave, Time}}, {start_time, Time}, {calc_time, Time}, {update_extra_data, [{{?TYPE_GOODS, ?EXP_DROP_GOODS_SUB_TYPE}, replace, InitRestBuffTime}]}],
            update_role_drop(RoleId, KvList); 
        true      -> 
            lib_exp_dun_drop:db_update_role_wave(RoleId, Wave, Time, Time),
            KvList = [{add_wave, {Wave, Time}}],
            update_role_drop(RoleId, KvList)
    end,
    % ?INFO_TTY("~p", [Wave]),
    ok.

%% 玩家查看放置经验物品奖励
role_query_exp_drop_reward_info(RoleId) ->
    case lib_exp_dun_drop:calc_manual_exp_drop_reward(RoleId) of
        {true, CalcGoods} ->
            lib_exp_dun_drop:query_exp_drop_reward_return(RoleId, CalcGoods);
        {false, Errcode} ->
            lib_server_send:send_to_uid(RoleId, pt_610, 61050, [Errcode, 0, 0, []])
    end.

%% 玩家领取放置经验物品奖励
role_get_exp_drop_reward(RoleId, UpdateArgs) ->
    case lib_exp_dun_drop:get_exp_drop_reward(RoleId) of
        {true, CalcGoods, CostTime, RestBuffTime} -> %?ERR("~p", [CalcGoods]),
            lib_exp_dun_drop:send_exp_drop_reward(RoleId, CalcGoods, CostTime, RestBuffTime, UpdateArgs);
        {false, Errcode} ->
            lib_server_send:send_to_uid(RoleId, pt_610, 61050, [Errcode, 1, 0, []])
    end.

%% 开启新定时器
send_new_ref(OldRef) ->
    AfterTime = round(?EXP_DUN_DROP_INTERVAL/?EXP_DUN_DROP_GROUP*1000),
    Ref       = util:send_after(OldRef, AfterTime, self(), {'timer_calc'}),
    Ref.

%% 定时结算
timer_calc(State) ->
    #exp_dun_drop_state{
        calc_maps  = CalcMaps,
        start_time = StartTime,
        ref = OldRef 
    } = State,
    GroupRoles = get_calc_group(StartTime, CalcMaps),
    % ?ERR("~p", [GroupRoles]),
    calc_drop_logic(GroupRoles),    
    NewRef = send_new_ref(OldRef),
    State#exp_dun_drop_state{
        ref = NewRef
    }.

%% 计算本次定时超时后需要结算的玩家组别
%% 每次定时结束决定执行哪个组别结算
%% 大间隔为玩家个人结算间隔,如3600秒
%% 小间隔为每个组的结算间隔,如300秒
%% 决定组id规则: 持续时间(min) rem 大间隔(min) div 小间隔(min)  
%% 玩家的id与组别数量rem运算作为映射
get_calc_group(StartTime, CalcMaps) ->
    Now = utime:unixtime(),
    GroupId = get_calc_group_id(Now, StartTime),
    % ?ERR("group ~p", [GroupId]),
    maps:get(GroupId, CalcMaps, []).

get_calc_group_id(Now, StartTime) ->
    %% 结算进程开启以来持续分钟
    LastMin  = round((Now - StartTime)/?EXP_DUN_DROP_SEC_PER_MIN),
    %% 大间隔持续分钟
    RoleInterValMin  = round(?EXP_DUN_DROP_INTERVAL/?EXP_DUN_DROP_SEC_PER_MIN),
    %% 小间隔持续分钟
    GroupIntervalMin = round(?EXP_DUN_DROP_INTERVAL/?EXP_DUN_DROP_GROUP/?EXP_DUN_DROP_SEC_PER_MIN),
    GroupId = LastMin rem RoleInterValMin div GroupIntervalMin,
    GroupId.    

%% 分组玩家结算逻辑
calc_drop_logic(GroupRoles) ->
    Now = utime:unixtime(),
    {_GroupIndex, ChangeRoles} = 
    lists:foldl(fun(RoleId, {Acc, TempDropRoles}) -> 
        case get_role_drop(RoleId) of
            DropRole = #drop_role{} ->
                case exp_dun_drop_check:role_drop_check(DropRole, Now) of
                    true ->
                        % ?ERR("calc role ~p", [RoleId]),
                        {NewDropRole, DbList} = lib_exp_dun_drop:calc_drop_role(Acc, DropRole, Now),
                        {Acc+1, [{Acc+1, NewDropRole, DbList}|TempDropRoles]};
                    _ ->
                        {Acc, TempDropRoles}
                end;
            _ ->
                {Acc, TempDropRoles}
        end
    end, {1, []}, GroupRoles),
    spawn_link(fun() -> 
        lists:map(fun lib_exp_dun_drop:db_update_drop_role_do/1, ChangeRoles)
    end),
    ok.


%% 更新玩家组别
update_calc_maps(RoleId, CalcMaps) ->
    RoleGroupId  = get_role_group(RoleId),
    GroupList    = maps:get(RoleGroupId, CalcMaps, []),
     case lists:member(RoleId, GroupList) of
        true -> 
            CalcMaps;
        _    -> 
            NewGroupList = [RoleId|GroupList],
            maps:put(RoleGroupId, NewGroupList, CalcMaps)
    end.

%% 获取玩家对应组别
get_role_group(RoleId) ->
    RoleId rem ?EXP_DUN_DROP_GROUP.


%% 领取奖励后同步状态
sync_drop_role_info_after_reward(RoleId, UpdateArgs) ->
    case get_role_drop(RoleId) of
        DropRole = #drop_role{extra_data = ExtraData} ->
            % ?ERR("~p", [RoleId]),
            Now = utime:unixtime(),
            {NewDbUpdate, NewExtraData} = lib_exp_dun_drop:update_drop_role_extra_data(ExtraData, UpdateArgs),
            if NewDbUpdate == true ->
                    DbUpdateArgs = [{all_drop_role_info, Now, Now, NewExtraData}, {delete_all_goods, []}];
                true ->
                    DbUpdateArgs = [{all_drop_role_info_exclude_extra_data, Now, Now}, {delete_all_goods, []}]
            end,
            lib_exp_dun_drop:db_update_drop_role(RoleId, DbUpdateArgs),
            KvList = [
                {start_time, Now},
                {calc_time, Now},
                {delete_all_goods, []},
                {update_goods_limit, []},
                {update_reward_sign, ?EXP_DROP_NOT_REWARD},
                {remain, 0},
                {update_extra_data, UpdateArgs}
            ],
            NewDropRole = lib_exp_dun_drop:do_update_role_drop(KvList, DropRole),
            save_role_drop(NewDropRole);
        _ ->
            skip
    end.