%% -------------------------------------
%% desc     :活动预约
%% module   :lib_activity_reserve.erl
%% author   :hh
%% time     :21/06/29
%% -------------------------------------

-module (lib_activity_reserve).

-export ([
    login/3
    ,get_act_reserve_status/1
    ,trigger_reserve_act/2
    ,trigger_reserve_act/3
    ,trigger_reserve_check/2

    ,enter_act/2
    ,act_reserve/2
    ,act_reserve_rewards/2
    ,handle_event/2
    ]).

-include ("activitycalen.hrl").
-include ("def_module.hrl").
-include ("predefine.hrl").
-include ("server.hrl").
-include ("errcode.hrl").
-include ("def_event.hrl").
-include ("rec_event.hrl").
-include ("common.hrl").
-include ("figure.hrl").
-include ("guild_war.hrl").
-include ("madness_land.hrl").
-include ("scene.hrl").

login(RoleId, Lv, Now) ->
    ActReserve = 
    case db:get_all(?sql_select_role_reserve, [RoleId]) of
        RawList = [_|_] ->
            transformat(Now, RawList, #{});
        _ -> #{}
    end,
    NewActReserve = get_today_act(Lv, Now),
    {ActReserve1, OldReserve} = calc_new_and_old_access_act(Now, ActReserve, NewActReserve),
    ActReserve =/= ActReserve1 andalso db_update_reserve(RoleId, ActReserve1),
    % ?ERR("~p ~p ~p", [ActReserve, ActReserve1, OldReserve]),
    db_delete_old_reserve(RoleId, OldReserve),
    ActReserve1.

calc_new_and_old_access_act(Now, RoleActReserve, TodayActReserve) ->
    RoleActReserve1 = maps:fold(fun(ActKey, Val, AccRoleReserve) ->
        case maps:get(ActKey, AccRoleReserve, false) of
            {ReserveTime, _, _} ->
                case utime:is_same_day(Now, ReserveTime) of
                    true ->
                        AccRoleReserve;
                    _ ->
                        maps:put(ActKey, {0, 0, 0}, AccRoleReserve)
                end;
            _ ->
                maps:put(ActKey, Val, AccRoleReserve)
        end
    end, RoleActReserve, TodayActReserve),
    {RoleActReserve2, DbDeleteList2} =
    maps:fold(fun(ActKey1, Val1, {AccRoleReserve1, DbDeleteList}) -> 
        case maps:get(ActKey1, TodayActReserve, false) of
            {_, _, _} ->
                AccRoleReserve2 = maps:put(ActKey1, Val1, AccRoleReserve1),
                {AccRoleReserve2, DbDeleteList};
            _ ->
                DbDeleteList1 = [ActKey1|DbDeleteList],
                {AccRoleReserve1, DbDeleteList1}
        end
    end, {#{}, []}, RoleActReserve1),
    {RoleActReserve2, DbDeleteList2}.

%% 删除旧活动
db_delete_old_reserve(_, []) ->
    ok;
db_delete_old_reserve(RoleId, [{Module, SubModule, SubAct}|Rest]) ->
    db:execute(?sql_delete_role_module_reserve, [RoleId, Module, SubModule, SubAct]),
    db_delete_old_reserve(RoleId, Rest).

db_update_reserve(RoleId, ActReserve) ->
    DbList = [[RoleId, Module, ModuleSub, AcSub, Reserve, Join, Rewards] ||  {{Module, ModuleSub, AcSub}, {Reserve, Join, Rewards}} <- maps:to_list(ActReserve)],
    DbList =/= [] andalso db:insert_batch(?sql_batch_replace_role_reserve, DbList),
    ok.

handle_event(Player, #event_callback{type_id = ?EVENT_ZERO_CLOCK}) ->
    #player_status{figure = #figure{lv = Lv}} = Player,
    Now = utime:unixtime(),
    NewActReserve = get_today_act(Lv, Now),
    %% 不更新数据,重登再做检查
    % lib_daily:set_count(RoleId, ?MOD_ACTIVITY, 5, 1),
    Player1 = Player#player_status{role_act_reserve = NewActReserve},
    get_act_reserve_status(Player1),
    {ok, Player1};
handle_event(Player, #event_callback{type_id = ?EVENT_LV_UP}) ->
    #player_status{figure = #figure{lv = Lv}} = Player,
    % ReserveOpenLv = lib_module:get_open_lv(?MOD_ACTIVITY, 2),
    if
        Lv == 50 ->
            Now = utime:unixtime(),
            ActReserve = get_today_act(Lv, Now),
            Player1 = Player#player_status{role_act_reserve = ActReserve},
            get_act_reserve_status(Player1),
            {ok, Player1};
        true ->
            {ok, Player}
    end.

transformat(_, [], ActReserve) ->
    ActReserve;
transformat(Now, [[Module, ModuleSub, AcSub, Reserve, Join, Rewards]|Rest], ActReserve) ->
    NewActReserve = maps:put({Module, ModuleSub, AcSub}, {Reserve, Join, Rewards}, ActReserve),
    % if Reserve == 0 ->
    %         maps:put({Module, ModuleSub, AcSub}, {Reserve, Join, Rewards}, ActReserve);
    %     true ->
    %         case utime:is_same_day(Reserve, Now) of
    %             true ->
    %                 maps:put({Module, ModuleSub, AcSub}, {Reserve, Join, Rewards}, ActReserve);
    %             _ ->
    %                 ActReserve
    %         end
    % end,
    transformat(Now, Rest, NewActReserve).

%% 获取今日新的活动预约数据
get_today_act(Lv, Now) ->
    Modules = data_activitycalen:get_all_reserve_module(),
    CheckList = [time, week, month, open_day, merge_day],
    {NowDay, _} = utime:unixtime_to_localtime(Now),
    ActReserve = get_today_act(#{}, Modules, Now, NowDay, Lv, CheckList),
    ActReserve.

get_today_act(ActReserve, [], _Now, _NowDay, _, _CheckList) -> ActReserve;
get_today_act(ActReserve, [{ModuleId, SubModule, AcSub}|Rest], Now, NowDay, Lv, CheckList) ->
    BaseAc = data_activitycalen:get_ac(ModuleId, SubModule, AcSub),
    case lib_activitycalen_util:do_check_ac_sub(BaseAc, CheckList) of
        true ->
            #base_ac{time_region = TimeRegion} = BaseAc,
            [{{StartH, StartM}, {_EndH, _EndM}}|_] = TimeRegion,
            _MinTime = utime:unixtime({NowDay, {StartH, StartM, 0}}),
            % OpenLv = lib_module:get_open_lv(ModuleId),
            % MaxTime = utime:unixtime({NowDay, {EndH, EndM, 0}}),
            if 
                % Lv < OpenLv -> %% 等级条件不满足
                %     get_today_act(ActReserve, Rest, Now, NowDay, Lv, CheckList);
                % Now >= MinTime -> %% 已开始的活动不能预约
                %     get_today_act(ActReserve, Rest, Now, NowDay, Lv, CheckList);
                true ->
                    NewActReserve = maps:put({ModuleId, SubModule, AcSub}, {0, 0, 0}, ActReserve),
                    get_today_act(NewActReserve, Rest, Now, NowDay, Lv, CheckList)
            end;
        _ ->
            get_today_act(ActReserve, Rest, Now, NowDay, Lv, CheckList)
    end.

trigger_reserve_act(Player, Module) when is_integer(Module) ->
    SceneCheck = false,
    Modules = data_activitycalen:get_all_reserve_module(),
    AccessModule = filter_access_module(Modules, Module, []),
    OpenModule = calc_open_act(AccessModule, []),
    case OpenModule of
        [{Module, SubModule, AcSub}|_] ->
            trigger_reserve_act(Player, {Module, SubModule, AcSub}, SceneCheck);
        _ ->
            Player
    end;
trigger_reserve_act(Player, ActKey) when is_tuple(ActKey) ->
    SceneCheck = false,
    trigger_reserve_act(Player, ActKey, SceneCheck).


trigger_reserve_act(Player, Module, SceneCheck) when is_integer(Module) ->
    Modules = data_activitycalen:get_all_reserve_module(),
    AccessModule = filter_access_module(Modules, Module, []),
    OpenModule = calc_open_act(AccessModule, []),
    case OpenModule of
        [{Module, SubModule, AcSub}|_] ->
            trigger_reserve_act(Player, {Module, SubModule, AcSub}, SceneCheck);
        _ ->
            Player
    end;
trigger_reserve_act(Player, ActKey, SceneCheck) when is_tuple(ActKey) ->
    #player_status{scene = SceneId, role_act_reserve = ActReserve, id = RoleId} = Player,
    {ReserveTime, Join, Rewards} = maps:get(ActKey, ActReserve, {0, 0, 0}),
    {Module, SubModule, AcSub} = ActKey,
    IsInActScene = is_in_act_scene(Module, SceneId),
    if
        ReserveTime == 0 ->
            Player;
        Join =/= 0 orelse Rewards =/= 0 -> %% 已记录参与或已领奖
            Player;
        SceneCheck andalso not IsInActScene ->
            Player;
        true ->
            NewActReserve = maps:put(ActKey, {ReserveTime, 1, Rewards}, ActReserve),
            db:execute(?sql_replace_into_role_reserve, [RoleId, Module, SubModule, AcSub, ReserveTime, 1, Rewards]),
            Player1 = Player#player_status{role_act_reserve = NewActReserve},
            get_act_reserve_status(Player1),
            Player1
    end.

is_in_act_scene(Module, SceneId) ->
    SceneType = act_scene_type(Module),
    case data_scene:get(SceneId) of
        #ets_scene{type = SceneType} -> true;
        _ -> false
    end.

act_scene_type(Module) ->
    if
        Module == ?MOD_GUILD orelse Module == ?MOD_GUILD_BOSS ->
            ?SCENE_TYPE_GUILD;
        Module == ?MOD_GUILD_WAR ->
            ?SCENE_TYPE_GWAR;
        Module == ?MOD_CAMP_WAR ->
            ?SCENE_TYPE_CAMP_WAR;
        Module == ?MOD_MADNESS_LAND ->
            ?SCENE_TYPE_MADNESS;
        Module == ?MOD_BEACH ->
            ?SCENE_TYPE_HOT_SPRING;
        Module == ?MOD_VOID_FAM ->
            ?SCENE_TYPE_VOID_FAM;
        Module == ?MOD_CGWAR ->
            ?SCENE_TYPE_CGW;
        Module == ?MOD_WASTELAND_WAR ->
            ?SCENE_TYPE_WASTELAND;
        true ->
            false
    end.


trigger_reserve_check(RoleId, Module) ->
    Modules = data_activitycalen:get_all_reserve_module(),
    AccessModule = filter_access_module(Modules, Module, []),
    OpenModule = calc_open_act(AccessModule, []),
    case OpenModule of
        [{Module, SubModule, SubAct}|_] ->
            lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, trigger_reserve_act, [{Module, SubModule, SubAct}]);
        _ -> skip
    end.

filter_access_module([], _Module, AccessModule) ->
    AccessModule;
filter_access_module([{Module, SubModule, SubAct}|Rest], Module, AccessModule) ->
    filter_access_module(Rest, Module, [{Module, SubModule, SubAct}|AccessModule]);
filter_access_module([_|Rest], Module, AccessModule) ->
    filter_access_module(Rest, Module, AccessModule).

calc_open_act([], Opens) ->
    Opens;
calc_open_act([{Module, SubMod, SubAct}|T], Opens)->
    case lib_activitycalen_api:check_mod_ac_start(Module, SubMod, SubAct) of
        true->
            calc_open_act(T, [{Module, SubMod, SubAct}|Opens]);
        _->
            calc_open_act(T, Opens)
    end.

enter_act(Player, ModuleInfo) when is_integer(Player) ->
    lib_player:apply_cast(Player, ?APPLY_CAST_STATUS, ?MODULE, enter_act, [ModuleInfo]);
enter_act(Player, ModuleInfo) when is_record(Player, player_status) ->
    case offline_hosting_api:is_offline_hosting(Player) of
        true ->
            Player;
        _ ->
            trigger_reserve_act(Player, ModuleInfo)
    end.

get_act_reserve_status(Player) ->
    #player_status{sid = Sid, figure = #figure{lv = Lv}, role_act_reserve = ActReserve} = Player,
    ActReserve1 =
    if 
        ActReserve == #{} ->
            get_today_act(Lv, utime:unixtime());
        true ->
            ActReserve
    end,
    ReserveInfo = maps:fold(fun({Module, SubModule, SubAct}, {ReserveTime, Join, Rewards}, AccInfo) -> 
        if ReserveTime == 0 ->
                [{Module, SubModule, SubAct, 0}|AccInfo];
            Join == 0 ->
                [{Module, SubModule, SubAct, 1}|AccInfo];
            Rewards == 0 ->
                [{Module, SubModule, SubAct, 2}|AccInfo];
            true ->
                [{Module, SubModule, SubAct, 3}|AccInfo]
        end
    end, [], ActReserve1),
    % ?ERR("~p", [ReserveInfo]),
    lib_server_send:send_to_sid(Sid, pt_157, 15718, [ReserveInfo]),
    ok.

act_reserve(Player, [Module, ModuleSub, SubAct]) ->
    #player_status{sid = Sid, id = RoleId, role_act_reserve = ActReserve} = Player,
    case maps:get({Module, ModuleSub, SubAct}, ActReserve, false) of
        {ReserveTime, Join, Rewards} ->
            CheckList = [time, time_region, week, month, open_day, merge_day],
            OpenCheck = 
            case lib_activitycalen_api:get_first_enabled_ac_id(Module, ModuleSub, [SubAct], CheckList) of
                {ok, _A} -> false;
                _ ->
                    {_NowDay, {NowH, NowM, _}} = DataTime = calendar:local_time(),
                    case lib_activitycalen_api:check_ac_start(Module, ModuleSub, DataTime) of
                        false -> %% 今天没开活动
                            false;
                        _ ->
                            #base_ac{time_region = TimeRegion} = data_activitycalen:get_ac(Module, ModuleSub, SubAct),
                            case TimeRegion of
                                [_|_] ->
                                    %% 未开启的活动才可预约
                                    TimeList=[true || {{H, M}, _} <- TimeRegion, (NowH < H) orelse (NowH == H andalso NowM < M)],
                                    Ret = lists:member(true, TimeList),
                                    Ret;
                                _ -> true
                            end
                    end
            end,
            if 
                not OpenCheck ->
                    lib_server_send:send_to_sid(Sid, pt_157, 15719, [?ERRCODE(err157_act_opening), Module, ModuleSub, SubAct, 0]),
                    ok;
                ReserveTime =/= 0 ->
                    lib_server_send:send_to_sid(Sid, pt_157, 15719, [?ERRCODE(err157_reserved), Module, ModuleSub, SubAct, 1]),
                    ok;
                Join =/= 0 ->
                    lib_server_send:send_to_sid(Sid, pt_157, 15719, [?ERRCODE(err157_have_joined), Module, ModuleSub, SubAct, 1]),
                    ok;
                Rewards =/= 0 ->
                    lib_server_send:send_to_sid(Sid, pt_157, 15719, [?ERRCODE(err157_have_rewards), Module, ModuleSub, SubAct, 1]),
                    ok;
                true ->
                    Now = utime:unixtime(),
                    NewActReserve = maps:put({Module, ModuleSub, SubAct}, {Now, Join, Rewards}, ActReserve),
                    db:execute(?sql_replace_into_role_reserve, [RoleId, Module, ModuleSub, SubAct, Now, Join, Rewards]),
                    Player1 = Player#player_status{role_act_reserve = NewActReserve},
                    lib_server_send:send_to_sid(Sid, pt_157, 15719, [?SUCCESS, Module, ModuleSub, SubAct, 1]),
                    {ok, Player1}
            end;
        _ ->
            lib_server_send:send_to_sid(Sid, pt_157, 15719, [?FAIL, Module, ModuleSub, SubAct, 0]),
            ok
    end.

act_reserve_rewards(Player, [Module, ModuleSub, SubAct]) ->
    #player_status{sid = Sid, id = RoleId, role_act_reserve = ActReserve} = Player,
    case maps:get({Module, ModuleSub, SubAct}, ActReserve, false) of
        false ->
            lib_server_send:send_to_sid(Sid, pt_157, 15720, [?ERRCODE(err157_not_reserve), Module, ModuleSub, SubAct]);
        {ReserveTime, Join, Rewards} ->
            if
                ReserveTime == 0 ->
                    lib_server_send:send_to_sid(Sid, pt_157, 15720, [?ERRCODE(err157_not_reserve), Module, ModuleSub, SubAct, Rewards]);
                Join == 0 ->
                    lib_server_send:send_to_sid(Sid, pt_157, 15720, [?ERRCODE(err157_not_joined), Module, ModuleSub, SubAct, Rewards]);
                Rewards =/= 0 ->
                    lib_server_send:send_to_sid(Sid, pt_157, 15720, [?ERRCODE(err157_have_rewards), Module, ModuleSub, SubAct, Rewards]);
                true ->
                    case data_activitycalen:get_reserve_rewards(Module, ModuleSub, SubAct) of
                        RewardList = [_|_] ->
                            Player1 = lib_goods_api:send_reward(Player, RewardList, act_reserve),
                            db:execute(?sql_replace_into_role_reserve, [RoleId, Module, ModuleSub, SubAct, ReserveTime, Join, 1]),
                            NewActReserve = maps:put({Module, ModuleSub, SubAct}, {ReserveTime, Join, 1}, ActReserve),
                            Player2 = Player1#player_status{role_act_reserve = NewActReserve},
                            lib_server_send:send_to_sid(Sid, pt_157, 15720, [?SUCCESS, Module, ModuleSub, SubAct, 3]),
                            {ok, Player2};
                        _A ->
                            lib_server_send:send_to_sid(Sid, pt_157, 15720, [?MISSING_CONFIG, Module, ModuleSub, SubAct, Rewards])
                    end
            end
    end.