%% ---------------------------------------------------------------------------
%% @doc 个性装扮-逻辑文件
%% @author tyl
%% @since 2019-11-07
%% @end
%% ---------------------------------------------------------------------------
-module(lib_dress_up).
-export([
    login/1,
    handle_event/2,
    load_dress_list/1,                  %% 加载装扮列表
    get_role_frame_dress/1              %% 获取头像框
    ,active_default_dress/2             %% 激活默认装扮
    ,calc_all_dress_up/3
    ,calc_power/3
    ,save_dress_up_combination/3        %% 穿戴装备组合
    ,check_configs/2
    ,check_same_type/1
    ,check_enable/2
    ,check_recycle/2
    ,refresh_attr/2
    ,handle_achievement/2
    ,db_replace_dress_up_enabled/4
    ,db_update_dress_up_enabled/3
    ,gm_active_all_dress_up/1
]).
-include("server.hrl").
-include("common.hrl").
-include("figure.hrl").
-include("errcode.hrl").
-include("rec_dress_up.hrl").
-include("attr.hrl").
-include("predefine.hrl").
-include("def_module.hrl").
-include("def_goods.hrl").
-include("goods.hrl").
-include("def_fun.hrl").
-include("def_event.hrl").
-include("rec_event.hrl").

%%------------------------ 登录初始化---------------------------------------
login(PS) ->
    #player_status{id = RoleId, figure = Figure} = PS,
    {EnabledMap, UsingMap, DressList} = load_dress_list(RoleId),
    NewDressStatus = #status_dress_up{enabled_map = EnabledMap, using_map = UsingMap},
    NewPS = PS#player_status{figure = Figure#figure{dress_list = DressList}, dress_up = NewDressStatus},
    AttrPS = count_dress_up_attribute(NewPS),
    AttrPS.

%% 切换场景之后需要统计初始装扮的数量
handle_event(Player, #event_callback{type_id = ?EVENT_CLIENT_LOGIN_SUCCESS}) ->
    #player_status{dress_up = StatusDressUp} = Player,
    #status_dress_up{enabled_map = EnabledMap} = StatusDressUp,
    handle_achievement(Player, EnabledMap),
    {ok, Player};
handle_event(Player, _) ->
    {ok, Player}.

%% ---------------------------------------------------------------------
%% @doc 计算装扮属性加成 @end
%% ---------------------------------------------------------------------
count_dress_up_attribute(PS) ->
    #player_status{dress_up = StatusDressUp} = PS,
    #status_dress_up{enabled_map = EnabledMap} = StatusDressUp,
    AllType = maps:keys(EnabledMap),
    F = fun(DressType, List) ->
        DressList = maps:get(DressType, EnabledMap, []),
        F1 = fun(DressId, TList1) ->
            case data_dress_up:get_dress_cfg(DressId) of
                [] -> TList1;
                #base_dress_up{dress_attr = DressAttr} -> DressAttr ++ TList1
            end end,
        lists:foldl(F1, List, DressList)
    end,
    EnabledAttrList = lists:foldl(F, [], AllType),
    EnabledAttr = util:combine_list(EnabledAttrList),
    Attr = lib_player_attr:partial_attr_convert(EnabledAttr),
    StatusDressUp2 = StatusDressUp#status_dress_up{attr = Attr},
    PS#player_status{dress_up = StatusDressUp2}.

%% ---------------------------------------------------------------------
%% @doc 加载装扮列表 @end
%% ---------------------------------------------------------------------
load_dress_list(PlayerId) ->
    EnabledList = db:get_all(io_lib:format(?sql_dress_up_enabled_select, [PlayerId])),
    {EnabledMap, UsingMap, DressList} =
        lists:foldl(fun([DressId, DressType, IsUsing], {EMap, UMap, DL}) ->
            TEnList  = maps:get(DressType, EMap, []),
            TEnList1 = [DressId|TEnList],
            if
                IsUsing == ?DRESS_ISUSING ->
                    {
                        maps:put(DressType, TEnList1, EMap),
                        maps:put(DressType, DressId, UMap),
                        [{DressType, DressId}|DL]
                    };
                true ->
                    {
                        maps:put(DressType, TEnList1, EMap),
                        UMap,
                        DL
                    }
            end
                    end, {#{}, #{}, []}, EnabledList),
    {EnabledMap, UsingMap, DressList}.

%% --------------------------------------------------------------------
%% @doc 获得玩家头像框 @end
%% --------------------------------------------------------------------
get_role_frame_dress(Player) when is_record(Player, player_status) ->
    get_role_frame_dress(Player#player_status.figure);
get_role_frame_dress(Figure) when is_record(Figure, figure) ->
    case lists:keyfind(?DRESS_UP_FRAME, 1, Figure#figure.dress_list) of
        {_, FrameId} ->
            FrameId;
        _ ->
            0
    end.

%% --------------------------------------------------------------------
%% @doc  开启默认装扮,主要是满足前端的需求 @end
%% 因为判断条件和存储数据的逻辑有点不同，避免与激活操作混杂起来
%% --------------------------------------------------------------------
active_default_dress(#player_status{sid = Sid, dress_up = StatusDressUp} = Player, DressType) ->
    #status_dress_up{enabled_map = EnabledMap} = StatusDressUp,
    EnabledList = maps:get(DressType, EnabledMap, []),
    case lists:keyfind(DressType, 1, ?DEFAULT_DRESS_LIST) of
        {_, TDressId} ->
            case do_active_default_dress(Player, DressType, TDressId) of
                {true, TmpPS} ->
                    #status_dress_up{enabled_map = NewEnabledMap} = TmpPS#player_status.dress_up,
                    NewEnabledList = maps:get(DressType, NewEnabledMap, []),
                    lib_server_send:send_to_sid(Sid, pt_112, 11200, [?SUCCESS, DressType, NewEnabledList, TDressId]),
                    {ok, TmpPS};
                {false, _Res} ->
                    lib_server_send:send_to_sid(Sid, pt_112, 11200, [?SUCCESS, DressType, EnabledList, TDressId]),
                    {ok, Player}
            end;
        _ ->
            lib_server_send:send_to_sid(Sid, pt_112, 11200, [?SUCCESS, DressType, EnabledList, 0]),
            {ok, Player}
    end.

do_active_default_dress(PS, DressType, DressId) ->
    #player_status{dress_up = StatusDressUp, id = RoleId, figure = Figure} = PS,
    #status_dress_up{enabled_map = OldEnabledMap, using_map = OldUsingMap} = StatusDressUp,
    %% 判断是否默认开启的聊天装扮
    case lists:member(DressId, ?DEFAULT_DRESS_ID) of
        false ->
            {false, ?ERRCODE(err112_dress_not_default)};
        true ->
            CheckList = [{has_enabled, OldEnabledMap, DressType, DressId}],
            case check_enable(PS, CheckList) of
                {true, _Ps} ->
                    %% 判断配置是否存在
                    CheckConfig =
                        case data_dress_up:get_dress_cfg(DressId) of
                            #base_dress_up{} -> true ;
                            _ -> false
                        end,
                    case CheckConfig of
                        false ->
                            {false, ?ERRCODE(err112_dress_not_exist)};
                        _ ->
                            %% 存库,老数据也处理一下
                            {NewUsingMap, NewEnabledMap} = update_default_dress(RoleId, OldEnabledMap, OldUsingMap, DressType, DressId),
                            StatusDressUp1 = StatusDressUp#status_dress_up{enabled_map = NewEnabledMap, using_map = NewUsingMap},
                            DressPS = PS#player_status{dress_up = StatusDressUp1},
                            RefreshedPS = refresh_attr(DressPS, active_dress_up),
                            %% 刷新角色数据
                            F1 = fun(DT, DI, L) -> [{DT, DI} | L] end,
                            NewDressList = maps:fold(F1, [], NewUsingMap),
                            FinalPS = RefreshedPS#player_status{figure = Figure#figure{dress_list = NewDressList}},
                            lib_role:update_role_show(FinalPS#player_status.id, [{figure, FinalPS#player_status.figure}]),
                            % 激活日志
                            % lib_log_api:log_dress_star(RoleId, DressType, DressId, 0, 0, [{0, 0}]),
                            {true, FinalPS}
                    end;
                {false, Res} ->
                    {false, Res}
            end
    end.

%% ------------------------------------------------
%% @doc     更新默认装扮
%% @param    RoleId:玩家Id OldEnabledMap:激活的装扮
%%           OldUsingMap:正在使用的装扮 DressType:装扮类型 DressId:装扮Id
%% @return   UsingMap, EnabledMap
%% @history  2019-11-12
%% @end
%% ------------------------------------------------
update_default_dress(RoleId, OldEnabledMap, OldUsingMap, DressType, DressId) ->
    {NewUsingMap, NewEnabledMap, OldDressId} =
        case maps:get(DressType, OldUsingMap, []) of
            TOldDressId when is_integer(TOldDressId) ->
                UsingMap = maps:put(DressType, DressId, OldUsingMap),
                EnableList = maps:get(DressType, OldEnabledMap, []),
                EnableList1 = [DressId|EnableList],
                EnabledMap1 = maps:put(DressType, EnableList1, OldEnabledMap),
                {UsingMap, EnabledMap1, TOldDressId};
            _ ->
                UsingMap = maps:put(DressType, DressId, OldUsingMap),
                EnableList = maps:get(DressType, OldEnabledMap, []),
                EnableList1 = [DressId|EnableList],
                EnabledMap1 = maps:put(DressType, EnableList1, OldEnabledMap),
                {UsingMap, EnabledMap1, 0}
        end,
    case OldDressId =/= 0 of
        true ->
            F = fun() ->
                lib_dress_up:db_replace_dress_up_enabled(RoleId, DressId, DressType, ?DRESS_ISUSING),
                lib_dress_up:db_replace_dress_up_enabled(RoleId, OldDressId, DressType, ?DRESS_UNUSING),
                true
                end;
        false ->
            F = fun() ->
                lib_dress_up:db_replace_dress_up_enabled(RoleId, DressId, DressType, ?DRESS_ISUSING), true
                end
    end,
    %% 保证存库成功
    case catch db:transaction(F) of
        true -> {NewUsingMap, NewEnabledMap};
        _ -> {OldUsingMap, OldEnabledMap}
    end.

%%检查是否满足激活的条件
check_enable(PS, []) -> {true, PS};
check_enable(PS, [H | T]) ->
    case do_check_enable(PS, H) of
        true -> check_enable(PS, T);
        {false, Res} -> {false, Res}
    end.

%%检查所需物品是否足够
do_check_enable(PS, {goods_enough, CostId, ActiveNum}) ->
    case lib_goods_api:check_object_list(PS, [{?TYPE_GOODS, CostId, ActiveNum}]) of
        true -> true;
        {false, Res} -> {false, Res}
    end;
%%检查装扮是否已经被激活（未激活则通过检查）
do_check_enable(_PS, {has_enabled, EnabledMap, DressType, DressId}) ->
    case maps:get(DressType, EnabledMap, []) of
        [_|_] = DressList ->
            case lists:member(DressId, DressList) of
                true -> {false, ?ERRCODE(err112_dress_has_enabled)};
                _ -> true
            end;
        _ -> true
    end;
%%扣除消耗物品返回新状态
do_check_enable(PS, {cost_goods, CostId, ActiveNum}) ->
    GoodsList = [{?TYPE_GOODS, CostId, ActiveNum}],
    case lib_goods_api:check_object_list(PS, GoodsList) of
        true -> true;
        {false, Res} -> {false, Res}
    end.

%% 检查是否满足回收条件
check_recycle(PS, []) -> {true, PS};
check_recycle(PS, [H|T]) ->
    case do_check_recycle(PS, H) of
        true -> check_recycle(PS, T);
        {false, Res} -> {false, Res}
    end.

%% 是否已经激活
do_check_recycle(_PS, {has_active, EnabledMap, DressType, DressId}) ->
    case maps:get(DressType, EnabledMap, []) of
        [_|_] = DressList ->
            case lists:member(DressId, DressList) of
                false -> {false, ?ERRCODE(err112_dress_has_not_enabled)};
                _ -> true
            end;
        _ ->
            true
    end;
%% 是否有物品
do_check_recycle(_PS, {has_good, GoodsId}) ->
    case lib_goods_api:get_goods_info(GoodsId) of
        #goods{type = Type} when Type == ?GOODS_TYPE_FIGURE_1
            orelse Type == ?GOODS_TYPE_CHAT_BUBBLE
            orelse Type == ?GOODS_TYPE_CHAT_BACKGROUND ->
            true;
        _ ->
            {false, ?ERRCODE(err150_no_goods)}
    end.

check_configs(DressList, Len) ->
    Filter = fun(DressId, TmpList) ->
        case DressId > 0 of
            true ->
                case data_dress_up:get_dress_cfg(DressId) of
                    #base_dress_up{} -> [1|TmpList];
                    _ -> [0|TmpList]
                end;
            _ -> [0|TmpList]
        end
             end,
    lists:sum(lists:foldl(Filter, [], DressList)) =:= Len.

check_same_type(NeedDressList) ->
    F = fun(DressId, {Ack, TMap}) ->
        case data_dress_up:get_dress_cfg(DressId) of
            #base_dress_up{dress_type = Type} ->
                case maps:get(Type, TMap, []) of
                    Num when is_integer(Num) -> {false, maps:put(Type, Num+1, TMap)};
                    _ -> {Ack, maps:put(Type, 1, TMap)}
                end;
            _ -> {false, TMap}
        end end,
    {Check, _} = lists:foldl(F, {true, #{}}, NeedDressList),
    Check.

%% -----------------------------------------------------------------
%% @doc     使用(保存)当前装扮组合
%% @param    Player:玩家信息    DressUpList  装扮Id列表
%% @return   {true, NewPlayer, FList} | {false, Err, _Player2}
%%            % FList 保存的装扮列表
%% @history   update 2019-11-07
%% @end
%% -----------------------------------------------------------------
save_dress_up_combination([], NewPlayer, FList) -> {true, NewPlayer, FList};
save_dress_up_combination([DressId|L], Player, TList) ->
    case do_save_single_dress(DressId, Player) of
        {true, NewPlayer, List} -> save_dress_up_combination(L, NewPlayer, [List|TList]);
        {false, Err, _Player2} -> {false, Err, _Player2}
    end.

do_save_single_dress(DressId, PS) ->
    #player_status{figure = Figure, dress_up = DressStatus} = PS,
    #status_dress_up{using_map = UsingMap, enabled_map = EnabledMap} = DressStatus,
    case data_dress_up:get_dress_cfg(DressId) of
        #base_dress_up{dress_type = DressType} ->
            case maps:get(DressType, EnabledMap, []) of
                [] -> {false, ?ERRCODE(err112_dress_not_enabled), PS};
                [_|_] = DressList ->
                    case lists:member(DressId, DressList) of
                        true ->
                            PreDressId = case maps:get(DressType, UsingMap, 0) of
                                             0 -> 0;
                                             TempId -> TempId
                                         end,
                            NewUsingMap = maps:put(DressType, DressId, UsingMap),
                            F1 = fun(DT, DI, L) -> [{DT, DI} | L] end,
                            NewDressList = maps:fold(F1, [], NewUsingMap),
                            NewFigure = Figure#figure{dress_list = NewDressList},
                            NewStateDressUp = DressStatus#status_dress_up{using_map = NewUsingMap},
                            NewPS = PS#player_status{figure = NewFigure, dress_up = NewStateDressUp},
                            {true, NewPS, {{DressType, DressId}, PreDressId}};
                        _ ->
                            {false, ?ERRCODE(err112_dress_not_enabled), PS}
                    end
            end;
        _ -> {false, ?ERRCODE(err112_dress_not_exist), PS}
    end.

%% 统计所有激活的装扮(对应装扮类型)
calc_all_dress_up(Lv, DressType, EnableMap) ->
    DressIdList = maps:get(DressType, EnableMap, []),
    DefaultId = data_dress_up:get_default_dress(DressType, 1),
    DressIdList1 = case Lv < ?DRESS_DEFAULT_LV of
                       true -> DressIdList;
                       _ ->
                           case DressIdList of
                               [_|_] ->
                                   case lists:member(DefaultId, DressIdList) of
                                       true -> DressIdList;
                                       _ -> [DefaultId|DressIdList]
                                   end;
                               _ -> [DefaultId|DressIdList]
                           end
                   end,
    F = fun(DressId, {TList, TNum}) ->
        case data_dress_up:get_dress_cfg(DressId) of
            #base_dress_up{dress_type = DressType} ->
                IsOpen = is_satisfy_open(DressId),
                case data_goods_type:get(DressId) of
                    #ets_goods_type{color = Color} ->
                        case lists:keyfind({1, Color}, 1, TList) of
                            {_, Num} ->
                                {
                                    lists:keystore({1, Color}, 1, TList, {{1, Color}, Num + 1}),
                                    case IsOpen of false -> TNum + 1; _ -> TNum end
                                };
                            _ ->
                                {
                                    lists:keystore({1, Color}, 1, TList, {{1, Color}, 1}),
                                    case IsOpen of false -> TNum + 1; _ -> TNum end
                                }
                        end;
                    _ -> {TList, TNum}
                end;
            _ -> {TList, TNum}
        end end,
    {ColorList, NotOpenNum} = lists:foldl(F, {[], 0}, DressIdList1),
    {ColorList, NotOpenNum}.

calc_power(Status, AttrPower, BaseAttr) ->
    #player_status{dress_up = DressUp} = Status,
    #status_dress_up{attr = AttrL} = DressUp,
    PartialAttr = attr_conv:to_attr_record(AttrL),
    Power = lib_player_attr:calc_attr_power_partial(AttrPower, BaseAttr, PartialAttr),
    NewStatus = Status#player_status{dress_up = DressUp#status_dress_up{power = Power}},
    {NewStatus, 0}.

is_satisfy_open(DressId) ->
    OpenDay = util:get_open_day(),
    NowTime = utime:unixtime(),
    case data_dress_up:get_dress_cfg(DressId) of
        #base_dress_up{open_day = CfgOpenDay, open_time = CfgOpenTime}
            when is_integer(CfgOpenDay), is_integer(CfgOpenTime) ->
            case OpenDay >= CfgOpenDay andalso NowTime >= CfgOpenTime of
                true -> true;
                _ -> false
            end;
        _ -> false
    end.

refresh_attr(PS, Type) ->
    NewPS = count_dress_up_attribute(PS),
    CountAttrPS = lib_player:count_player_attribute(NewPS, ?MOD_DRESS_UP, Type),
    lib_player:send_attribute_change_notify(CountAttrPS, ?NOTIFY_ATTR),
    CountAttrPS.

%% 激活总数成就
handle_achievement(#player_status{figure = Figure} = Player, EnabledMap) ->
    {ColorList, _NotOpenNum} = lib_dress_up:calc_all_dress_up(Figure#figure.lv, 1, EnabledMap),
    {ColorList1, _NotOpenNum1} = lib_dress_up:calc_all_dress_up(Figure#figure.lv, 2, EnabledMap),
    F = fun({_, Num}, TSumNum) ->
        Num + TSumNum
        end,
    SumNum = lists:foldl(F, 0, ColorList ++ ColorList1),
    lib_achievement_api:active_dress_up_event(Player, SumNum),
    lib_common_rank_api:refresh_rank_by_dress_up(Player, SumNum),
    ok.

%% gm激活所有装扮
gm_active_all_dress_up(Player) ->
    #player_status{dress_up = DressStatus} = Player,
    #status_dress_up{enabled_map = OldEnabledMap} = DressStatus,
    %% 激活头像框
    DressList1 = data_dress_up:get_dress_list(1),
    %% 激活聊天气泡
    DressList2 = data_dress_up:get_dress_list(2),
    F = fun(DressId, TEnableMap) ->
        case data_dress_up:get_dress_cfg(DressId) of
            #base_dress_up{dress_type = DressType} ->
                DressList = maps:get(DressType, TEnableMap, []),
                EnabledMap1 = maps:put(DressId, [DressId|DressList], TEnableMap),
                db_replace_dress_up_enabled(Player#player_status.id, DressId, DressType, ?DRESS_UNUSING),
                EnabledMap1;
            _ ->
                TEnableMap
    end end,
    NewEnabledMap = lists:foldl(F, OldEnabledMap, DressList1++DressList2),
    StatusDressUp2 = DressStatus#status_dress_up{enabled_map = NewEnabledMap},
    SettedDressPS = Player#player_status{dress_up = StatusDressUp2},
    RefreshedPS = refresh_attr(SettedDressPS, active_dress_up),
    DefaultDress = [
        {data_dress_up:get_default_dress(1,1), 0},
        {data_dress_up:get_default_dress(2,1), 0}
    ],
    %% 穿戴上默认装扮
    {ok, FinPS} = pp_dress_up:handle(11202, RefreshedPS, [DefaultDress]),
    %% 更新一下前端
    pp_dress_up:handle(11200, RefreshedPS, [1]),
    pp_dress_up:handle(11200, RefreshedPS, [2]),
    FinPS.

%%---------------------------------------db 操作----------------------------------------------
db_replace_dress_up_enabled(PlayerId, DressId, DressType, IsUsed) ->
    NowTime = utime:unixtime(),
    db:execute(io_lib:format(?sql_dress_up_replace_enabled, [PlayerId, DressId, DressType, IsUsed, NowTime])),
    true.
db_update_dress_up_enabled(PlayerId, DressId, IsUsing) ->
    NowTime = utime:unixtime(),
    Sql = io_lib:format(?sql_dress_up_update_enabled, [IsUsing, NowTime, PlayerId, DressId]),
    db:execute(Sql),
    true.

