%%% -------------------------------------------------------
%%% @author huangyongxing@yeah.net
%%% @doc
%%% 珍宝阁（留存抽奖）
%%% @end
%%% -------------------------------------------------------
-module(treasure_house).
-export([
        login/1
        ,query_all_info/1
        ,draw/2
        ,recv_count_award/2
        ,recv_task_award/2
        ,query_task_progress/1
        ,buy_ticket/2
        ,kill_mon/2
    ]).
% for debug
-export([
        find_thouse_task/2
        ,task_default_by_id/1
    ]).

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

%% 登录加载数据
login(Player = #player_status{id = RoleId}) ->
    % ?INFO("--------login----"),
    CurrPeriod = curr_period(),
    Sql = util:fmt_bin(?SQL_SELECT_TREASURE_HOUSE, [RoleId]),
    RoleTreasureHouse =
    case db:get_row(Sql) of
        DbData = [_|_] ->
            % ?INFO("------login load old data---"),
            NowTime = utime:unixtime(),
            OldRoleTreasureHouse = to_treasure_house(DbData),
            valid_role_data(RoleId, OldRoleTreasureHouse, CurrPeriod, NowTime);
        _ ->
            % ?INFO("------login no data---"),
            %% 初始化数据
            #treasure_house{
                period = CurrPeriod
                ,round = 1
                ,count = 0
                ,recv_round = 1
                ,recv_counts = []
                ,task_data = []
                ,time = 0
            }
    end,
    Player#player_status{treasure_house = RoleTreasureHouse}.

%% 珍宝阁综合信息查询
query_all_info(Player) ->
    % ?INFO("---------query_all_info-----"),
    #player_status{
        id = RoleId,
        treasure_house = OldRoleTreasureHouse
    } = Player,
    RoleTreasureHouse = valid_role_data(RoleId, OldRoleTreasureHouse),
    #treasure_house{
        period = CurrPeriod
        ,round = CurrRound
        ,count = CurrCount
        ,recv_round = RecvRound
        ,recv_counts = RecvCounts
    } = RoleTreasureHouse,
    case is_integer(CurrPeriod) of
        true  ->
            RoundMax = data_treasure_house:round_max(CurrPeriod),
            CliCurrRound = to_cli_round(CurrRound, RoundMax),
            CliRecvRound = to_cli_round(RecvRound, RoundMax),
            %% 服务端取其他配置都要以修正为前端显示的round来读取
            RecvRoundCurrCount = recv_round_curr_count(CurrPeriod, CurrRound, RecvRound, CurrCount),
            RoundCounts = data_treasure_house:round_counts(CurrPeriod, CliRecvRound),
            RoundRecvState = round_recv_state(RecvRoundCurrCount, RoundCounts, RecvCounts),
            %% 这里主要是防止历史数据错误，不用存储数据，在领取奖励的时候会存储
            {NewRecvRound, NewRecvCounts} =
            case check_recv_round_update(RecvRound, RoundRecvState) of
                RecvRound -> {RecvRound, RecvCounts};
                NextRecvRound -> {NextRecvRound, []}
            end,
            NewRoleTreasureHouse = RoleTreasureHouse#treasure_house{
                recv_round = NewRecvRound
                ,recv_counts = NewRecvCounts
            },
            NewPlayer = Player#player_status{treasure_house = NewRoleTreasureHouse},
            % ?INFO("----query_all_info- return ---------"),
            {ok, [CurrPeriod, CliCurrRound, CurrRound, CurrCount, NewRecvRound, RoundRecvState], NewPlayer};
        false ->
            {ok, [0, 0, 0, 0, 0, []]}
    end.

%% 抽取奖励
draw(Player, DrawCount) when DrawCount =< 10 andalso DrawCount > 0 ->
    % ?INFO("----------draw----"),
    #player_status{
        id = RoleId,
        treasure_house = OldRoleTreasureHouse
    } = Player,
    %% 校验数据（可能发生更新）
    RoleTreasureHouse = valid_role_data(RoleId, OldRoleTreasureHouse),
    #treasure_house{
        period = CurrPeriod
    } = RoleTreasureHouse,
    OpenDay = util:get_open_day(),
    NowPeriod = curr_period(),
    Result =
    case is_integer(NowPeriod) of
        true  ->
            case data_treasure_house:kv_get(ticket_goods) of
                GoodsTypeId when is_integer(GoodsTypeId) ->
                    {Date, _} = erlang:localtime(),
                    WeekDay = calendar:day_of_the_week(Date),
                    IsProbUp = is_prob_up(OpenDay, WeekDay),
                    DrawWeightIds = calc_draw_weight_list(CurrPeriod, IsProbUp),
                    case DrawWeightIds of
                        [_|_] ->
                            NowPlayer = Player#player_status{treasure_house = RoleTreasureHouse},
                            do_draw(NowPlayer, DrawCount, GoodsTypeId, DrawWeightIds);
                        _ ->
                            {false, ?ERRCODE(config_err)}
                    end;
                _ ->
                    {false, ?ERRCODE(config_err)}
            end;
        false ->
            {false, ?ERRCODE(config_err)}
    end,
    case Result of
        {false, ErrCode} ->
            NewPlayer = Player#player_status{treasure_house = RoleTreasureHouse},
            {ok, [ErrCode, []], NewPlayer};
        {ok, _Data, _NewPlayer} ->
            Result
    end;
draw(_Player, _) ->
    error.

do_draw(Player, DrawCount, GoodsTypeId, DrawWeightIds) ->
    % ?INFO("--------do_draw-----"),
    #player_status{
        id = RoleId
        ,treasure_house = RoleTreasureHouse
        ,figure = #figure{name = RoleName}
    } = Player,
    #treasure_house{
        period = CurrPeriod
        ,round = CurrRound
        ,count = CurrCount
    } = RoleTreasureHouse,
    Cycle = case data_treasure_house:kv_get(guarantee_cycle) of
        CycleX when is_integer(CycleX) andalso CycleX > 0 -> CycleX;
        _ -> 10
    end,
    %% 自动购买处理（无道具时，自动扣款替代）
    CostList = [{?TYPE_BIND_GOODS, GoodsTypeId, DrawCount}],
    %% case lib_goods_api:cost_object_list_with_check(Player, CostList, treasure_house, "") of
    case lib_goods_api:cost_objects_with_auto_buy(Player, CostList, treasure_house, "") of
        {true, PlayerCost, _} ->
            {NewRound, NewCount, AwardIdList} =
            calc_multi_draw(CurrPeriod, CurrRound, CurrCount, DrawCount, Cycle, DrawWeightIds, []),
            AwardList = draw_awards(AwardIdList),
            %% 传闻（自带检测是否需要传闻）
            NowTime = utime:unixtime(),
            TVList = check_broadcast(AwardIdList),
            case TVList of
                [_|_] ->
                    TvSubId = 1,
                    broadcast_draw_awards(TvSubId, RoleId, RoleName, TVList),
                    %% 记录到传闻日志进程
                    treasure_house_mgr:record_awards(RoleId, RoleName, TVList, NowTime);
                _ -> ignore
            end,
            %% 更新treasure_house数据
            NewRoleTreasureHouse = RoleTreasureHouse#treasure_house{
                round = NewRound
                ,count = NewCount
                ,time = NowTime
            },
            db_save_treasure_house(RoleId, NewRoleTreasureHouse),
            TmpPlayer = PlayerCost#player_status{treasure_house = NewRoleTreasureHouse},
            %% TODO 是否前置检查背包空间
            NewPlayer = lib_goods_api:send_reward(TmpPlayer, AwardList, treasure_house),
            {ok, [?SUCCESS, AwardIdList], NewPlayer};
        {false, ErrCode, NewPlayer} ->
            {ok, [ErrCode, []], NewPlayer}
    end.

%% 计算本轮多次抽奖
%% -> {Round, Count, Acc}
calc_multi_draw(Period, Round, Count, DrawCount, Cycle, DrawWeightIds, Acc) when DrawCount >= 0 ->
    RoundMax = data_treasure_house:round_max(Period),
    CliRound = to_cli_round(Round, RoundMax),
    RoundCountMax = data_treasure_house:round_count_max(Period, CliRound),
    if
        Count >= RoundCountMax ->
            calc_multi_draw(Period, Round + 1, 0, DrawCount, Cycle, DrawWeightIds, Acc);
        DrawCount =:= 0 ->
            %% DrawCount = 0 不能再发奖励，直接返回
            %% 但是要进入到这个接口判断一下，
            %% 目的是检测Count >= RoundCountMax，进行Round -> Round+1切换
            {Round, Count, Acc};
        true ->
            NewDrawWeightIds =
            case Count rem Cycle =:= 0 of
                true  -> rare_draw_ids(DrawWeightIds);
                false -> DrawWeightIds
            end,
            AwardId = urand:rand_with_weight(NewDrawWeightIds),
            NewAcc = [AwardId | Acc],
            NewCount = Count + 1,
            NewDrawCount = DrawCount - 1,
            calc_multi_draw(Period, Round, NewCount, NewDrawCount, Cycle, DrawWeightIds, NewAcc)
    end;
calc_multi_draw(_Period, Round, Count, _DrawCount, _Cycle, _DrawWeightIds, Acc) ->
    {Round, Count, Acc}.

draw_awards([AwardId|AwardIdList]) ->
    AwardList = data_treasure_house:draw_award(AwardId),
    AwardList ++ draw_awards(AwardIdList);
draw_awards([]) ->
    [].

rare_draw_ids(DrawWeightIds = [_|_]) ->
    RareDrawWeightIds = [
        Data
        ||
        Data = {_Weight, Id} <- DrawWeightIds,
        begin
                {IsRare, _} = data_treasure_house:draw_id_others(Id),
                IsRare > 0
        end
    ],
    case RareDrawWeightIds of
        [_|_] -> RareDrawWeightIds;
        _     -> DrawWeightIds
    end;
rare_draw_ids(_) ->
    [].

%% 领取次数保底奖励
recv_count_award(Player, GetCount) ->
    % ?INFO("-----recv_count_award----"),
    #player_status{
        id = RoleId,
        treasure_house = OldRoleTreasureHouse
    } = Player,
    %% 校验数据（可能发生更新）
    RoleTreasureHouse = valid_role_data(RoleId, OldRoleTreasureHouse),
    #treasure_house{
        period = CurrPeriod
        ,round = CurrRound
        ,count = CurrCount
        ,recv_round = RecvRound
        ,recv_counts = RecvCounts
    } = RoleTreasureHouse,
    NowPeriod = curr_period(),
    Result =
    case is_integer(NowPeriod) of
        true  ->
            RoundMax = data_treasure_house:round_max(CurrPeriod),
            CliCurrRound = to_cli_round(CurrRound, RoundMax),
            CliRecvRound = to_cli_round(RecvRound, RoundMax),
            RoundCounts = data_treasure_house:round_counts(CurrPeriod, CliCurrRound),
            RecvRoundCurrCount = recv_round_curr_count(CurrPeriod, CurrRound, RecvRound, CurrCount),
            RoundRecvState = round_recv_state(RecvRoundCurrCount, RoundCounts, RecvCounts),
            case lists:keyfind(GetCount, 1, RoundRecvState) of
                {_, ?THOUSE_COUNT_CAN_GET} ->
                    NowPlayer = Player#player_status{treasure_house = RoleTreasureHouse},
                    do_recv_count_award(NowPlayer, RoundRecvState, CliRecvRound, GetCount);
                {_, ?THOUSE_COUNT_NOT_ENOUGH} ->
                    {false, ?ERRCODE(err331_act_can_not_get)};
                {_, ?THOUSE_COUNT_GOT} ->
                    {false, ?ERRCODE(err331_already_get_reward)};
                _ ->
                    {false, ?FAIL}
            end;
        false ->
            {false, ?ERRCODE(config_err)}
    end,
    case Result of
        {false, ErrCode} ->
            NewPlayer = Player#player_status{treasure_house = RoleTreasureHouse},
            {ok, [ErrCode], NewPlayer};
        {ok, _Data, _NewPlayer} ->
            Result
    end.

do_recv_count_award(Player, RoundRecvState, CliRecvRound, GetCount) ->
    % ?INFO("-----do_recv_count_award----"),
    #player_status{
        id = RoleId,
        sid = Sid,
        treasure_house = RoleTreasureHouse
    } = Player,
    #treasure_house{
        period = CurrPeriod
        ,round = CurrRound
        ,count = CurrCount
        ,recv_round = RecvRound
        ,recv_counts = RecvCounts
    } = RoleTreasureHouse,
    %% 是不是可发放
    AwardList = data_treasure_house:draw_count_award(CurrPeriod, CliRecvRound, GetCount),
    CanGive = lib_goods_api:can_give_goods(Player, AwardList),
    case CanGive =/= true of
        true  ->
            {false, ?ERRCODE(err150_no_cell)};
        false ->
            %% 数据更新存储
            NewRoundRecvState = lists:keystore(GetCount, 1, RoundRecvState, {GetCount, ?THOUSE_COUNT_GOT}),
            {NewRecvRound, NewRecvCounts} =
            case check_recv_round_update(RecvRound, NewRoundRecvState) of
                RecvRound -> {RecvRound, [GetCount | RecvCounts]};
                NextRecvRound -> {NextRecvRound, []}
            end,
            case NewRecvRound =/= RecvRound of
                true  ->
                    send_treasure_house_info(Sid,
                        CurrPeriod, CurrRound, CurrCount, NewRecvRound,
                        NewRoundRecvState
                    );
                false ->
                    ignore
            end,
            NewRoleTreasureHouse = RoleTreasureHouse#treasure_house{
                recv_round = NewRecvRound
                ,recv_counts = NewRecvCounts
            },
            db_save_treasure_house(RoleId, NewRoleTreasureHouse),
            %% 发放奖励
            TmpPlayer = Player#player_status{treasure_house = NewRoleTreasureHouse},
            NewPlayer = lib_goods_api:send_reward(TmpPlayer, AwardList, treasure_house),
            {ok, [?SUCCESS], NewPlayer}
    end.

%% 抽奖券任务领取奖励
recv_task_award(Player, TaskId) ->
    % ?INFO("---recv_task_award-----"),
    #player_status{
        id = RoleId,
        treasure_house = OldRoleTreasureHouse
    } = Player,
    %% 校验数据（可能发生更新）
    RoleTreasureHouse = valid_role_data(RoleId, OldRoleTreasureHouse),
    #treasure_house{
        task_data = TaskData
    } = RoleTreasureHouse,
    GoodsTypeId = data_treasure_house:kv_get(ticket_goods),
    Result =
    case is_integer(GoodsTypeId) of
        true  ->
            THouseTask = find_thouse_task(TaskId, TaskData),
            #thouse_task{finish_times = FinishTimes, recv_times = RecvTimes} = THouseTask,
            case FinishTimes > RecvTimes of
                true  ->
                    % ?INFO("---recv_task_award : RecvTimes:~w=>~w--", [RecvTimes, RecvTimes+1]),
                    NewTHouseTask = THouseTask#thouse_task{
                        recv_times = RecvTimes + 1
                    },
                    NewTaskData = lists:keystore(TaskId, #thouse_task.task_id, TaskData, NewTHouseTask),
                    NewRoleTreasureHouse = RoleTreasureHouse#treasure_house{
                        task_data = NewTaskData
                        ,time = utime:unixtime()
                    },
                    db_save_treasure_house(RoleId, NewRoleTreasureHouse),
                    TmpPlayer = Player#player_status{
                        treasure_house = NewRoleTreasureHouse
                    },
                    AwardList = [{?TYPE_BIND_GOODS, GoodsTypeId, 1}],
                    NewPlayer = lib_goods_api:send_reward(TmpPlayer, AwardList, treasure_house),
                    {ok, [?SUCCESS], NewPlayer};
                false ->
                    {false, ?ERRCODE(err331_act_can_not_get)}
            end;
        false ->
            {false, ?ERRCODE(config_err)}
    end,
    case Result of
        {false, ErrCode} ->
            NewPlayer2 = Player#player_status{treasure_house = RoleTreasureHouse},
            {ok, [ErrCode], NewPlayer2};
        {ok, _Data, _NewPlayer} ->
            Result
    end.

%% 抽奖券任务进度
query_task_progress(Player) ->
    #player_status{
        id = RoleId,
        treasure_house = OldRoleTreasureHouse
    } = Player,
    %% 校验数据（可能发生更新）
    RoleTreasureHouse = valid_role_data(RoleId, OldRoleTreasureHouse),
    #treasure_house{
        task_data = TaskData
    } = RoleTreasureHouse,
    AllTask = data_treasure_house:ticket_tasks(),
    TaskProgressList = to_cli_task_data(TaskData, AllTask),
    % ?INFO("--query task_progress-:~w", [TaskProgressList]),
    case RoleTreasureHouse =/= OldRoleTreasureHouse of
        true  ->
            NewPlayer = Player#player_status{treasure_house = RoleTreasureHouse},
            {ok, [TaskProgressList], NewPlayer};
        false ->
            {ok, [TaskProgressList]}
    end.

%% 抽奖券购买（任务）
buy_ticket(Player, TaskId) ->
    % ?INFO("-------buy_ticket-"),
    #player_status{
        id = RoleId,
        treasure_house = OldRoleTreasureHouse
    } = Player,
    %% 校验数据（可能发生更新）
    RoleTreasureHouse = valid_role_data(RoleId, OldRoleTreasureHouse),
    #treasure_house{
        task_data = TaskData
    } = RoleTreasureHouse,
    AllTask = data_treasure_house:ticket_tasks(),
    GoodsTypeId = data_treasure_house:kv_get(ticket_goods),
    Result =
    case is_integer(GoodsTypeId) of
        true  ->
            case lists:keyfind(TaskId, 1, AllTask) of
                {_, DailyMax, _TaskDetails = {buy, CostList}} when DailyMax > 0 ->
                    case find_thouse_task(TaskId, TaskData) of
                        THouseTask = #thouse_task{finish_times = FinishTimes} when FinishTimes < DailyMax ->
                            NowPlayer = Player#player_status{treasure_house = RoleTreasureHouse},
                            do_buy_ticket(NowPlayer, THouseTask, TaskData, CostList, GoodsTypeId);
                        _Over ->
                            {false, ?ERRCODE(err150_num_limit)}
                    end;
                _ ->
                    {false, ?FAIL}
            end;
        false ->
            {false, ?ERRCODE(config_err)}
    end,
    case Result of
        {false, ErrCode} ->
            NewPlayer = Player#player_status{treasure_house = RoleTreasureHouse},
            {ok, [ErrCode], NewPlayer};
        {ok, _Data, _NewPlayer} ->
            Result
    end.

do_buy_ticket(Player, THouseTask, TaskData, CostList, GoodsTypeId) ->
    % ?INFO("-------do_buy_ticket----"),
    case lib_goods_api:cost_object_list_with_check(Player, CostList, treasure_house, "") of
        {true, PlayerCost, _} ->
            #player_status{id = RoleId, treasure_house = RoleTreasureHouse} = Player,
            #thouse_task{task_id = TaskId, finish_times = FinishTimes, recv_times = RecvTimes} = THouseTask,
            %% 更新保存数据
            %% 购买没有额外的任务奖励，直接是购买所得，所以recv_times也要加
            NewTHouseTask = THouseTask#thouse_task{
                finish_times = FinishTimes + 1
                ,recv_times = RecvTimes + 1
            },
            NewTaskData = lists:keystore(TaskId, #thouse_task.task_id, TaskData, NewTHouseTask),
            NewRoleTreasureHouse = RoleTreasureHouse#treasure_house{
                task_data = NewTaskData
                ,time = utime:unixtime()
            },
            db_save_treasure_house(RoleId, NewRoleTreasureHouse),
            AwardList = [{?TYPE_BIND_GOODS, GoodsTypeId, 1}],
            TmpPlayer = PlayerCost#player_status{treasure_house = NewRoleTreasureHouse},
            %% 发放奖励
            NewPlayer = lib_goods_api:send_reward(TmpPlayer, AwardList, treasure_house),
            {ok, [?SUCCESS], NewPlayer};
        {false, ErrCodeCost, _} ->
            {false, ErrCodeCost}
    end.

%% -> #thouse_task{}.
find_thouse_task(TaskId, TaskData) ->
    case lists:keyfind(TaskId, #thouse_task.task_id, TaskData) of
        #thouse_task{} = THouseTask -> THouseTask;
        _ -> task_default_by_id(TaskId)
    end.

task_default_by_id(TaskId) ->
    TaskDetails = data_treasure_house:task_details(TaskId),
    task_default(TaskId, TaskDetails).

%% 签到任务特殊，签到即直接领取奖励
task_default(TaskId, {checkin, _}) ->
    #thouse_task{
        task_id = TaskId
        ,finish_times = 1
        ,recv_times = 0
    };
%% 其他任务初始状态均为未完成
task_default(TaskId, _) ->
    #thouse_task{
        task_id = TaskId
        ,finish_times = 0
        ,recv_times = 0
    }.

%% -------------------------------------------------------
%% 击杀怪物任务触发
%% -------------------------------------------------------

%% 击杀特定BOSS类型的怪物，触发任务完成检查
kill_mon(RoleId, MonBossType) when is_integer(RoleId) ->
    % ?INFO("-----kill_mon:~w,~w", [RoleId, MonBossType]),
    case lists:member(MonBossType, [?MON_REWARD_BOSS, ?MON_DESERTED_BOSS]) of
        true  ->
            % ?INFO("---check--kill_mon--MonBossType:~w--", [MonBossType]),
            lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS,
                ?MODULE, kill_mon, [MonBossType]
            );
        false ->
            ignore
    end;

%% 回调到角色进程，处理任务更新
kill_mon(Player, MonBossType) ->
    % ?INFO("-------kill_mon--MonBossType:~w--", [MonBossType]),
    #player_status{
        id = RoleId,
        treasure_house = OldRoleTreasureHouse
    } = Player,
    %% 校验数据（可能发生更新）
    RoleTreasureHouse = valid_role_data(RoleId, OldRoleTreasureHouse),
    #treasure_house{
        task_data = TaskData
    } = RoleTreasureHouse,
    AllTask = data_treasure_house:ticket_tasks(),
    GoodsTypeId = data_treasure_house:kv_get(ticket_goods),
    Result =
    case is_integer(GoodsTypeId) of
        true  ->
            case lists:keyfind({kill_mon, MonBossType}, 3, AllTask) of
                {TaskId, DailyMax, _} when DailyMax > 0 ->
                    case find_thouse_task(TaskId, TaskData) of
                        THouseTask = #thouse_task{finish_times = FinishTimes} when FinishTimes < DailyMax ->
                            NowPlayer = Player#player_status{treasure_house = RoleTreasureHouse},
                            do_kill_mon(NowPlayer, THouseTask, TaskData, GoodsTypeId);
                        _Other ->
                            ignore
                    end;
                _ ->
                    ignore
            end;
        false ->
            ignore
    end,
    case Result of
        ignore ->
            Player#player_status{treasure_house = RoleTreasureHouse};
        {ok, NewPlayer} ->
            NewPlayer
    end.

do_kill_mon(Player, THouseTask, TaskData, _GoodsTypeId) ->
    % ?INFO("-------role_kill_mon2----"),
    #player_status{id = RoleId, treasure_house = RoleTreasureHouse} = Player,
    #thouse_task{task_id = TaskId, finish_times = FinishTimes} = THouseTask,
    %% 更新保存数据
    NewTHouseTask = THouseTask#thouse_task{finish_times = FinishTimes + 1},
    NewTaskData = lists:keystore(TaskId, #thouse_task.task_id, TaskData, NewTHouseTask),
    NewRoleTreasureHouse = RoleTreasureHouse#treasure_house{
        task_data = NewTaskData
        ,time = utime:unixtime()
    },
    db_save_treasure_house(RoleId, NewRoleTreasureHouse),
    % AwardList = [{?TYPE_BIND_GOODS, GoodsTypeId, 1}],
    % TmpPlayer = Player#player_status{treasure_house = NewRoleTreasureHouse},
    % %% 发放奖励
    % NewPlayer = lib_goods_api:send_reward(TmpPlayer, AwardList, treasure_house),
    NewPlayer = Player#player_status{treasure_house = NewRoleTreasureHouse},
    {ok, NewPlayer}.

%% -------------------------------------------------------
%% 广播TV消息
%% -------------------------------------------------------

%% 广播TV消息（即lib_chat send_TV的效果，合并了消息）
broadcast_draw_awards(TvSubId, RoleId, RoleName, TVList) ->
    BinDataAll = pack_awards_tv(TvSubId, RoleId, RoleName, TVList),
    % ?INFO("TVList:~w, BinDataAll:~w", [TVList, BinDataAll]),
    case BinDataAll of
        <<>> -> ignore;
        _ -> lib_server_send:send_to_all(BinDataAll)
    end,
    ok.

%% 打包奖励列表的TV消息数据
pack_awards_tv(TvSubId, RoleId, Name, AwardList) ->
    BinDataList = [
        pack_award_tv(TvSubId, RoleId, Name, Award) ||
        Award = {_Type, _GoodsTypeId, _GoodsNum} <- AwardList
    ],
    list_to_binary(BinDataList).

pack_award_tv(TvSubId, RoleId, Name, {_Type, GoodsTypeId, _GoodsNum}) ->
    ModuleId = ?MOD_THOUSE,
    Msg = [Name, RoleId, GoodsTypeId],
    lib_chat:make_tv(ModuleId, TvSubId, Msg).

check_broadcast([AwardId | AwardIdList]) ->
    {_, IsBroadcast} = data_treasure_house:draw_id_others(AwardId),
    case IsBroadcast > 0 of
        true  ->
            AwardList = data_treasure_house:draw_award(AwardId),
            AwardList ++ check_broadcast(AwardIdList);
        false ->
            check_broadcast(AwardIdList)
    end;
check_broadcast([]) ->
    [].

%% -------------------------------------------------------
%% 内部接口 数据转换、数据存储
%% -------------------------------------------------------

%% 数据转换
to_treasure_house([Period, Round, Count, RecvRound, DataBin, DataTime]) ->
    [RecvCounts, TaskData] =
    case util:bitstring_to_term(DataBin) of
        Data = [_RecvCounts, _TaskData] -> Data;
        _ -> [[], []]
    end,
    #treasure_house{
        period = Period
        ,round = Round
        ,count = Count
        ,recv_round = RecvRound
        ,recv_counts = RecvCounts
        ,task_data = TaskData
        ,time = DataTime
    }.

%% 数据存储
db_save_treasure_house(RoleId, RoleTreasureHouse) ->
    DataFmtFun = fun(Data) -> fmt_treasure_house(RoleId, Data) end,
    udb:save_data_list([RoleTreasureHouse], ?SQL_REPLACE_TREASURE_HOUSE_HEAD, DataFmtFun).

fmt_treasure_house(RoleId, #treasure_house{
        period = Period
        ,round = Round
        ,count = Count
        ,recv_round = RecvRound
        ,recv_counts = RecvCounts
        ,task_data = TaskData
        ,time = DataTime
    }) ->
    Data = util:term_to_string([RecvCounts, TaskData]),
    util:fmt_bin("(~w,~w,~w,~w,~w,'~ts',~w)", [RoleId, Period, Round, Count, RecvRound, Data, DataTime]).

%% -------------------------------------------------------
%% 内部接口 有效性、状态判断
%% -------------------------------------------------------

%% -> Period :: non_neg_integer() | false.
curr_period() ->
    OpenDay = util:get_open_day(),
    curr_period(OpenDay).

curr_period(OpenDay) ->
    data_treasure_house:get_period(OpenDay).

%% 检查是否概率提升期间
is_prob_up(OpenDay, WeekDay) ->
    OpenDays = data_treasure_house:kv_get(open_days),
    IsProbUpOpenDay =
    case OpenDays of
        [_|_] -> lists:member(OpenDay, OpenDays);
        _ -> false
    end,
    case IsProbUpOpenDay of
        false ->
            WeekDays = data_treasure_house:kv_get(week_days),
            case WeekDays of
                [_|_] -> lists:member(WeekDay, WeekDays);
                _ -> false
            end;
        true  ->
            true
    end.

valid_role_data(RoleId, RoleTreasureHouse) ->
    NowTime = utime:unixtime(),
    OpenDay = util:get_open_day(NowTime),
    CurrPeriod = curr_period(OpenDay),
    valid_role_data(RoleId, RoleTreasureHouse, CurrPeriod, NowTime).

%% 玩家数据时效性校验
valid_role_data(RoleId, RoleTreasureHouse, CurrPeriod, NowTime) ->
    % ?INFO("-valid_role_data, role:~w, curr_period:~w, RoleTreasureHouse:~w", [RoleId, CurrPeriod, RoleTreasureHouse]),
    #treasure_house{
        period = Period
        ,time = DataTime
    } = RoleTreasureHouse,
    if
        not is_integer(CurrPeriod) ->
            % ?INFO("配置不应该关闭，保留玩家数据", []),
            %% 配置不应该关闭，保留玩家数据
            RoleTreasureHouse;
        CurrPeriod =/= Period ->
            % ?INFO("需要发放奖励，更新为下一期数据", []),
            %% 需要发放奖励，更新为下一期数据
            NewRoleTreasureHouse = up_period(RoleTreasureHouse, CurrPeriod),
            db_save_treasure_house(RoleId, NewRoleTreasureHouse),
            check_settle_period(RoleId, RoleTreasureHouse),
            NewRoleTreasureHouse;
        true ->
            % ?INFO("当期数据，再检查任务数据时间", []),
            %% 当期数据，再检查任务数据时间
            Midnight = utime:unixdate(NowTime),
            case DataTime >= Midnight of
                true  ->
                    RoleTreasureHouse;
                false ->
                    NewRoleTreasureHouse = RoleTreasureHouse#treasure_house{
                        task_data = []
                        ,time = NowTime
                    },
                    % ?INFO("更新任务数据", []),
                    db_save_treasure_house(RoleId, NewRoleTreasureHouse),
                    NewRoleTreasureHouse
            end
    end.

up_period(RoleTreasureHouse, NewPeriod) ->
    RoleTreasureHouse#treasure_house{
        period = NewPeriod
        ,round = 1
        ,count = 0
        ,recv_round = 1
        ,recv_counts = []
    }.

%% 检查结算发放奖励
check_settle_period(RoleId, RoleTreasureHouse) ->
    % ?INFO("check_settle_period"),
    #treasure_house{
        period = Period
        ,round = Round
        ,count = Count
        ,recv_round = RecvRound
        ,recv_counts = RecvCounts
    } = RoleTreasureHouse,
    %% 轮次比较，计算出应该给的全部奖励，邮件发放
    RoundMax = data_treasure_house:round_max(Period),
    AwardList = settle_period_calc(Period, Round, Count, RecvRound, RecvCounts, RoundMax, []),
    case AwardList of
        [_ | _] ->
            Title = utext:get(?THOUSE_MAIL_TITLE),
            Content = utext:get(?THOUSE_MAIL_CONTENT),
            lib_mail:send_sys_mail([RoleId], ?MOD_THOUSE, ?MOD_NONE, Title, Content, AwardList);
        _ ->
            ignore
    end.

settle_period_calc(Period, CurrRound, CurrCount, RecvRound, RecvCounts, RoundMax, Acc) when CurrRound >= RecvRound ->
    CliRecvRound = to_cli_round(RecvRound, RoundMax),
    RecvRoundCurrCount = recv_round_curr_count(Period, CurrRound, RecvRound, CurrCount),
    RoundCounts = data_treasure_house:round_counts(Period, CliRecvRound),
    RoundRecvState = round_recv_state(RecvRoundCurrCount, RoundCounts, RecvCounts),
    NewAcc = lists:foldl(fun({CountNeed, ?THOUSE_COUNT_CAN_GET}, AccX) ->
                AwardList = data_treasure_house:draw_count_award(Period, CliRecvRound, CountNeed),
                AwardList ++ AccX;
            (_, AccX) ->
                AccX
        end, Acc, RoundRecvState),
    settle_period_calc(Period, CurrRound, CurrCount, RecvRound + 1, [], RoundMax, NewAcc);
settle_period_calc(_Period, _Round, _Count, _RecvRound, _RecvCounts, _RoundMax, Acc) ->
    Acc.

%% 转换次数奖励档次的领取状态
round_recv_state(RecvRoundCurrCount, RoundCounts = [_|_], RecvCounts) ->
    [
        {
            CountNeed,
            to_count_recv_state(RecvRoundCurrCount, CountNeed, RecvCounts)
        } ||
        CountNeed <- RoundCounts
    ];
round_recv_state(_, [], _) ->
    [].

to_count_recv_state(RecvRoundCurrCount, CountNeed, RecvCounts) ->
    case lists:member(CountNeed, RecvCounts) of
        true  -> ?THOUSE_COUNT_GOT;
        false ->
            case RecvRoundCurrCount >= CountNeed of
                true  -> ?THOUSE_COUNT_CAN_GET;
                false -> ?THOUSE_COUNT_NOT_ENOUGH
            end
    end.

%% 服务端使用真实的Round，但是发给前端时需要检验是否超出最大值，进行修正
-compile({inline, [to_cli_round/2]}).
to_cli_round(Round, RoundMax) ->
    erlang:min(Round, RoundMax).

% calc_new_round_count(CurrPeriod, CurrRound, CurrCount, CliCurrRound, DrawCount) ->
%     RoundCountMax = data_treasure_house:round_count_max(CurrPeriod, CliCurrRound),
%     NewCount = CurrCount + DrawCount,
%     case NewCount >= RoundCountMax of
%         true  ->
%             %% 限制DrawCount不会很大，不会一次加多轮，所以这里直接加一轮
%             {CurrRound + 1, NewCount - RoundCountMax};
%         false ->
%             {CurrRound, NewCount}
%     end.

%% 转换为当前已领轮次下，可领的最大次数
recv_round_curr_count(CurrPeriod, CurrRound, RecvRound, CurrCount) ->
    if
        CurrRound > RecvRound ->
            RoundMax = data_treasure_house:round_max(CurrPeriod),
            CliRecvRound = to_cli_round(RecvRound, RoundMax),
            data_treasure_house:round_count_max(CurrPeriod, CliRecvRound);
        CurrRound =:= RecvRound ->
            CurrCount;
        true ->
            0
    end.

%% 转换为所有任务的状态数据
to_cli_task_data(TaskData, AllTask) ->
    lists:foldl(fun({TaskId, DailyMax, _TaskDetails}, Acc) ->
                #thouse_task{
                    finish_times = FinishTimes
                    ,recv_times = RecvTimes
                } = find_thouse_task(TaskId, TaskData),
                State = to_cli_task_state(DailyMax, FinishTimes, RecvTimes),
                CliTaskState = {TaskId, FinishTimes, State},
                [CliTaskState | Acc]
        end, [], AllTask).

%% 发送给前端的数据，已完成次数是指服务端的已领取次数
%% 服务端的已完成次数相当于前端的 未领取+已领取 总数
to_cli_task_state(DailyMax, FinishTimes, RecvTimes) ->
    if
        FinishTimes =< 0 -> 0;
        DailyMax =< RecvTimes -> 2;
        FinishTimes > RecvTimes -> 1;
        FinishTimes =< RecvTimes andalso DailyMax > FinishTimes -> 0;
        true -> 2
    end.

calc_draw_weight_list(CurrPeriod, IsProbUp) ->
    AllPeriod = data_treasure_house:draw_periods(),
    WeightList = calc_draw_weight_list2(AllPeriod, CurrPeriod, []),
    case IsProbUp of
        true  ->
            case data_treasure_house:kv_get(prob_up) of
                [_ | _] = ProbUpList ->
                    [
                        case data_treasure_house:draw_id_others(Id) of
                            {IsRare, _} when IsRare > 0 ->
                                case lists:keyfind(IsRare, 1, ProbUpList) of
                                    {_, UpWeight} ->
                                        {Weight + UpWeight, Id};
                                    _ ->
                                        Data
                                end;
                            _ ->
                                Data
                        end
                        || Data = {Weight, Id} <- WeightList
                    ];
                _ ->
                    WeightList
            end;
        false ->
            WeightList
    end.

calc_draw_weight_list2([Period | Remain], CurrPeriod, Acc) when Period =< CurrPeriod ->
    DrawWeightIds = data_treasure_house:draw_ids(Period),
    NewAcc = DrawWeightIds ++ Acc,
    calc_draw_weight_list2(Remain, CurrPeriod, NewAcc);
calc_draw_weight_list2(_, _, Acc) ->
    Acc.

send_treasure_house_info(Sid, Period, Round, Count, RecvRound, RoundRecvState) ->
    RoundMax = data_treasure_house:round_max(Period),
    CliRound = to_cli_round(Round, RoundMax),
    Data = [Period, CliRound, Round, Count, RecvRound, RoundRecvState],
    {ok, BinData} = pt_104:write(10400, Data),
    lib_server_send:send_to_sid(Sid, BinData).

check_recv_round_update(RecvRound, RoundRecvState) ->
    case [
            1 ||
            {_CountNeed, State} <- RoundRecvState,
            State =/= ?THOUSE_COUNT_GOT
        ] of
        [_|_] ->    %% 还有未领取或未达成的，领取轮次保持
            RecvRound;
        _ ->        %% 领取完成
            RecvRound + 1
    end.
