%% ------------------------------------
%% desc     :唤神副本人物相关
%% module   :lib_role_god_dun.erl
%% author   :hh
%% time     :20/08/07
%% -----------------------------------

-module(lib_role_god_dun).

-include("server.hrl").
-include("god_dun.hrl").
-include("rec_event.hrl").
-include("def_event.hrl").
-include("def_module.hrl").
-include("dungeon.hrl").
-include("figure.hrl").
-include("attr.hrl").
-include("def_fun.hrl").
-include("errcode.hrl").
-include("def_goods.hrl").
-include("common.hrl").

-export([
    login/1
    ,logout/1
    ,handle_event/2
    ,daily_reset/1
    ,get_role_god_shop/1
    ,god_shop_exchange/3
    ]).

login(RoleId) ->
    case db:get_row(?SQL_ROLE_GOD_DUN_SELECT, [RoleId]) of
        [DayShopsBin, DayTime, WeekShopsBin, WeekTime, AttrsBin|_] ->
            #role_god_dun{
                day_shop_list = util:bitstring_to_term(DayShopsBin),
                day_time = DayTime,
                week_shop_list = util:bitstring_to_term(WeekShopsBin),
                week_time = WeekTime,
                role_attrs = util:bitstring_to_term(AttrsBin)
            };
        _ ->
            #role_god_dun{}
    end.

logout(Player) ->
    #player_status{id = RoleId, role_god_dun = RoleGodDun} = Player,
    db_update_role_data(RoleId, RoleGodDun),
    ok.

db_update_role_data(RoleId, RoleGodDun) ->
    #role_god_dun{
        day_shop_list = DayShops,
        day_time = DayTime,
        week_shop_list = WeekShops,
        week_time = WeekTime,
        role_attrs = Attrs
    } = RoleGodDun,
    DayShopsBin = util:term_to_bitstring(DayShops),
    WeekShopsBin = util:term_to_bitstring(WeekShops),
    AttrsBin = util:term_to_bitstring(Attrs),
    db:execute(?SQL_ROLE_GOD_DUN_REPLACE, [RoleId, DayShopsBin, DayTime, WeekShopsBin, WeekTime, AttrsBin]),
    ok.

handle_event(Player, #event_callback{type_id = ?EVENT_LOGIN_CAST}) ->
    #player_status{role_god_dun = RoleGodDun, figure = #figure{lv = RoleLv}} = Player,
    % OpenLv = lib_module:get_open_lv(?MOD_DUNGEON, ?DUNGEON_TYPE_GOD),
    IsOpen = is_god_shop_open(RoleLv),
    if
        not IsOpen ->
            {ok, Player};
        true ->
            #role_god_dun{
                day_shop_list = DayShops,
                day_time = DayTime,
                week_shop_list = WeekShops,
                week_time = WeekTime
            } = RoleGodDun,
            Now = utime:unixtime(),
            {NewDayTime, NewDayShops}   = check_daily_shop_update(RoleLv, DayShops, DayTime, Now),
            {NewWeekTime, NewWeekShops} = check_week_shop_update(RoleLv, WeekShops, WeekTime, Now),
            NewRoleGodDun = RoleGodDun#role_god_dun{
                day_shop_list = NewDayShops,
                day_time = NewDayTime,
                week_shop_list = NewWeekShops,
                week_time = NewWeekTime
            },
            NewPlayer = Player#player_status{role_god_dun = NewRoleGodDun},
            {ok, NewPlayer}
    end;

handle_event(Player, #event_callback{type_id = ?EVENT_LV_UP}) ->
    #player_status{figure = #figure{lv = RoleLv}, role_god_dun = RoleGodDun} = Player,
    OpenLv = lib_module:get_open_lv(?MOD_DUNGEON, ?DUNGEON_TYPE_GOD),
    if
        RoleLv == OpenLv ->
            #role_god_dun{
                day_shop_list = DayShops,
                day_time = DayTime,
                week_shop_list = WeekShops,
                week_time = WeekTime
            } = RoleGodDun,
            Now = utime:unixtime(),
            {NewDayTime, NewDayShops}   = check_daily_shop_update(RoleLv, DayShops, DayTime, Now),
            {NewWeekTime, NewWeekShops} = check_week_shop_update(RoleLv, WeekShops, WeekTime, Now),
            NewRoleGodDun = RoleGodDun#role_god_dun{
                day_shop_list = NewDayShops,
                day_time = NewDayTime,
                week_shop_list = NewWeekShops,
                week_time = NewWeekTime
            },
            NewPlayer = Player#player_status{role_god_dun = NewRoleGodDun},
            {ok, NewPlayer};
        true ->
            {ok, Player} 
    end;

handle_event(Player, #event_callback{type_id = ?EVENT_COMBAT_POWER}) ->
    #player_status{role_god_dun = RoleGodDun, battle_attr = #battle_attr{base_attr_map = Map}} = Player,
    NewAttrs = get_new_base_attr(Map),
    NewRoleGodDun = RoleGodDun#role_god_dun{role_attrs = NewAttrs},
    NewPlayer = Player#player_status{role_god_dun = NewRoleGodDun},
    {ok, NewPlayer}.

daily_reset(Player) ->
    {ok, NewPlayer} = handle_event(Player, #event_callback{type_id = ?EVENT_LOGIN_CAST}),
    get_role_god_shop(NewPlayer),
    NewPlayer.

get_new_base_attr(Map) ->
    lists:foldl(fun(AttrId, Acc) -> 
        Val = maps:get(AttrId, Map, 0),
        [{AttrId, Val}|Acc]
    end, [], ?GOD_DUN_USE_ATTR).

%% TODO 上线检查日常商店和周常商店更新
check_daily_shop_update(RoleLv, ShopL, DayTime, Now) ->
    IsToday = utime:is_same_day(DayTime, Now),
    if
        DayTime == 0 orelse (not IsToday) ->
            NewDayTime = Now,
            NewShopL = get_rand_day_shop(day, RoleLv),
            {NewDayTime, NewShopL};
        true ->
            {DayTime, ShopL}
    end.

check_week_shop_update(RoleLv, ShopL, WeekTime, Now) ->
    IsSameWeek = utime:is_same_week(WeekTime, Now),
    if
        WeekTime == 0 orelse (not IsSameWeek) ->
            NewWeekTime = Now,
            NewShopL = get_rand_day_shop(week, RoleLv),
            {NewWeekTime, NewShopL};
        true ->
            {WeekTime, ShopL}
    end.


get_rand_day_shop(Type, RoleLv) ->
    case data_god_dun:get_shop_rule(RoleLv) of
        #base_god_dun_shop_rule{
            fix_goods = FixGoods,         %% 每日固定商品列表
            rand_goods = CfgRandGoods,        %% 每日随机商品列表
            rand_num = RandNum,           %% 每日随机商品数
            week_fix_goods = WeekFixGoods,    %% 每周固定商品列表
            week_rand_goods = CfgWeekRandGoods,   %% 每周随机商品列表
            week_rand_num = WeekRandNum       %% 每周随机商品数            
        } ->
            FixGoods1 = [{GoodsId, 0, MaxCount} || {GoodsId, MaxCount} <- FixGoods],
            WeekFixGoods1 = [{GoodsId1, 0, MaxCount1} || {GoodsId1, MaxCount1} <- WeekFixGoods],
            ChooseGoods = ?IF(Type == day, CfgRandGoods, CfgWeekRandGoods),
            ChooseNum   = ?IF(Type == day, RandNum, WeekRandNum),
            RandGoods = urand:list_rand_by_weight(ChooseGoods, ChooseNum),
            RandGoods1 = [{GoodsIdX, 0, MaxCountX} || {GoodsIdX, MaxCountX} <- RandGoods],
            ?IF(Type == day, lists:append(FixGoods1, RandGoods1), lists:append(WeekFixGoods1, RandGoods1));
        _ ->
            []
    end.

get_role_god_shop(Player) ->
    #player_status{sid = Sid, role_god_dun = RoleGodDun} = Player,
    #role_god_dun{
        day_shop_list = DayShopL,
        week_shop_list = WeekShopL
    } = RoleGodDun,
    Data = pack_query_shop_data(1, DayShopL, []),
    NewData = pack_query_shop_data(2, WeekShopL, Data),
    lib_server_send:send_to_sid(Sid, pt_610, 61090, [NewData]),
    ok.

god_shop_exchange(Player, ShopId, Num) ->
    #player_status{id = RoleId, sid = Sid, role_god_dun = RoleGodDun, figure = #figure{lv = RoleLv}, currency_map = Cmap} = Player,
    #role_god_dun{day_shop_list = DayShopL, week_shop_list = WeekShopL, day_time = DayTime, week_time = WeekTime} = RoleGodDun,
    IsOpen = is_god_shop_open(RoleLv),
    ShopInfo = get_shop_info(ShopId, DayShopL, WeekShopL),
    ShopCfg = data_god_dun:get_goods(ShopId),
    if
        not IsOpen ->
            {false, ?ERRCODE(lv_limit)};
        not is_record(ShopCfg, base_god_dun_shop_goods) ->
            {false, ?ERRCODE(missing_config)};
        ShopInfo == [] ->
            {false, ?ERRCODE(err610_god_dun_not_exist_shop)};
        true ->
            #base_god_dun_shop_goods{price = Pirce, goods_id = GoodsId, is_bind = IsBind, goods_num = GoodsNum} = ShopCfg,
            {Type, {ShopId, UseCount, MaxCount}} = ShopInfo,
            NewUseCount = UseCount + Num,
            IsEnough = check_cost_enough(Cmap, Pirce, Num),
            if
                MaxCount < NewUseCount->
                    {false, ?ERRCODE(err610_shop_use_count_max)};
                IsEnough =/= true ->
                    IsEnough;
                true ->
                    GoodsList = get_shop_goods(Num, GoodsId, IsBind, GoodsNum),
                    Cost = round(Pirce*Num),
                    Res = 
                    if
                        Cost =/= 0 ->
                            CostL = [{?TYPE_CURRENCY, ?GOODS_ID_GOD_SCORE, Cost}],
                            case lib_goods_api:cost_object_list_with_check(Player, CostL, god_dun_shop_exchange_cost, "") of
                                {true, NewPlayer, _} ->
                                    {true, NewPlayer};
                                {false, ErrorCode, _NewPS} ->
                                    {false, ErrorCode}
                            end;
                        true ->
                            {true, Player}
                    end,
                    case Res of
                        {true, NewPlayer1} ->
                            NewPlayer2 = lib_goods_api:send_reward(NewPlayer1, GoodsList, god_shop_exchange_reward),
                            {NewDayShops, NewWeekShops} = update_shop_count(Type, {ShopId, max(0, NewUseCount), MaxCount}, DayShopL, WeekShopL),
                            Now = utime:unixtime(),
                            NewRoleGodDun = RoleGodDun#role_god_dun{
                                day_shop_list = NewDayShops, week_shop_list = NewWeekShops,
                                day_time = ?IF(Type == 1, Now, DayTime),
                                week_time = ?IF(Type == 2, Now, WeekTime)
                            },
                            db_update_role_data(RoleId, NewRoleGodDun),
                            NewPlayer3 = NewPlayer2#player_status{role_god_dun = NewRoleGodDun},
                            lib_server_send:send_to_sid(Sid, pt_610, 61091, [?SUCCESS, ShopId, max(0, MaxCount-NewUseCount)]),
                            {true, NewPlayer3};
                        {false, ErrorCode1} ->
                            {false, ErrorCode1}
                    end
            end
    end.

check_cost_enough(Map, Price, Num) ->
    TotalCost = round(Price*Num),
    Score = maps:get(?GOODS_ID_GOD_SCORE, Map, 0),
    if
        Num == 0 ->
            {false, ?FAIL};
        Score < TotalCost ->
            {false, ?ERRCODE(err610_score_not_enough)};
        true ->
            true
    end.

update_shop_count(Type, {ShopId, _, _} = NewShopInfo, DayShopL, WeekShopL) ->
    if
        Type == 1 ->
            NewDayShopL = lists:keystore(ShopId, 1, DayShopL, NewShopInfo),
            {NewDayShopL, WeekShopL};
        true ->
            NewWeekShopL = lists:keystore(ShopId, 1, WeekShopL, NewShopInfo),
            {DayShopL, NewWeekShopL}
    end.


get_shop_info(ShopId, DayShopL, WeekShopL) ->
    case lists:keyfind(ShopId, 1, DayShopL) of
        {_, UseCount, MaxCount} ->
            {1, {ShopId, UseCount, MaxCount}};
        _ ->
            case lists:keyfind(ShopId, 1, WeekShopL) of
                {_, UseCount1, MaxCount1} ->
                    {2, {ShopId, UseCount1, MaxCount1}};
                _ ->
                    []
            end
    end.

get_shop_goods(Num, GoodsId, IsBind, GoodsNum) ->
    if
        IsBind == 1 ->
            [{?TYPE_BIND_GOODS, GoodsId, round(Num*GoodsNum)}];
        true ->
            [{?TYPE_GOODS, GoodsId, round(Num*GoodsNum)}]
    end.

pack_query_shop_data(Type, ShopL, PackData) ->
    lists:foldl(fun({GoodsId, UseCount, MaxCount}, Acc) -> 
        [{GoodsId, Type, MaxCount, max(0, MaxCount-UseCount)}|Acc]
    end, PackData, ShopL).


is_god_shop_open(RoleLv) ->
    OpenLv = lib_module:get_open_lv(?MOD_DUNGEON, ?DUNGEON_TYPE_GOD),
    if
        RoleLv >= OpenLv ->
            true;
        true ->
            false
    end.