%% ------------------------------
%% module:lib_marble.erl
%% time  :19/12/17
%% author:hh
%% desc  :弹珠玩法
%% ------------------------------

-module(lib_marble).

-include("server.hrl").
-include("common.hrl").
-include("errcode.hrl").
-include("figure.hrl").
-include("marble.hrl").
-include("def_event.hrl").
-include("def_goods.hrl").
-include("rec_event.hrl").

-export([
    login/1             %% 登录
    ,logout/1
    ,sql_update/2       %% 数据库更新

    ,marble_info/1      %% 充能玩法界面
    ,start_marble/4     %% 开始抽奖
    ,marble_result/4
    % ,energy_reward/2
    ,reset_marble/1
    ,marble_exchange/3
    ]).

-export([
    handle_event/2      %% 事件回调
    ]).

-export([marble_token/1]).
-export([is_correct_token/3]).
% -export([shop_update_do/0]).

login(RoleId) ->
    case catch db:get_row(?ROLE_MARBLE_SELECT, [RoleId]) of
        [MarbleCount, MarbleReset, EnergyPro, MarbleTime, RewardBin, FixRewardBin, QuaIndexsBin|_] ->
            RoleMarble = #role_marble{
                marble_count   = MarbleCount,
                marble_reset   = MarbleReset,
                energy_pro     = EnergyPro,
                marble_time    = MarbleTime,
                energy_rewards = util:bitstring_to_term(RewardBin),
                fixed_rewards  = util:bitstring_to_term(FixRewardBin),
                qua_indexs     = util:bitstring_to_term(QuaIndexsBin)
            },
            daily_update_marble(RoleMarble);
        _A ->
            #role_marble{}
    end.

%% 只有在线跨天的下线才回写
logout(Player) ->
    #player_status{id = RoleId, role_marble = RoleMarble} = Player,
    RoleMarble#role_marble.online_cross_day == 1 andalso sql_update(RoleId, RoleMarble),
    ok.

handle_event(Player, #event_callback{type_id = ?EVENT_ZERO_CLOCK}) ->
    #player_status{role_marble = RoleMarble} = NewPlayer = marble_update(Player, daily_reset),
    NewPlayer1 = NewPlayer#player_status{role_marble = RoleMarble#role_marble{online_cross_day = 1}},
    %% 通知变更
    lib_marble:marble_info(NewPlayer1),
    {ok, NewPlayer1};
handle_event(Player, #event_callback{type_id = ?EVENT_LOGIN_CAST}) ->
    #player_status{id = RoleId, role_marble = RoleMarble} = Player,
    #role_marble{fixed_rewards = FixRewards, qua_indexs = QuaIndexs} = RoleMarble,
    {IsSqlUpdate, NewPlayer} = case FixRewards of
        [_|_] ->
            {false, Player};
        _ ->
            FixRewards1 = fixed_rewards_update(),
            % ?ERR("~p", [FixRewards1]),
            NewRoleMarble = RoleMarble#role_marble{fixed_rewards = FixRewards1},
            % sql_update(RoleId, NewRoleMarble),
            {true, Player#player_status{role_marble = NewRoleMarble}}
    end,
    {IsSqlUpdate1, NewPlayer1} = case QuaIndexs of
        [_|_] ->
            {false, NewPlayer};
        _ ->
            NewQuaIndexs = qua_indexs_update(),
            #player_status{role_marble = NewRoleMarble1} = NewPlayer,
            NewRoleMarble2 = NewRoleMarble1#role_marble{qua_indexs = NewQuaIndexs},
            {true, NewPlayer#player_status{role_marble = NewRoleMarble2}}
    end,
    (IsSqlUpdate orelse IsSqlUpdate1) andalso sql_update(RoleId, NewPlayer1#player_status.role_marble),
    {ok, NewPlayer1};

handle_event(Player = #player_status{role_marble = #role_marble{}}, #event_callback{type_id = ?EVENT_DISCONNECT}) ->
    #player_status{role_marble = RoleMarble} = Player,
    #role_marble{start_time = StartTime, cost_type = CostType, marble_cost = Cost} = RoleMarble,
    if StartTime =/= 0 orelse Cost =/= 0 ->
            Diff = utime:unixtime() - StartTime,
            if Diff =< ?MARBLE_TIME_LIMIT ->
                    AllQuals = data_marble:get_all_qual(),
                    Id = urand:rand_with_weight(AllQuals),
                    #base_marble_reward{qualitys = Quals} = data_marble:get_qual(Id),
                    [Quals1|_] = Quals,
                    QualId = urand:list_rand(tuple_to_list(Quals1)),
                    #base_marble_qual_reward{rewards = Rewards0, energy_point = EnergyPoint} = data_marble:get_qual_reward(QualId),
                    Rewards = urand:rand_with_weight(Rewards0),
                    case Cost of
                        [_|_] ->
                            if CostType == 0 ->
                                    case lib_goods_api:cost_object_list_with_check(Player, Cost, marble_cost, "") of
                                        {true, NewPlayer, _} ->
                                            do_gain_marble_reward(NewPlayer, [Rewards], EnergyPoint, QualId);
                                        {false, _ErrCode, _} ->
                                            {ok, Player}
                                    end;
                                true ->
                                    case lib_goods_api:cost_objects_with_auto_buy(Player, Cost, marble_cost, "") of
                                        {true, NewPlayer, _} ->
                                            do_gain_marble_reward(NewPlayer, [Rewards], EnergyPoint, QualId);
                                        {false, _ErrCode, _} ->
                                            {ok, Player}
                                    end
                            end;
                        _ ->
                            do_gain_marble_reward(Player, [Rewards], EnergyPoint, QualId)
                    end;
                true ->
                    {ok, Player}
            end;
        true ->
            {ok, Player}
    end;
handle_event(Player, _) ->
    {ok, Player}.

daily_update_marble(RoleMarble) ->
    #role_marble{marble_time = Time1} = RoleMarble,
    % NewRoleMarble  = do_daily_update_marble(shop, Time2, RoleMarble),
    NewRoleMarble1 = do_daily_update_marble(marble, Time1, RoleMarble),
    NewRoleMarble1.

%% 跨天重置
do_daily_update_marble(Type, Time, RoleMarble) ->
    case utime:is_today(Time) of
        true ->
            RoleMarble;
        _ ->
            if Type == marble ->
                    marble_update(RoleMarble, daily_reset);
                true ->
                    RoleMarble
                    % shop_update(RoleMarble, daily_reset)
            end
    end.

sql_update(RoleId, RoleMarble) ->
    #role_marble{
        marble_count   = MarbleCount,
        marble_reset   = MarbleReset,
        energy_pro     = EnergyPro,
        marble_time    = Time,
        energy_rewards = Rewards,
        fixed_rewards  = FixRewards,
        qua_indexs     = QuaIndexs
    } = RoleMarble,
    RewardBin    = util:term_to_bitstring(Rewards),
    FixRewardBin = util:term_to_bitstring(FixRewards),
    QuaIndexsBin = util:term_to_bitstring(QuaIndexs),
    db:execute(?ROLE_MARBLE_REPLACE,
        [RoleId, MarbleCount, MarbleReset, EnergyPro, Time, RewardBin, FixRewardBin, QuaIndexsBin]),
    ok.

marble_info(Player) ->
    #player_status{sid = Sid, role_marble = RoleMarble} = Player,
    #role_marble{marble_count = MarbleCount, marble_reset = MarbleReset, energy_pro = EnergyProgress,
         energy_rewards = EnergyRewards, fixed_rewards = FixRewards, qua_indexs = QuaIndexs} = RoleMarble,
    Rewards = pack_reward_info(EnergyRewards, FixRewards),
    {QuaIndexs1, _} = lists:foldl(fun(QuaX, {Acc, Acc1}) ->
        {[{Acc1, QuaX}|Acc], Acc1+1}
     end, {[], 1}, QuaIndexs),
    % ?ERR("~p ~p", [EnergyProgress, MarbleCount]),
    lib_server_send:send_to_sid(Sid, pt_414, 41400, [EnergyProgress, MarbleCount, MarbleReset, Rewards, QuaIndexs1]),
    ok.

pack_reward_info(EnergyRewards, FixRewards) ->
    AllEnergyPoints = data_marble:get_all_count(),
    lists:foldl(fun({Index, _}, Acc) ->
        case lists:keyfind(Index, 1, EnergyRewards) of
            {_, RewardState0} ->
                RewardState = if
                    RewardState0 == ?MARBLE_SHOP_UNGET -> 1;
                    true -> 2
                end;
            _ -> RewardState = 0
        end,
        case lists:keyfind(Index, 1, FixRewards) of
            {_, GoodsL} -> ok;
            _           -> GoodsL = []
        end,
        [{Index, GoodsL, RewardState}|Acc]
    end, [], AllEnergyPoints).

start_marble(Player, Type, CostOpt, TokenTime) ->
    #player_status{sid = Sid} = Player,
    case marble_check(Player, Type, CostOpt, TokenTime) of
        {true, _Now, CostType, Cost} ->
            do_marble(Player, TokenTime, CostType, Cost);
        {false, ErrCode} ->
            lib_server_send:send_to_sid(Sid, pt_414, 41401, [ErrCode]),
            ok
    end.

marble_result(Player, Result, QualId, Tokens) ->
    case marble_result_check(Player, Result, QualId, Tokens) of
        true -> %% 本次弹珠失败(卡住等可接受失败原因)
            #player_status{sid = Sid, role_marble = RoleMarble} = Player,
            NewRoleMarble = RoleMarble#role_marble{start_time = 0, marble_cost = []},
            NewPlayer = Player#player_status{role_marble = NewRoleMarble},
            lib_server_send:send_to_sid(Sid, pt_414, 41402, [?SUCCESS, []]),
            {ok, NewPlayer};
        {true, Rewards, EnergyPoint} ->
            % ?ERR("~p ~p ~p", [QualId, Rewards, EnergyPoint]),
            #player_status{role_marble = #role_marble{cost_type = CostType, marble_cost = Cost}} = Player,
            if Cost == [] ->
                    do_gain_marble_reward(Player, Rewards, EnergyPoint, QualId);
                true ->
                    do_marble_cost(Player, CostType, Cost, Rewards, EnergyPoint, QualId)
            end;
        {false, ErrCode} -> %?ERR("~p", [ErrCode]),
            lib_server_send:send_to_sid(Player#player_status.sid, pt_414, 41402, [ErrCode, []]),
            #player_status{role_marble = RoleMarble} = Player,
            NewRoleMarble = RoleMarble#role_marble{start_time = 0, marble_cost = []},
            {ok, Player#player_status{role_marble = NewRoleMarble}}
    end.

do_marble_cost(Player, CostType, Cost, Rewards, EnergyPoint, QualId) ->
    #player_status{sid = Sid, role_marble = RoleMarble} = Player,
    if CostType == 0 ->
            case lib_goods_api:cost_object_list_with_check(Player, Cost, marble_cost, "") of
                {true, NewPlayer, _} ->
                    do_gain_marble_reward(NewPlayer, Rewards, EnergyPoint, QualId);
                {false, ErrCode, _} ->
                    lib_server_send:send_to_sid(Sid, pt_414, 41402, [ErrCode, []]),
                    NewRoleMarble = RoleMarble#role_marble{start_time = 0, cost_type = 0, marble_cost = []},
                    {ok, Player#player_status{role_marble = NewRoleMarble}}
            end;
        true ->
            case lib_goods_api:cost_objects_with_auto_buy(Player, Cost, marble_cost, "") of
                {true, NewPlayer, _} ->
                    do_gain_marble_reward(NewPlayer, Rewards, EnergyPoint, QualId);
                {false, ErrCode, _} ->
                    lib_server_send:send_to_sid(Sid, pt_414, 41402, [ErrCode, []]),
                    NewRoleMarble = RoleMarble#role_marble{start_time = 0, cost_type = 0, marble_cost = []},
                    {ok, Player#player_status{role_marble = NewRoleMarble}}
            end
    end.

energy_reward(Player, RewardId, MarbleRewards) ->
    #player_status{sid = Sid, role_marble = RoleMarble} = Player,
    #role_marble{energy_rewards = EnergyRewards} = RoleMarble,
    case energy_reward_check(RewardId, EnergyRewards) of
        true ->
            do_energy_reward(Player, RewardId, MarbleRewards);
        {false, ErrCode} ->
            lib_server_send:send_to_sid(Sid, pt_414, 41403, [ErrCode, []]),
            Player
    end.

reset_marble(Player) ->
    #player_status{sid = Sid, figure = #figure{vip = VipLv}, role_marble = RoleMarble} = Player,
    case reset_marble_check(VipLv, RoleMarble) of
        {true, Cost} ->
            case lib_goods_api:cost_object_list_with_check(Player, Cost, reset_marble, "") of
                {true, NewPlayer, _} ->
                    do_reset_marble(NewPlayer);
                {false, ErrCode, _} ->
                    lib_server_send:send_to_sid(Sid, pt_414, 41404, [ErrCode]),
                    ok
            end;
        {false, ErrCode} ->
            lib_server_send:send_to_sid(Sid, pt_414, 41404, [ErrCode]),
            ok
    end.

marble_exchange(Player, ExchangeId, Num) ->
    case marble_exchange_check(Player, ExchangeId, Num) of
        {true, Cost, Rewards} ->
            case lib_goods_api:cost_object_list_with_check(Player, Cost, marble_exchange_cost, "") of
                {true, NewPlayer, _} ->
                    do_marble_exchange(NewPlayer, Rewards);
                {false, ErrCode, _} ->
                    lib_server_send:send_to_sid(Player#player_status.sid, pt_414, 41408, [ErrCode]),
                    ok
            end;
        {false, ErrCode} ->
            lib_server_send:send_to_sid(Player#player_status.sid, pt_414, 41408, [ErrCode]),
            ok
    end.

do_marble_exchange(Player, Rewards) ->
    NewPlayer = lib_goods_api:send_reward(Player, Rewards, marble_exchange_reward),
    lib_server_send:send_to_sid(Player#player_status.sid, pt_414, 41408, [?SUCCESS]),
    {ok, NewPlayer}.

do_reset_marble(Player) ->
    #player_status{id = RoleId, sid = Sid, role_marble = RoleMarble} = Player,
    NewRoleMarble = manual_reset_marble(RoleMarble),
    sql_update(RoleId, NewRoleMarble),
    NewPlayer = Player#player_status{role_marble = NewRoleMarble},
    lib_server_send:send_to_sid(Sid, pt_414,41404, [?SUCCESS]),
    {ok, NewPlayer}.


manual_reset_marble(RoleMarble) ->
    #role_marble{
        marble_count = MarbleCount,
        marble_reset = MarbleResetCount
    } = RoleMarble,
    NewRoleMarble = #role_marble{
        marble_count = MarbleCount,
        marble_reset = MarbleResetCount+1,
        fixed_rewards = fixed_rewards_update(),
        qua_indexs = qua_indexs_update(),
        marble_time  = utime:unixtime()
    },
    NewRoleMarble.

do_energy_reward(Player, RewardId, MarbleRewards) ->
    #player_status{id = RoleId, sid = Sid, role_marble = RoleMarble} = Player,
    #role_marble{energy_rewards = EnergyRewards, fixed_rewards = FixRewards} = RoleMarble,
    Rewards = get_energy_rewards(RewardId),
    Rewards1 = get_fixed_rewards(RewardId, FixRewards),
    IsEnergyRewards = Rewards =/= [] orelse Rewards1 =/= [],
    Rewards2 = lists:append([Rewards, Rewards1, MarbleRewards]),
    NewPlayer = lib_goods_api:send_reward(Player, Rewards2, energy_rewards),
    NewEnergyRewards = lists:keyreplace(RewardId, 1, EnergyRewards, {RewardId, ?MARBLE_SHOP_GETED}),
    NewRoleMarble = RoleMarble#role_marble{energy_rewards = NewEnergyRewards, marble_time = utime:unixtime()},
    sql_update(RoleId, NewRoleMarble),
    NewPlayer1 = NewPlayer#player_status{role_marble = NewRoleMarble},
    lib_server_send:send_to_sid(Sid, pt_414, 41403, [?SUCCESS, Rewards2]),
    {IsEnergyRewards, NewPlayer1}.

get_energy_rewards(RewardId) ->
    #base_marble_count_reward{
        reward = ConfigRewards,
        rand_count = RandCount
    } = data_marble:get_count_reward(RewardId),
    urand:list_rand_by_weight(ConfigRewards, RandCount, true).

get_fixed_rewards(RewardId, FixRewards) ->
    case lists:keyfind(RewardId, 1, FixRewards) of
        {_, GoodsL} ->
            GoodsL;
        _ ->
            []
    end.

do_gain_marble_reward(Player, Rewards, EnergyPoint, QualId) ->
    #player_status{sid = Sid, id = RoleId, figure = #figure{name = RoleName}, role_marble = RoleMarble} = NewPlayer = lib_goods_api:send_reward(Player, Rewards, marble_reward),
    #role_marble{marble_count = MarbleCount, energy_pro = EnergyPro} = RoleMarble,
    NewEnergyPro = EnergyPro+EnergyPoint,
    NewMarbleCount = MarbleCount+1,
    Now = utime:unixtime(),
    NewRoleMarble = RoleMarble#role_marble{
        marble_count = NewMarbleCount,
        marble_time = Now,
        energy_pro = NewEnergyPro,
        start_time = 0,
        marble_cost = [],
        online_cross_day = 0},
    {NewPoints, NewRoleMarble1} = update_role_energy_rewards(NewRoleMarble),
    sql_update(RoleId, NewRoleMarble1),
    % ?ERR("~p", [NewMarbleCount]),
    NewPlayer1 = NewPlayer#player_status{role_marble = NewRoleMarble1},
    NewPlayer3 = case NewPoints of
        [NewIndex|_] ->
            {_IsEnergyRewards, NewPlayer2} = energy_reward(NewPlayer1, NewIndex, Rewards),
            NewPlayer2;
        _ ->
            % IsEnergyRewards = false,
            NewPlayer1
    end,
     lib_server_send:send_to_sid(Sid, pt_414, 41402, [?SUCCESS, Rewards]),
    %% 日志
    lib_log_api:log_marble(RoleId, RoleName, QualId, Rewards, EnergyPoint, NewEnergyPro, Now),
    {ok, NewPlayer3}.

update_role_energy_rewards(RoleMarble) ->
    #role_marble{energy_rewards = Rewards, energy_pro = EnergyPro} = RoleMarble,
    {NewPoints, NewRewards} = calc_energy_rewards(EnergyPro, Rewards),
    NewRoleMarble = RoleMarble#role_marble{energy_rewards = NewRewards},
    {NewPoints, NewRoleMarble}.

calc_energy_rewards(EnergyPro, Rewards) ->
    AllEnergyIds = data_marble:get_all_count(),
    lists:foldl(fun({Index, Energy}, {NewPoints, Acc}) ->
            if EnergyPro >= Energy ->
                    case lists:keyfind(Index, 1, Rewards) of
                        {_, _} ->
                            {NewPoints, Acc};
                        _ ->
                            {[Index|NewPoints], [{Index, ?MARBLE_SHOP_UNGET}|Acc]}
                    end;
                true ->
                    {NewPoints, Acc}
            end
    end, {[], Rewards}, AllEnergyIds).


do_marble(Player, TokenTime, CostType, Cost) ->
    #player_status{sid = Sid, role_marble = RoleMarble} = Player,
    NewRoleMarble = RoleMarble#role_marble{start_time = TokenTime, cost_type = CostType, marble_cost = Cost},
    NewPlayer = Player#player_status{role_marble = NewRoleMarble},
    % ?ERR("~p ~p", [StartTime, Cost]),
    lib_server_send:send_to_sid(Sid, pt_414, 41401, [?SUCCESS]),
    {ok, NewPlayer}.

%% Type 是否补齐
%% CostOpt 优先扣代金券|优先扣钻石
marble_check(Player, Type, CostOpt, Time) ->
    #player_status{role_marble = RoleMarble} = Player,
    #role_marble{marble_count = MarbleCount, start_time = StartTime, marble_cost = Cost, energy_pro = EnergyProgress} = RoleMarble,
    Now = utime:unixtime(),
    Diff = abs(Now - Time),
    %% 弹射消耗
    MarbleCostL = data_marble:get_kv(6),
    %% 最大充能进度
    MaxEnergy   = data_marble:get_kv(13),
    Result = lists:keyfind(CostOpt, 1, MarbleCostL),
    if StartTime =/= 0 orelse Cost =/= [] ->
            {false, ?ERRCODE(err414_have_marbled)};
        Time == 0 orelse Diff > 10 ->
            {false, ?ERRCODE(err414_marble_time_error)};
        not Result ->
            {false, ?ERRCODE(err414_cost_option_error)};
        EnergyProgress >= MaxEnergy ->
            {false, ?ERRCODE(err414_max_energy)};
        true ->
            FreeCount = data_marble:get_kv(5),
            if MarbleCount >= FreeCount ->
                    {_, {GoodsType, GoodsId1, CostNum}} = Result,
                    % [{GoodsType, GoodsId1, CostNum}|_] = calc_marble_cost(FreeCount, MarbleCount+1, Rest, CostTuple),
                    % if GoodsType == ?TYPE_GOODS orelse GoodsType == ?TYPE_BIND_GOODS -> %% 优先扣物品
                    %        Cost1 = [{GoodsType, GoodsId1, CostNum}];
                    %    true ->
                            % GoodsId = data_marble:get_kv(2),
                            % [{_, GoodsNum}|_] = lib_goods_api:get_goods_num(Player, [GoodsId]),
                            % if GoodsNum == 0 ->
                            Cost1 = [{GoodsType, GoodsId1, CostNum}],
                            %     true ->
                            %         [{_, _, UnitNum}|_] = data_marble:get_kv(1),
                            %         Cost1 = calc_deduct_num(CostNum, GoodsId, GoodsNum, UnitNum)
                            % end
                    % end,
                    if Type == 0 ->
                            case lib_goods_api:check_object_list(Player, Cost1) of
                                true ->
                                    {true, Now, Type, Cost1};
                                {false, ErrCode} ->
                                    {false, ErrCode}
                            end;
                        true ->
                            case lib_goods_api:check_object_list_with_auto_buy(Player, Cost1) of
                                true ->
                                    {true, Now, Type, Cost1};
                                {false, ErrCode} ->
                                    {false, ErrCode}
                            end
                    end;
                true ->
                    {true, Now, 0, []}
            end
    end.

%% 代金券折算
% calc_deduct_num(CostNum, GoodsId, GoodsNum, UnitNum) ->
%     DeductNum = GoodsNum*UnitNum,
%     if CostNum >= DeductNum ->
%             [{?TYPE_GOLD, 0, round(CostNum - DeductNum)}, {?TYPE_GOODS, GoodsId, GoodsNum}];
%         true -> %% CostNum < DeductNum
%             NeedCostNum = umath:floor(CostNum/UnitNum),
%             NeedCostGold = CostNum - NeedCostNum*UnitNum,
%             [{?TYPE_GOLD, 0, NeedCostGold}, {?TYPE_GOODS, GoodsId, NeedCostNum}]
%     end.

% calc_marble_cost(_, _, [], Cost) -> [Cost];
% calc_marble_cost(FreeCount, NewMarbleCount, [{ConfigCount, ConfigCost}|Rest], Cost) ->
%     if NewMarbleCount < FreeCount+ConfigCount ->
%             [Cost];
%         true ->
%             calc_marble_cost(FreeCount, NewMarbleCount, Rest, ConfigCost)
%     end.

marble_result_check(Player, Result, QualId, Tokens) ->
    #player_status{id = RoleId, role_marble = RoleMarble} = Player,
    #role_marble{start_time = StartTime} = RoleMarble,
    Fail = ?FAIL,
    if Result == Fail ->
            CheckL = [
                {is_marbling, RoleMarble}
                % {is_correct_token, RoleId, RoleMarble, Tokens}
            ];
        true ->
            CheckL = [
                {is_marbling, RoleMarble},
                {marble_time_check, StartTime, utime:unixtime()},
                {qual_id_check, QualId},
                {is_correct_token, RoleId, RoleMarble, Tokens}
            ]
    end,
    case do_check(CheckL) of
        true ->
            if Result == Fail ->
                    true;
                true ->
                    #base_marble_qual_reward{rewards = Rewards, energy_point = Point} = data_marble:get_qual_reward(QualId),
                    RewardsTuple = urand:rand_with_weight(Rewards),
                    {true, [RewardsTuple], Point}
            end;
        {false, ErrCode} ->
            {false, ErrCode}
    end.

energy_reward_check(RewardId, EnergyRewards) ->
    case lists:keyfind(RewardId, 1, EnergyRewards) of
        {_, ?MARBLE_SHOP_UNGET} ->
            true;
        {_, ?MARBLE_SHOP_GETED} ->
            {false, ?ERRCODE(reward_is_got)};
        _ ->
            {false, ?ERRCODE(err414_energy_point)}
    end.

reset_marble_check(VipLv, RoleMarble) ->
    #role_marble{marble_reset = MarbleCount} = RoleMarble,
    VipCounts = data_marble:get_kv(8),
    SortVipCounts = lists:keysort(1, VipCounts),
    CheckL = [
        {vip_con_check, VipLv, MarbleCount, SortVipCounts}
    ],
    case do_check(CheckL) of
        true ->
            case calc_this_count_cost(MarbleCount+1) of
                Cost = [_|_] ->
                    {true, Cost};
                {false, ErrCode} ->
                    {false, ErrCode}
            end;
        {false, ErrCode} ->
            {false, ErrCode}
    end.

calc_this_count_cost(Count) ->
    CostList = data_marble:get_kv(9),
    [{MinCount, MinCost}|Rest] = lists:keysort(1, CostList),
    if Count >= MinCount ->
            do_calc_this_count_cost(Count, Rest, MinCost);
        true ->
            {false, ?ERRCODE(err414_count_cost)}
    end.

do_calc_this_count_cost(_Count, [], Cost) -> [Cost];
do_calc_this_count_cost(Count, [{ConfigCount, ConfigCost}|Rest], Cost) ->
    if Count < ConfigCount ->
            [Cost];
        true ->
            do_calc_this_count_cost(Count, Rest, ConfigCost)
    end.

calc_vip_count(VipLv, SortVipCounts) ->
    [{MinVip, ConfigCount}|Rest] = SortVipCounts,
    if VipLv < MinVip ->
            0;
        true ->
            do_calc_vip_count(VipLv, Rest, ConfigCount)
    end.

do_calc_vip_count(_VipLv, [], Count) -> Count;
do_calc_vip_count(VipLv, [{ConfigVip, ConfigCount}|Rest], Count) ->
    if VipLv < ConfigVip ->
            Count;
        true ->
            do_calc_vip_count(VipLv, Rest, ConfigCount)
    end.

marble_exchange_check(Player, ExchangeId, Num) ->
    ExchangeRewards = data_marble:get_lattice_kv(exchange_reward),
    ExchangeCost    = data_marble:get_lattice_kv(exchange_cost),
    CheckL = [
        {exchange_reward_check, ExchangeId, ExchangeRewards}
    ],
    case do_check(CheckL) of
        true ->
            ExchangeCost1 = [{GoodsType, GoodsId, round(GoodsNum*Num)} || {GoodsType, GoodsId, GoodsNum} <- ExchangeCost],
            case lib_goods_api:check_object_list(Player, ExchangeCost1) of
                true ->
                    Rewards = lists:keyfind(ExchangeId, 2, ExchangeRewards),
                    Rewards1 = [{GoodsType1, GoodsId1, round(GoodsNum1*Num)} || {GoodsType1, GoodsId1, GoodsNum1} <- [Rewards]],
                    {true, ExchangeCost1, Rewards1};
                {false, ErrCode} ->
                    {false, ErrCode}
            end;
        {false, ErrCode} ->
            {false, ErrCode}
    end.

marble_update(Player = #player_status{}, ResetType) ->
    #player_status{role_marble = RoleMarble} = Player,
    Now = utime:unixtime(),
    FixRewards = fixed_rewards_update(),
    QuaIndexs  = qua_indexs_update(),
    if ResetType == daily_reset ->
           NewRoleMarble = RoleMarble#role_marble{
                marble_count = 0,
                marble_reset = 0,
                energy_pro = 0,
                marble_time = Now,
                energy_rewards = [],
                fixed_rewards = FixRewards,
                qua_indexs = QuaIndexs
           };
        true ->
            NewRoleMarble = RoleMarble#role_marble{
                energy_pro = 0,
                marble_time = Now,
                energy_rewards = [],
                fixed_rewards = FixRewards,
                qua_indexs = QuaIndexs
            }
    end,
    Player#player_status{role_marble = NewRoleMarble};
marble_update(RoleMarble = #role_marble{}, ResetType) ->
    Now = utime:unixtime(),
    FixRewards = fixed_rewards_update(),
    QuaIndexs  = qua_indexs_update(),
    if ResetType == daily_reset ->
           NewRoleMarble = RoleMarble#role_marble{
                marble_count = 0,
                marble_reset = 0,
                energy_pro = 0,
                marble_time = Now,
                energy_rewards = [],
                fixed_rewards = FixRewards,
                qua_indexs = QuaIndexs
           };
        true ->
            NewRoleMarble = RoleMarble#role_marble{
                energy_pro = 0,
                marble_time = Now,
                energy_rewards = [],
                fixed_rewards = FixRewards,
                qua_indexs = QuaIndexs
            }
    end,
    NewRoleMarble.

fixed_rewards_update() ->
    AllEnergyPoints = data_marble:get_all_count(),
    lists:foldl(fun({Index, _EnergyPoint}, Acc) ->
        #base_marble_count_reward{fixed_reward = FixRewards} = data_marble:get_count_reward(Index),
        case FixRewards of
            [_|_] ->
                case urand:rand_with_weight(FixRewards) of
                    RandRes = {_, _, _} ->
                        [{Index, [RandRes]}|Acc];
                    _ ->
                        Acc
                end;
            _ ->
                Acc
        end
     end, [], AllEnergyPoints).

qua_indexs_update() ->
    AllQuals = data_marble:get_all_qual(),
    Id = urand:rand_with_weight(AllQuals),
    #base_marble_reward{qualitys = [Quals|_]} = data_marble:get_qual(Id),
    tuple_to_list(Quals).


is_correct_token(Tokens, RoleId, RoleMarble) ->
    #role_marble{start_time = StartTime, marble_count = Count} = RoleMarble,
    ServTokens = marble_token([RoleId, StartTime, Count+1]),
    ServTokens == Tokens.

marble_token([RoleId, StartTime, Count]) ->
    RandString = data_marble:get_kv(7),
    TokenList  = lists:concat([RoleId, StartTime, Count, RandString]),
    % ?ERR("~w ~w ~w", [StartTime, Count, RandString]),
    Tokens = lists:flatten([io_lib:format("~2.16.0b", [N]) || N <- binary_to_list(erlang:md5(TokenList))]),
    % ?ERR("~p", [Tokens]),
    Tokens.

do_check([]) -> true;
do_check([{is_marbling, RoleMarble}|Rest]) ->
    #role_marble{start_time = StartTime} = RoleMarble,
    if StartTime == 0 ->
            {false, ?ERRCODE(err414_marble_error)};
        true ->
            do_check(Rest)
    end;
do_check([{qual_id_check, QualId}|Rest]) ->
    case data_marble:get_qual_reward(QualId) of
        #base_marble_qual_reward{} ->
            do_check(Rest);
        _ ->
            {false, ?ERRCODE(err414_marble_error)}
    end;
do_check([{vip_con_check, VipLv, MarbleCount, SortVipCounts}|Rest]) ->
    ConfigVipCount = calc_vip_count(VipLv, SortVipCounts),
    if MarbleCount >= ConfigVipCount ->
            {false, ?ERRCODE(err414_marble_reset_count)};
        true ->
            do_check(Rest)
    end;
do_check([{exchange_reward_check, ExchangeId, ExchangeRewards}|Rest]) ->
    case lists:keyfind(ExchangeId, 2, ExchangeRewards) of
        {_Type, _, _Num} ->
            do_check(Rest);
        _ ->
            {false, ?FAIL}
    end;
do_check([{marble_time_check, StartTime, Now}|Rest]) ->
    Diff = Now - StartTime,
    if Diff > ?MARBLE_TIME_LIMIT ->
            {false, ?ERRCODE(err414_marble_error)};
        true ->
            do_check(Rest)
    end;
do_check([{is_correct_token, RoleId, RoleMarble, Tokens}|Rest]) ->
    case is_correct_token(Tokens, RoleId, RoleMarble) of
        true ->
            do_check(Rest);
        false ->
            {false, ?ERRCODE(err414_marble_error)}
    end;
do_check([_Data|_Rest]) ->
    {false, ?FAIL}.