%% -------------------------
%% desc  :器灵寻宝-走格子
%% module:lib_lattice.erl
%% author:hh
%% time  :19/02/27
%% -------------------------

-module(lib_lattice).

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

-export([
    login/1
    ,zero_time/1
    ,get_role_lattice_info/1
    ,lattice_go/2
    ,lattice_reset/1
    ,update_circle_reward/2
    ]).

-export([check/1]).
-export([handle_event/2]).

-export([gm_reset/1]).
-export([get_count_by_vip/2]).

login(RoleId) ->
    case db:get_row(?SQL_LATTICE_SELECT, [RoleId]) of
        [Pos, Circle, UpdateTime, RewardBin|_] ->
            case utime:is_today(UpdateTime) of
                false ->
                    {NewPos, NewCircle} = init_pos_circle(),
                    RoleLattice     = #role_lattice{role_id = RoleId, pos = NewPos, circle = NewCircle, update_time = utime:unixtime()},
                    CircleReward    = update_circle_reward(NewCircle, NewPos),
                    NewRoleLattice  = RoleLattice#role_lattice{circle_reward = CircleReward},
                    % db_replace_role_lattice(NewRoleLattice),
                    NewRoleLattice;
                _ ->
                    #role_lattice{
                        role_id = RoleId, pos = Pos, circle = Circle, update_time = UpdateTime,
                        circle_reward = util:bitstring_to_term(RewardBin)
                    }
            end;
        _ ->
            {NewPos, NewCircle} = init_pos_circle(),
            RoleLattice     = #role_lattice{role_id = RoleId, pos = NewPos, circle = NewCircle, update_time = utime:unixtime()},
            CircleReward    = update_circle_reward(NewCircle, NewPos),
            NewRoleLattice  = RoleLattice#role_lattice{circle_reward = CircleReward},
            % db_replace_role_lattice(NewRoleLattice),
            NewRoleLattice
    end.

handle_event(Player, #event_callback{type_id = ?EVENT_LV_UP}) ->
    #player_status{id = RoleId, figure = #figure{lv = RoleLv}} = Player,
    ConfigLv = lib_module:get_open_lv(?MOD_MARBLE),
    if RoleLv =:= ConfigLv ->
            {NewPos, NewCircle} = init_pos_circle(),
            RoleLattice     = #role_lattice{role_id = RoleId, pos = NewPos, circle = NewCircle, update_time = utime:unixtime()},
            CircleReward    = update_circle_reward(NewCircle, NewPos),
            NewRoleLattice  = RoleLattice#role_lattice{circle_reward = CircleReward},
            db_replace_role_lattice(NewRoleLattice),
            NewPlayer = Player#player_status{role_lattice = NewRoleLattice},
            pp_marble:handle(41409, NewPlayer, []),
            {ok, NewPlayer};
        true ->
            {ok, Player}
    end.

%% {Pos, Circle}
init_pos_circle() ->
    {0, 1}.

%% 在线跨天
zero_time(Player) ->
    #player_status{role_lattice = RoleLattice} = Player,
    {NewPos, NewCircle} = init_pos_circle(),
    NewRoleLattice0 = RoleLattice#role_lattice{
        pos = NewPos, circle = NewCircle, update_time = utime:unixtime()
    },
    CircleReward    = update_circle_reward(NewCircle, NewPos),
    NewRoleLattice  = NewRoleLattice0#role_lattice{circle_reward = CircleReward},
    NewPlayer       = Player#player_status{role_lattice = NewRoleLattice},
    pp_marble:handle(41409, NewPlayer, []),
    NewPlayer.

%% 秘籍重置
gm_reset(Player) ->
    #player_status{id = RoleId} = Player,
    {NewPos, NewCircle} = init_pos_circle(),
    CircleReward = update_circle_reward(NewCircle, NewPos),
    RoleLattice  = #role_lattice{
        role_id = RoleId, pos = NewPos, circle = NewCircle,
        update_time = utime:unixtime(), circle_reward = CircleReward
    },
    db_replace_role_lattice(RoleLattice),
    NewPlayer = Player#player_status{role_lattice = RoleLattice},
    pp_marble:handle(41409, NewPlayer, []),
    NewPlayer.

%% 获取格子信息
get_role_lattice_info(Player) ->
    #player_status{id = RoleId, sid = Sid, role_lattice = RoleLattice, figure = #figure{lv = RoleLv}} = Player,
    #role_lattice{pos = Pos, circle = Circle, circle_reward = CircleReward} = RoleLattice,
    CfgLv = lib_module:get_open_lv(?MOD_MARBLE),
    if RoleLv >= CfgLv ->
            FreeCount = lib_daily:get_count(RoleId, ?MOD_MARBLE, ?LATTICE_DAILY_FREE),
            ConfigFreeTime = data_marble:get_lattice_kv(lattice_free_times),
            LeftFreeCount = max(0, ConfigFreeTime-FreeCount),
            CircleReward1 = change_format(CircleReward),
            DoubleCheck = is_double_reward_time_range(),
            Times = ?IF(DoubleCheck == true, 2, 1),
            CircleReward2 = [{Step, DoubleSign, [{GoodId, round(GoodNum*Times)} || {GoodId, GoodNum} <- RewardX1]} || {Step, DoubleSign, RewardX1} <- CircleReward1], 
            % ?ERR("~p ~p ~p ~p", [Circle, Pos, LeftFreeCount, CircleReward1]),
            {ok, Bin} = pt_414:write(41409, [?SUCCESS, Circle, Pos, max(Circle-1, 0), LeftFreeCount, CircleReward2, Times]),
            lib_server_send:send_to_sid(Sid, Bin);
        true ->
            {ok, Bin} = pt_414:write(41409, [?ERRCODE(lv_limit), 0, 0, 0, 0, [], 1]),
            lib_server_send:send_to_sid(Sid, Bin)
    end.

change_format(Reward) ->
    lists:foldl(fun({Step, DoubleSign, RewardX}, Acc) ->
        RewardX1 = [{GoodId, GoodNum} || {_GoodType, GoodId, GoodNum} <- RewardX],
        [{Step, DoubleSign, RewardX1}|Acc]
     end, [], Reward).

update_circle_reward(Circle, Pos) ->
    do_update_circle_reward(Circle, Pos, []).

do_update_circle_reward(Circle, Pos, Acc) ->
    ConfigMaxStep = data_marble:get_lattice_kv(max_lattice_step),
    if Pos >= ConfigMaxStep ->
            Acc;
        true ->
            RandStep = get_rand_step(ConfigMaxStep, Pos),
            NewStep  = Pos + RandStep,
            PassStep = lists:seq(Pos+1, NewStep),
            Reward   = calc_step_reward(Circle, PassStep),
            do_update_circle_reward(Circle, NewStep, lists:append(Reward, Acc))
    end.

get_step_reward([], _, _Reward) -> [];
get_step_reward(PassStep, IsDouble, Reward) ->
    lists:foldl(fun(Pass, Acc) ->
        case lists:keyfind(Pass, 1, Reward) of
            {_, _, RewardX} when IsDouble == true ->
                lists:append(multi_times_reward(2, RewardX), Acc);
            {_, _, RewardX} ->
                lists:append(RewardX, Acc);
            _ ->
                Acc
        end
    end, [], PassStep).

multi_times_reward(Times, Reward) ->
    [{GoodsType, GoodsId, round(Times*GoodsNum)} || {GoodsType, GoodsId, GoodsNum} <- Reward].

%% 是否处于双倍活动指定时间
is_double_reward_time_range() ->
    TimeCon = data_marble:get_lattice_kv(double_time_conditon),
    OpenDay = util:get_open_day(),
    WeekDay = utime:day_of_week(),
    OpenDayCheck =
     case lists:keyfind(open_day, 1, TimeCon) of
        {_, MinDay, MaxDay} ->
            OpenDay >= MinDay andalso OpenDay =< MaxDay;
        _ ->
            false
    end,
    WeekCheck =
     case lists:keyfind(week, 1, TimeCon) of
        {_, MinDay1, MaxDay1} ->
            WeekDay >= MinDay1 andalso WeekDay =< MaxDay1;
        _ ->
            false
    end,
    OpenDayCheck andalso WeekCheck.

%% 走格子
lattice_go(Type, Player) ->
    #player_status{id = RoleId, figure = #figure{name = RoleName}, sid = Sid, role_lattice = RoleLattice} = Player,
    #role_lattice{circle = _Circle, pos = Step, circle_reward = CircleReward} = RoleLattice,
    MaxStep = data_marble:get_lattice_kv(max_lattice_step),
    case lattice_go_check(Type, MaxStep, Player) of
        {true, Cost} ->
            case lib_goods_api:cost_object_list_with_check(Player, Cost, lattice_cost, "") of
                {true, NewPlayer, _} ->
                    %% 本次随机步数
                    RandStep = get_rand_step(MaxStep, Step),
                    NewStep  = Step + RandStep,
                    %% 经过步数
                    PassStep = lists:seq(Step+1, NewStep),
                    %% 双倍奖励活动时间段判断
                    DoubleCheck = is_double_reward_time_range(),
                    %% 步数奖励
                    Reward = get_step_reward(PassStep, DoubleCheck, CircleReward),
                    % ?ERR("~p ~p ~p ~p ~p", [RandStep, Step, NewStep, PassStep, Reward]),
                    NewPlayer0 = lib_goods_api:send_reward(NewPlayer, Reward, lattice_reward),
                    NewPlayer1 = NewPlayer0#player_status{role_lattice = RoleLattice#role_lattice{
                        pos = NewStep, update_time = utime:unixtime()}
                    },
                    db_replace_role_lattice(NewPlayer1#player_status.role_lattice),
                    Cost =:= [] andalso lib_daily:increment(RoleId, ?MOD_MARBLE, ?LATTICE_DAILY_FREE),
                    %% 完成一轮走格子
                    if NewStep >= MaxStep ->
                            lib_chat:send_TV({all}, ?MOD_MARBLE, _SubType = 1, [RoleName, RoleId]);
                        true ->
                            skip
                    end,
                    lib_log_api:log_lattice(RoleId, _Circle, Step, RandStep, Cost, Reward),
                    {ok, Bin} = pt_414:write(41410, [?SUCCESS, RandStep, NewStep]),
                    lib_server_send:send_to_sid(Sid, Bin),
                    lib_task_api:soul_roll(NewPlayer1),
                    {ok, NewPlayer1};
                {false, Errcode, _} ->
                    {ok, Bin} = pt_414:write(41410, [Errcode, 0, Step]),
                    lib_server_send:send_to_sid(Sid, Bin),
                    ok
            end;
        {false, Errcode} ->
            {ok, Bin} = pt_414:write(41410, [Errcode, 0, Step]),
            lib_server_send:send_to_sid(Sid, Bin)
    end.

%% 本次随机步数
get_rand_step(MaxStep, Step) ->
    WalkList = data_marble:get_lattice_kv(lattice_toss_rate),
    LeftStep = MaxStep - Step,
    if LeftStep >= ?ROLL_MAX_POINT ->
            RandStep = urand:rand_with_weight(WalkList);
        true ->
            RandStep = LeftStep
    end,
    RandStep.

lattice_go_check(Type, MaxStep, Player) ->
    #player_status{role_lattice = RoleLattice} = Player,
    #role_lattice{pos = Step} = RoleLattice,
    Cost = get_lattice_cost(Type, Player),
    CheckList = [
        {check_step, Step, MaxStep}
    ],
    case util:check_lists(?MODULE, CheckList) of
        true ->
            {true, Cost};
        {false, Errcode} ->
            {false, Errcode};
        _ ->
            {false, ?FAIL}
    end.

lattice_reset(Player) ->
    #player_status{
        id = RoleId, sid = Sid, figure = #figure{vip = Vip},
        role_lattice = #role_lattice{circle = Circle, pos = Pos} = RoleLattice
    } = Player,
    CheckList = [
        {check_vip_reset_count, Vip, Circle},
        {check_vip_reset_cost, Circle},
        {check_pos, Pos}
    ],
    case util:check_lists(?MODULE, CheckList) of
        true ->
            CostList = data_marble:get_lattice_kv(reset_lattice_cost),
            {_, ResetCost} = lists:keyfind(Circle, 1, CostList),
            case lib_goods_api:cost_object_list_with_check(Player, [ResetCost], lattice_reset, "") of
                {true, Player0, _} ->
                    NewPos = 0, NewCircle = Circle + 1,
                    NewRoleLattice0 = RoleLattice#role_lattice{
                        pos = NewPos, circle = NewCircle, update_time = utime:unixtime()
                    },
                    CircleReward  = update_circle_reward(NewCircle, NewPos),
                    ResetCount    = Circle,
                    ResetReward   = data_marble:get_reset_reward(ResetCount),
                    NewRoleLattice = NewRoleLattice0#role_lattice{circle_reward = CircleReward},
                    NewPlayer  = Player0#player_status{role_lattice = NewRoleLattice},
                    if ResetReward =/= [] ->
                            NewPlayer1 = lib_goods_api:send_reward(NewPlayer, ResetReward, lattice_reset_reward);
                        true ->
                            NewPlayer1 = NewPlayer
                    end,
                    lib_daily:increment(RoleId, ?MOD_MARBLE, ?LATTICE_DAILY_RESET),
                    db_replace_role_lattice(NewRoleLattice),
                    pp_marble:handle(41409, NewPlayer1, []),
                    lib_server_send:send_to_sid(Sid, pt_414, 41411, [?SUCCESS]),
                    {ok, NewPlayer1};
                {false, ErrCode, _} ->
                    lib_server_send:send_to_sid(Sid, pt_414, 41411, [ErrCode]),
                    ok
            end;
        {false, Errcode} ->
            lib_server_send:send_to_sid(Sid, pt_414, 41411, [Errcode])
    end.

 get_count_by_vip(Vip, VipPoints) ->
    [{Min, MinCount}|Rest] = lists:keysort(1, VipPoints),
    if Vip =< Min -> MinCount;
        true ->
            do_get_count_by_vip(Vip, MinCount, Rest)
    end.

do_get_count_by_vip(_Vip, Count, []) -> Count;
do_get_count_by_vip(Vip, _Count, [{CfgVip, CfgCount}|Rest]) ->
    if
        Vip =< CfgVip -> CfgCount;
        true ->
            do_get_count_by_vip(Vip, CfgCount, Rest)
    end.

%% 获取本次格子消耗
get_lattice_cost(Type, Player) ->
    #player_status{id = RoleId} = Player,
    FreeCount      = lib_daily:get_count(RoleId, ?MOD_MARBLE, ?LATTICE_DAILY_FREE),
    ConfigFreeTime = data_marble:get_lattice_kv(lattice_free_times),
    Costs          = data_marble:get_lattice_kv(lattice_roll_cost),
    VouForGold     = data_marble:get_lattice_kv(voucher_for_gold),
    LeftCount      = ConfigFreeTime - FreeCount,
    % ?ERR("~p ~p", [ConfigFreeTime, FreeCount]),
    if LeftCount =< 0 ->
            {_, {_, VouCherId, CostNum1}} = lists:keyfind(?VOUCHER, 1, Costs),
            {_, {GoodType, _, GoldNum}}   = lists:keyfind(?GOLD_CONSUME, 1, Costs),
            [{_, RestNum}] = lib_goods_api:get_goods_num(Player, [VouCherId]),
            if Type =:= ?VOUCHER ->
                    if RestNum =:= 0 ->
                            [{GoodType, 0, GoldNum}];
                       RestNum < CostNum1 ->
                            VouNum   = CostNum1 - RestNum,
                            NeedGold = VouNum * VouForGold,
                            [{GoodType, 0, NeedGold}, {?TYPE_GOODS, VouCherId, RestNum}];
                        true ->
                            [{?TYPE_GOODS, VouCherId, CostNum1}]
                    end;
                Type =:= ?GOLD_CONSUME ->
                    [{GoodType, 0, GoldNum}]
            end;
        true -> %% 免费
            []
    end.

calc_step_reward(Circle, PassStep) ->
    Reward = lists:foldl(fun(Step, Acc) ->
        case data_marble:get_lattice_reward(Step, Circle) of
            #base_lattice_reward{reward = RewardX, rand_reward = RandReward, double_chance = DoubleChance} ->
                case urand:rand_with_weight(RandReward) of
                    RandRewardX when is_tuple(RandRewardX) ->
                        PosReward = RewardX ++ [RandRewardX],
                        {DoubleSign, PosReward1} = rand_reward_with_double(DoubleChance, PosReward),
                        [{Step, DoubleSign, PosReward1}|Acc];
                    _ ->
                        if RewardX =/= [] ->
                                {DoubleSign, RewardX1} = rand_reward_with_double(DoubleChance, RewardX),
                                [{Step, DoubleSign, RewardX1}|Acc];
                            true ->
                                Acc
                        end
                end;
            _ ->
                Acc
        end
     end, [], PassStep),
    Reward.

rand_reward_with_double(DoubleChance, Reward) ->
    if DoubleChance =/= [] ->
        DoubleSign = urand:rand_with_weight(DoubleChance),
        {DoubleSign, [{GoodType, GoodId, GoodNum*DoubleSign}|| {GoodType, GoodId, GoodNum} <- Reward]};
        true ->
            {1, Reward}
    end.

db_replace_role_lattice(RoleLattice) ->
    #role_lattice{
        role_id = RoleId,
        pos = Pos,
        circle = Circle,
        update_time = UpdateTime,
        circle_reward = CircleReward
    } = RoleLattice,
    SQL = io_lib:format(?SQL_LATTICE_REPLACE,
     [RoleId, Pos, Circle, UpdateTime, util:term_to_bitstring(CircleReward)]),
    db:execute(SQL),
    ok.

check({check_step, Step, MaxStep}) ->
    if Step < MaxStep ->
            true;
        true ->
            {false, ?ERRCODE(err414_max_step)}
    end;
% check({check_lattice_cost, Cost}) ->
%     if
%         is_list(Cost) andalso Cost =/= [] ->
%             true;
%         true ->
%             {false, ?ERRCODE(data_error)}
%     end;
check({check_vip_reset_count, Vip, Circle}) ->
    VipPoints = data_marble:get_lattice_kv(lattice_vip_point),
    VipResetCount = get_count_by_vip(Vip, VipPoints),
    if VipResetCount == false ->
            {false, ?ERRCODE(err414_have_no_count)};
        true ->
            if Circle > VipResetCount ->
                    {false, ?ERRCODE(err414_vip_count_limit)};
                true ->
                    true
            end
    end;
check({check_vip_reset_cost, Circle}) ->
    CostList = data_marble:get_lattice_kv(reset_lattice_cost),
    case lists:keyfind(Circle, 1, CostList) of
        {_, _ResetCost} ->
            true;
        _ ->
            {false, ?ERRCODE(config_err)}
    end;
check({check_pos, Pos}) ->
    CfgMaxStep = data_marble:get_lattice_kv(max_lattice_step),
    if Pos >= CfgMaxStep ->
            true;
        true ->
            {false, ?ERRCODE(err414_have_not_finish)}
    end;
check(_) ->
    {false, ?FAIL}.