%%%-----------------------------------------------------------------------------
%%%	@Module  : lib_designation
%%% @Author  : liuxl
%%% @Email   : liuxingli@suyougame.com
%%% @Created : 2016-11-21
%%% @Description : 称号系统
%%%-----------------------------------------------------------------------------
-module(lib_designation).
-compile(export_all).
-include("server.hrl").
-include("designation.hrl").
-include("predefine.hrl").
-include("common.hrl").
-include("errcode.hrl").
-include("goods.hrl").
-include("figure.hrl").
-include("def_module.hrl").
-include("def_fun.hrl").
-include("def_goods.hrl").
-include("def_id_create.hrl").
-include("language.hrl").

-export([
     get_player_dsgt/1,			%% 获取已获得的称号列表

     change_dsgt/3,				%% 佩戴称号
     hide_dsgt/2,               %% 卸下称号
     use_dsgt_goods/3,			%% 使用称号道具
     
     db_get_designation/1,      %% 获取玩家当前佩戴的称号名和id
     
     active_dsgt/2,             %% 玩家进程调用激活称号
     active_dsgt_do/5,		    %%
     active_dsgt_not_online/2,	%% 玩家不在线时激活称号
     cancel_dsgt/2,				%% 删除称号
     remove_dsgt/2,
     cancel_dsgt_by_id/2,		%%
     
     update_dsgt_attr/1,        %% 统计玩家称号属性
     calc_power/3,
     get_all_actived_dsgt/1
    ,calc_all_dsgt/1            %% 统计所有激活称号品质
    ]).

-export([active_all_dsgt/1]).

%% 获取玩家当前已经激活的称号列表
get_player_dsgt(DsgtStatus) ->
    case maps:to_list(DsgtStatus#dsgt_status.dsgt_map) of
        [] -> {[], []};
        List ->
            NowTime = utime:unixtime(),
            change_to_client_format(List, NowTime, [], [])
    end.

%% 佩戴称号
change_dsgt(DsgtId, UseDsgtId, DsgtStatus) ->
    Now = utime:unixtime(),
    #dsgt_status{player_id = PlayerId, dsgt_map = DsgtMap} = DsgtStatus,
    if
        UseDsgtId == 0 -> NewDsgtMap = DsgtMap;
        true ->
            case maps:find(UseDsgtId, DsgtMap) of
                {ok, SrcDesignation} ->
                    OldDesignation = SrcDesignation#designation{status = ?STATUS_UNUSED, time = Now},
                    lib_designation_util:update_dsgt_state(PlayerId, UseDsgtId, ?STATUS_UNUSED,SrcDesignation#designation.dress_time),
                    NewDsgtMap = maps:put(UseDsgtId, OldDesignation, DsgtMap);
                error ->
                    lib_designation_util:delete_dsgt(PlayerId, UseDsgtId),
                    NewDsgtMap = DsgtMap
            end
    end,
    %% 佩戴称号
    {ok, DstDesignation} = maps:find(DsgtId, DsgtMap),
    NewCurDesignation = DstDesignation#designation{status = ?STATUS_USED,  time = Now,dress_time = Now},
    lib_designation_util:update_dsgt_state(PlayerId, DsgtId, ?STATUS_USED,Now),
    %% 更新玩家称号状态
    NDsgtMap = maps:put(DsgtId, NewCurDesignation, NewDsgtMap),
    DsgtStatus#dsgt_status{dsgt_map = NDsgtMap}.

%% 卸下称号
hide_dsgt(DsgtId, DsgtStatus) ->
    Now = utime:unixtime(),
    #dsgt_status{player_id = PlayerId, dsgt_map = DsgtMap} = DsgtStatus,
    {ok, CurDesignation} = maps:find(DsgtId, DsgtMap),
    NewDesignation = CurDesignation#designation{status = ?STATUS_UNUSED, time = Now},
    lib_designation_util:update_dsgt_state(PlayerId, DsgtId, ?STATUS_UNUSED,CurDesignation#designation.dress_time),
    NewDsgtMap = maps:put(DsgtId, NewDesignation, DsgtMap),
    NewDsgtStatus = DsgtStatus#dsgt_status{dsgt_map = NewDsgtMap},
    NewDsgtStatus.

%% 获取玩家当前佩戴的称号名和id
db_get_designation(Id) ->
    Sql = io_lib:format(?SQL_DSGT_SEL_USED_ID, [Id]),
    case db:get_row(Sql) of
        [DsgtId|_] -> DsgtId;
        _          -> 0
    end.

%% 使用称号道具
use_dsgt_goods(Ps, GoodsInfo, GoodsNum) ->
    {DgstId, TrialTime, IsOverLay} = lib_designation_api:get_dsgt(GoodsInfo#goods.goods_id),
    case data_designation:get_by_id(DgstId) of
        #base_designation{} ->
            ActivePara = #active_para{id = DgstId, goods_id = GoodsInfo#goods.goods_id, expire_time = TrialTime, is_overlay = IsOverLay},
            case active_dsgt_with_goods(Ps, ActivePara, GoodsNum) of
                {ok, NewPS} ->
                    {_, NewPS1} = lib_goods_api:cost_object_list(NewPS, [{0, GoodsInfo#goods.goods_id, GoodsNum}], goods_use, ""),
                    {ok, NewPS1, GoodsNum};
                {add_money, NewPS, Price} ->
                    {_, NewPS1} = lib_goods_api:cost_object_list(NewPS, [{0, GoodsInfo#goods.goods_id, GoodsNum}], goods_use, ""),
                    dsgt_recover(NewPS#player_status.id, Price, GoodsInfo#goods.goods_id),
                    case Price of
                        [{_, _, Num}|_] -> ok;
                        _ -> Num = 0
                    end,
                    {use_designtion_good, NewPS1, Num, GoodsNum};
                {fail, Err, _NewPS} ->
                    {fail, Err}
            end;
        _ ->
            {fail, ?MISSING_CONFIG, Ps}
    end.


%% 称号回收
dsgt_recover(RoleId, Price, GoodsId) ->
    case Price of
        [{PriceType, _, Num}|_] ->
            Title = utext:get(4110001),
            Content = utext:get(4110002),
            lib_mail_api:send_sys_mail([RoleId], ?MOD_DESIGNATION, Title, Content, Price),
            lib_server_send:send_to_uid(RoleId, pt_411, 41107, [GoodsId, PriceType, Num]);
        _ -> 
            ok
    end,
    ok.

%% ================================= 激活称号 =================================
%% 玩家进程激活称号
%% Id：待激活的称号id
active_dsgt(Ps, #active_para{id = Id} = ActivePara) when is_record(Ps, player_status) ->
    #player_status{sid = Sid} = Ps,
    % ?ERR("~p ~p", [Ps#player_status.id, ActivePara]),
    case data_designation:get_by_id(Id) of
        DsgtCfgInfo = #base_designation{} ->
            case active_dsgt_do(Ps, DsgtCfgInfo, ActivePara, ?OP_ACTIVE, 1) of
                {true, #designation{end_time = EndTime}, NewPS} ->
                    {ok, BinData} = pt_411:write(41104, [1, Id, EndTime]),
                    lib_server_send:send_to_sid(Sid, BinData),
                    {ok, NewPS};
                {true, NewPS} ->
                    {ok, NewPS};
                {add_money, NewPS, Price} ->
                    dsgt_recover(Ps#player_status.id, Price, DsgtCfgInfo#base_designation.goods_id),
                    {ok, NewPS};
                {fail, _Error} ->
                    {ok, Ps}
            end;
        _ ->
            {ok, Ps}
    end.

%% 使用道具激活称号激活称号
active_dsgt_with_goods(PS, ActivePara, GoodsNum) ->
    #player_status{id = PlayerId, sid = Sid} = PS,
    #active_para{id = DsgtId, goods_id = GoodsId, expire_time = VaildTime, is_overlay = IsOverLay} = ActivePara,
    case data_designation:get_by_id(DsgtId) of
        DsgtCfgInfo = #base_designation{is_global = Global}->
            if
                Global > 0 ->
                    lib_designation_api:active_global_dsgt(DsgtId, [PlayerId], Global, {GoodsId, VaildTime, IsOverLay}),
                    {ok, PS};
                Global == 0 ->
                    case active_dsgt_do(PS, DsgtCfgInfo, ActivePara, ?OP_PROP, GoodsNum) of
                        {true, Designation, NewPS} ->
                            #designation{end_time = EndTime} = Designation,
                            %% 获得称号通知
                            {ok, BinData} = pt_411:write(41104, [1, DsgtId, EndTime]),
                            lib_server_send:send_to_sid(Sid, BinData),
                            {ok, NewPS};
                        {true, NewPS} ->
                            {ok, NewPS};
                        {add_money, NewPS, SellPrice} ->
                            {add_money, NewPS, SellPrice};
                        {fail, Errno} ->
                            {fail, Errno, PS}
                    end;
                true ->
                    {fail, ?FAIL, PS}
            end;
        _ ->
            {fail, ?MISSING_CONFIG, PS}
    end.

%% 激活称号
active_dsgt_do(PS, DsgtCfgInfo, ActivePara, Op, GoodsNum) ->
    #active_para{goods_id = GoodsId, expire_time = ExpireTime, is_overlay = IsOverLay} = ActivePara,
    if
        %% 激活的是有时间限制的称号
        ExpireTime > 0 ->
            active_timed_dsgt(PS, DsgtCfgInfo, GoodsId, ExpireTime, IsOverLay, Op, GoodsNum);
        %% 激活的是永久称号
        true ->
            active_permanent_dsgt(PS, DsgtCfgInfo, GoodsId, Op, GoodsNum)
    end.

%% 激活有时间限制的称号
%% DsgtCfgInfo：需激活的称号信息#base_designation
active_timed_dsgt(PS, DsgtCfgInfo, GoodsId, ExpireTime, IsOverLay, Op, GoodsNum) ->
    Now = utime:unixtime(),
    #player_status{id = RoleId, dsgt_status = DsgtStatus} = PS,
    #base_designation{id = Id} = DsgtCfgInfo,
    DsgtMap = DsgtStatus#dsgt_status.dsgt_map,
    case maps:get(Id, DsgtMap, none) of
        none ->
            {true, R, NewPS} = active_new_dsgt(PS, DsgtStatus, DsgtCfgInfo, ExpireTime, Op),
            DsgtRet = check_dress_dsgt(NewPS, R, Id),
            DsgtRet;
        Designation ->
            if
                Designation#designation.end_time == 0 ->
                    case get_dsgt_return_price(GoodsId) of
                        ReturnMoney = [_|_] ->
                            {add_money, PS, ReturnMoney};
                        _ -> 
                            {fail, ?ERRCODE(err411_already_exist)}
                    end;
                Designation#designation.end_time =< Now ->
                    {true, R, NewPS} = active_new_dsgt(PS, DsgtStatus, DsgtCfgInfo, ExpireTime, Op),
                    DsgtRet = check_dress_dsgt(NewPS, R, Id),
                    DsgtRet;
                true -> %% endtime > Now
                    if IsOverLay == 0 -> %% 不能叠加、覆盖,回收
                            case get_dsgt_return_price(GoodsId) of
                                ReturnMoney = [_|_] ->
                                    {add_money, PS, ReturnMoney};
                                _ -> 
                                    {fail, ?ERRCODE(err411_dgst_can_not_overlay)}
                            end;
                        IsOverLay == 1 -> %% 覆盖
                            OldEndTime = Designation#designation.end_time,
                            NewEndTime = calc_expire_time(Now, round(ExpireTime*GoodsNum)),
                            if NewEndTime > OldEndTime ->
                                    NewDesignation = Designation#designation{active_time = Now, end_time = NewEndTime, time = Now},
                                    NewDsgtMap = maps:put(Id, NewDesignation, DsgtMap),
                                    NewDsgtStatus = DsgtStatus#dsgt_status{dsgt_map = NewDsgtMap},
                                    lib_designation_util:update_dsgt_status(RoleId, NewDesignation),
                                    lib_player_record:role_func_check_update(RoleId, ?FUNC_TYPE_DESIGNATION, {Id, NewEndTime}),
                                    handle_achievement(PS, NewDsgtMap),
                                    log_active_dsgt(Op, RoleId, Id, Now, ExpireTime*GoodsNum, NewEndTime),
                                    NewPS = PS#player_status{dsgt_status = NewDsgtStatus},
                                    DsgtRet = check_dress_dsgt(NewPS, NewDesignation, Id),
                                    DsgtRet;
                                true ->
                                    {true, PS}
                            end;
                        true -> %% 叠加
                            NewEndTime = calc_expire_time(Designation#designation.end_time, round(ExpireTime*GoodsNum)),
                            % NewEndTime = Designation#designation.end_time + DsgtCfgInfo#base_designation.expire_time * GoodsNum,
                            NewDesignation = Designation#designation{active_time = Now, end_time = NewEndTime, time = Now},
                            NewDsgtMap = maps:put(Id, NewDesignation, DsgtMap),
                            NewDsgtStatus = DsgtStatus#dsgt_status{dsgt_map = NewDsgtMap},
                            lib_designation_util:update_dsgt_status(RoleId, NewDesignation),
                            lib_player_record:role_func_check_update(RoleId, ?FUNC_TYPE_DESIGNATION, {Id, NewEndTime}),
                            handle_achievement(PS, NewDsgtMap),
                            log_active_dsgt(Op, RoleId, Id, Now, ExpireTime*GoodsNum, NewEndTime),
                            NewPS = PS#player_status{dsgt_status = NewDsgtStatus},
                            DsgtRet = check_dress_dsgt(NewPS, NewDesignation, Id),
                            DsgtRet
                    end
            end
    end.

get_dsgt_return_price(GoodsId) ->
    case data_goods_price:get_goods_price(GoodsId) of
        #goods_price{price_type = PriceType, price = Price} when Price =/= 0 ->
            [{PriceType, 0, Price}];
        _ ->
            []
    end.

check_dress_dsgt(PS, DsgtCfg, NewId) ->
    #player_status{figure = #figure{designation = OldDsgtId}} = PS,
    if OldDsgtId == NewId ->
            {true, DsgtCfg, PS};
        true ->
            case need_replace_check(OldDsgtId, NewId) of
                true ->
                    case pp_designation:handle(41102, PS, [NewId]) of
                        {ok, NewPS} ->
                            {true, DsgtCfg, NewPS};
                        _ ->
                            {true, DsgtCfg, PS}
                    end;
                false ->
                    {true, DsgtCfg, PS}
            end
    end.

%% 只有高于等于原先称号品质才考虑替换
%% 低于原先称号品质忽略
need_replace_check(NowDsgtId, NewDsgtId) ->
    RetA = data_designation:get_by_id(NowDsgtId),
    RetB = data_designation:get_by_id(NewDsgtId),
    if 
        is_record(RetB, base_designation) andalso NowDsgtId == 0 ->
            true;
        not is_record(RetA, base_designation) orelse not is_record(RetB, base_designation) ->
            false;
        true ->
            #base_designation{color = ColorA, is_trial = IsTrialA} = RetA,
            #base_designation{color = ColorB, is_trial = IsTrialB} = RetB,
            if 
                ColorA < ColorB -> %% 替换
                    true;
                ColorA == ColorB -> %% 同品质比较限时,限时优先
                    if 
                        IsTrialA == 1 andalso IsTrialB == 0 ->
                            false;
                        IsTrialA == 1 andalso IsTrialB == 1 ->
                            true;
                        IsTrialA == 0 andalso IsTrialB == 0 ->
                            true;
                        IsTrialA == 0 andalso IsTrialB == 1 ->
                            true;
                        true ->
                            false
                    end;
                true ->
                    false
            end
    end.


% %% 激活永久称号
% %% DsgtCfgInfo：需激活的称号信息#base_designation
active_permanent_dsgt(PS, DsgtCfgInfo, GoodsId, Op, _GoodsNum) ->
    #player_status{id = RoleId, dsgt_status = DsgtStatus} = PS,
    #base_designation{id = Id} = DsgtCfgInfo,
    DsgtMap = DsgtStatus#dsgt_status.dsgt_map,
    % Now = utime:unixtime(),
    case maps:get(Id, DsgtMap, none) of
        none ->
            {true, R, NewPS} = active_new_dsgt(PS, DsgtStatus, DsgtCfgInfo, 0, Op),
            #player_status{dsgt_status = NewDsgtStatus} = NewPS,
            NewDsgtMap = NewDsgtStatus#dsgt_status.dsgt_map,
            AllColorList = calc_all_dsgt(NewDsgtMap),
            {ok, NewPS1} = lib_wardrobe_active:active_desg(NewPS, AllColorList, GoodsId),
            DsgtRet = check_dress_dsgt(NewPS1, R, Id),
            DsgtRet;
        Designation = #designation{end_time = EndTime} when EndTime =/= 0 -> %% 限时->永久
            NewEndTime = 0,
            Now = utime:unixtime(),
            NewDesignation = Designation#designation{active_time = Now, end_time = NewEndTime, time = Now},
            NewDsgtMap     = maps:put(Id, NewDesignation, DsgtMap),
            NewDsgtStatus  = DsgtStatus#dsgt_status{dsgt_map = NewDsgtMap},
            lib_designation_util:update_dsgt_status(RoleId, NewDesignation),
            lib_player_record:role_func_check_delete_sub_id(RoleId, ?FUNC_TYPE_DESIGNATION, Id),
            log_active_dsgt(Op, RoleId, Id, Now, 0, 0),
            NewPS = PS#player_status{dsgt_status = NewDsgtStatus},
            %% 社团竞速称号不计算入内
            case lists:member(GoodsId, [433014,433015,433016,433017,433018,433019,433020,433021,433022]) of
                true -> NewPS1 = NewPS;
                _ ->
                    AllColorList = calc_all_dsgt(NewDsgtMap),
                    {ok, NewPS1} = lib_wardrobe_active:active_desg(NewPS, AllColorList, GoodsId)
            end,
            DsgtRet = check_dress_dsgt(NewPS1, NewDesignation, Id),
            DsgtRet;
        _ ->
            case get_dsgt_return_price(GoodsId) of
                Price = [_|_] ->
                    {add_money, PS, Price};
                _ ->
                    {fail, ?ERRCODE(err411_already_exist)}
            end
    end.

handle_achievement(Player, DsgtMap) ->
    GuildRaceDsg = [433014,433015,433016,433017,433018,433019,433020,433021,433022],
    DsgtList = maps:to_list(DsgtMap),
    SumNum = erlang:length([DsgtId||{DsgtId, _} <- DsgtList, not lists:member(DsgtId, GuildRaceDsg)]),
    lib_achievement_api:active_designation_event(Player, SumNum),
    ok.

%% 激活新称号
active_new_dsgt(PS, DsgtStatus, DsgtCfgInfo, LastTime, Op) ->
    Now = utime:unixtime(),
    #player_status{id = RoleId} = PS,
    #dsgt_status{dsgt_map = DsgtMap} = DsgtStatus,
    #base_designation{id = DsgtId} = DsgtCfgInfo,
    if LastTime =< 0 -> EndTime = 0;
        true -> EndTime = calc_expire_time(Now, LastTime)
    end,
    ?IF(EndTime =< 0, skip,
        lib_player_record:role_func_check_update(RoleId, ?FUNC_TYPE_DESIGNATION, {DsgtId, EndTime})),
    R = #designation{id = DsgtId, status = ?STATUS_USED, active_time = Now,
            end_time = EndTime, time = Now, dress_time = Now, is_replace = 1},
    lib_designation_util:update_dsgt_status(RoleId, R),
    % 更新内存
    NDsgtMap = maps:put(DsgtId, R, DsgtMap),
    AttrList = update_dsgt_attr(NDsgtMap),
    %% 更新属性
    NewDsgtStatus = DsgtStatus#dsgt_status{dsgt_map = NDsgtMap, attr = AttrList},
    NewPS = PS#player_status{dsgt_status = NewDsgtStatus},
    NewPSCount = lib_player:count_player_attribute(NewPS, ?MOD_DESIGNATION, active_dsgt),
    lib_player:send_attribute_change_notify(NewPSCount, ?NOTIFY_ATTR),
    %% 更新场景属性
    mod_scene_agent:update(NewPSCount, [{battle_attr, NewPSCount#player_status.battle_attr}]), %% {designation, DsgtId}
    handle_achievement(PS, NDsgtMap),
    %% log
    log_active_dsgt(Op, RoleId, DsgtId, Now, LastTime, EndTime),
    {true, R, NewPSCount}.

%% 玩家不在线时触发的激活称号
%% PlayerId：玩家id;Id：称号Id
active_dsgt_not_online(PlayerId, ActivePara) when PlayerId > 0 ->
    Now = utime:unixtime(),
    #active_para{goods_id = GoodsId, expire_time = ExpireTime, is_overlay = IsOverLay} = ActivePara,
    {DsgtId, _TrialTime, _IsOverLay} = lib_designation_api:get_dsgt(GoodsId),
    if DsgtId == 0 ->
            skip;
        true ->
            NewEndTime = ?IF(ExpireTime == 0, 0, calc_expire_time(Now, ExpireTime)),
            SelectRet = lib_designation_util:select_dsgt_by_id(PlayerId, DsgtId),
            OldDressDsgtIds0 = lib_designation_util:select_lastdress(PlayerId),
            OldDressDsgtIds = lists:flatten(OldDressDsgtIds0),
            R = #designation{id = DsgtId, active_time = Now, time = Now, end_time = NewEndTime, is_replace = 1},
            case SelectRet of
                [] -> %% 新称号
                    case compare_old_dress_dsgtids(DsgtId, OldDressDsgtIds) of
                        {new_acitve_replace, NeedUpdateOldDressIds} ->
                            NewR = R#designation{status = ?STATUS_USED, dress_time = Now},
                            lib_designation_util:update_dsgt_status(PlayerId, NewR),
                            update_old_dress_dsgtids(PlayerId, NeedUpdateOldDressIds);
                        {new_active_dress} ->
                            NewR = R#designation{status = ?STATUS_USED, dress_time = Now},
                            lib_designation_util:update_dsgt_status(PlayerId, NewR);
                        false ->
                            lib_designation_util:update_dsgt_status(PlayerId, R)
                    end,
                    log_active_dsgt(?OP_ACTIVE, PlayerId, DsgtId, Now, ExpireTime, NewEndTime);
                [Status, EndTime|_] ->
                    if 
                        EndTime =/= 0 andalso IsOverLay == 0 -> %% 不能叠加
                            if 
                                ExpireTime == 0 -> %% 限时->永久
                                    lib_designation_util:update_dsgt_status(PlayerId, DsgtId, Status, Now, NewEndTime, Now, 1);
                                true -> %% 未过期且不可叠加
                                    if EndTime > Now -> %% 未过期且不可叠加
                                            case get_dsgt_return_price(GoodsId) of
                                                Price = [_|_] ->
                                                    dsgt_recover(PlayerId, Price, GoodsId);
                                                _ ->
                                                    skip
                                            end;
                                        true -> %% 已过期
                                            case compare_old_dress_dsgtids(DsgtId, OldDressDsgtIds) of
                                                {new_acitve_replace, NeedUpdateOldDressIds} ->
                                                    NewR = R#designation{status = ?STATUS_USED, dress_time = Now},
                                                    lib_designation_util:update_dsgt_status(PlayerId, NewR),
                                                    update_old_dress_dsgtids(PlayerId, NeedUpdateOldDressIds);
                                                {new_active_dress} ->
                                                    NewR = R#designation{status = ?STATUS_USED, dress_time = Now},
                                                    lib_designation_util:update_dsgt_status(PlayerId, NewR);
                                                false ->
                                                    lib_designation_util:update_dsgt_active_and_end_time(PlayerId, DsgtId, Now, NewEndTime, 1)     
                                            end
                                    end
                            end,
                            log_active_dsgt(?OP_ACTIVE, PlayerId, DsgtId, Now, ExpireTime, NewEndTime);
                        EndTime =/= 0 andalso IsOverLay == 2 -> %% 叠加时间
                            if
                                ExpireTime == 0 -> %% 限时->永久
                                    NewEndTime1 = 0;
                                true -> %% 限时续时
                                    NewEndTime1 = ?IF(EndTime > Now, calc_expire_time(EndTime, ExpireTime), NewEndTime)
                            end,
                            case compare_old_dress_dsgtids(DsgtId, OldDressDsgtIds) of
                                {new_acitve_replace, NeedUpdateOldDressIds} ->
                                    NewR = R#designation{status = ?STATUS_USED, dress_time = Now, end_time = NewEndTime1},
                                    lib_designation_util:update_dsgt_status(PlayerId, NewR),
                                    update_old_dress_dsgtids(PlayerId, NeedUpdateOldDressIds);
                                {new_active_dress} ->
                                    NewR = R#designation{status = ?STATUS_USED, dress_time = Now, end_time = NewEndTime1},
                                    lib_designation_util:update_dsgt_status(PlayerId, NewR);
                                false -> %% 只修改激活过期时间
                                    lib_designation_util:update_dsgt_active_and_end_time(PlayerId, DsgtId, Now, NewEndTime1, 1)     
                            end,
                            log_active_dsgt(?OP_ACTIVE, PlayerId, DsgtId, Now, ExpireTime, NewEndTime1);                            
                        EndTime =/= 0 andalso IsOverLay == 1 -> %% 覆盖
                            if ExpireTime == 0 -> %% 限时->永久;
                                    NewEndTime1 = 0;
                                true ->
                                    NewEndTime1 = ?IF(EndTime > Now, ?IF(NewEndTime > EndTime, NewEndTime, EndTime), NewEndTime)
                            end,
                            case compare_old_dress_dsgtids(DsgtId, OldDressDsgtIds) of
                                {new_acitve_replace, NeedUpdateOldDressIds} ->
                                    NewR = R#designation{status = ?STATUS_USED, dress_time = Now, end_time = NewEndTime1},
                                    lib_designation_util:update_dsgt_status(PlayerId, NewR),
                                    update_old_dress_dsgtids(PlayerId, NeedUpdateOldDressIds);
                                {new_active_dress} ->
                                    NewR = R#designation{status = ?STATUS_USED, dress_time = Now, end_time = NewEndTime1},
                                    lib_designation_util:update_dsgt_status(PlayerId, NewR);
                                false -> %% 只修改激活过期时间
                                    lib_designation_util:update_dsgt_active_and_end_time(PlayerId, DsgtId, Now, NewEndTime1, 1)     
                            end;
                        EndTime == 0 -> %% 已是永久激活
                            case get_dsgt_return_price(GoodsId) of
                                Price = [_|_] ->
                                    dsgt_recover(PlayerId, Price, GoodsId);
                                _ ->
                                    skip
                            end;
                        true ->
                            skip
                    end
            end
    end;
active_dsgt_not_online(_, _) -> skip.


compare_old_dress_dsgtids(_DsgtId, []) ->
    {new_active_dress};
compare_old_dress_dsgtids(DsgtId, OldDressDsgtIds) ->
    NewDsgtId = compare_old_dress_dsgtids_1(DsgtId, OldDressDsgtIds),
    if NewDsgtId == DsgtId ->
            {new_acitve_replace, OldDressDsgtIds};
        true ->
            % ?ERR("~p", [{NewDsgtId, DsgtId}]),
            false
    end.

compare_old_dress_dsgtids_1(DsgtId, []) ->
    DsgtId;
compare_old_dress_dsgtids_1(ChooseDsgtId, [DsgtId|Rest]) ->
    case need_replace_check(ChooseDsgtId, DsgtId) of
        true ->
            compare_old_dress_dsgtids_1(DsgtId, Rest);
        false ->
            compare_old_dress_dsgtids_1(ChooseDsgtId, Rest)
    end.

update_old_dress_dsgtids(PlayerId, UpdateOldDressIds) ->
    lists:map(fun(DressId) -> 
        db:execute(?SQL_DSGT_UPDATE_STATUS, [?STATUS_UNUSED, PlayerId, DressId])
    end, UpdateOldDressIds).

%% ================================= 清理称号 =================================
%% 称号结束时效被调用
cancel_dsgt(PS, DsgtId) ->
    Now = utime:unixtime(),
    #player_status{id = PlayerId, dsgt_status = DsgtStatus} = PS,
    #dsgt_status{dsgt_map = DsgtMap} = DsgtStatus,
    case maps:get(DsgtId, DsgtMap, none) of
        #designation{end_time = EndTime} = Designation when EndTime < Now+5 ->
            log_cancel_dsgt(2, PlayerId, DsgtId, Now, 0),
            cancel_dsgt_do(PS, Designation, 1);
        _ ->
            {ok, PS}
    end.

%% 移除称号，不管称号有没有过期
remove_dsgt(PS, DsgtId) ->
    Now = utime:unixtime(),
    #player_status{id = PlayerId, dsgt_status = DsgtStatus} = PS,
    #dsgt_status{dsgt_map = DsgtMap} = DsgtStatus,
    case maps:get(DsgtId, DsgtMap, none) of
        #designation{} = Designation ->
            log_active_dsgt(?OP_REMOVE, PlayerId, DsgtId, Now, 0, 0),
            cancel_dsgt_do(PS, Designation, 1);
        _ ->
            {ok, PS}
    end.

%% 取消配置id的称号：
%% 1：其他玩家激活全局称号时，取消旧玩家的称号
%% 2：玩家激活称号时，先取消玩家身上相斥的称号，如 （战士第一，战士第二）属于相斥称号
cancel_dsgt_by_id(PS, #active_para{id=Id}) ->
    #player_status{id = PlayerId, dsgt_status = DsgtStatus} = PS,
    #dsgt_status{dsgt_map = DsgtMap} = DsgtStatus,
    case maps:get(Id, DsgtMap, none) of
        none -> {ok, PS};
        #designation{end_time = EndTime} = Designation ->
             ?IF(EndTime =< 0, ok,
                 lib_player_record:role_func_check_delete_sub_id(PlayerId, ?FUNC_TYPE_DESIGNATION, Id)),
            lib_designation:log_active_dsgt(3, PlayerId, Id, utime:unixtime(), 0, 0),
            cancel_dsgt_do(PS, Designation, 0)
    end.

%% 删除称号
cancel_dsgt_do(PS, Designation, IsReplace) ->
    #player_status{id = PlayerId, sid = Sid, dsgt_status = DsgtStatus, figure = Figure,
                   scene = SceneId, scene_pool_id = SPId, copy_id = CopyId, x = X, y = Y} = PS,
    #designation{id = DsgtId} = Designation,
    #dsgt_status{dsgt_map = DsgtMap} = DsgtStatus,
    Now = utime:unixtime(),
    %% 移除数据库和map的数据
    case IsReplace of
        0  ->
            R = Designation#designation{id = DsgtId,status = ?STATUS_UNUSED, is_replace = IsReplace};
        1 ->
            R = Designation#designation{id = DsgtId,status = ?STATUS_UNUSED}
    end,
    %% 社团竞速称号特殊 - 直接删除数据库中数据
    case data_designation:get_by_id(DsgtId) of
        #base_designation{force_cancel = CanCancel} when CanCancel == 1 ->
            Sql = io_lib:format(?SQL_DSGT_DELETE, [PlayerId, DsgtId]),
            db:execute(Sql);
        _ ->
            lib_designation_util:update_dsgt_status(PlayerId, R)
    end,
    NewDsgtMap = maps:remove(DsgtId, DsgtMap),
    % 统计新属性
    AttrList = update_dsgt_attr(NewDsgtMap),
    NewDsgtStatus = DsgtStatus#dsgt_status{dsgt_map = NewDsgtMap, attr = AttrList},
    case DsgtId == Figure#figure.designation of
        true -> %% 更新场景显示
            #designation{id = LastDress} = D = get_last_dsgt(NewDsgtMap, Now),
            mod_scene_agent:update(PS, [{designation, LastDress}]),
            lib_role:update_role_show(PlayerId, [{designation, LastDress}]),
            {ok, BinData} = pt_411:write(41105, [PlayerId, LastDress]),
            lib_server_send:send_to_area_scene(SceneId, SPId, CopyId,  X, Y, BinData),
            NewFigure =  Figure#figure{designation = LastDress},
            case LastDress of
                0 ->
                    NewPS = PS#player_status{figure = NewFigure, dsgt_status = NewDsgtStatus};
                _ ->
                    NewDsgtMap1 = maps:put(LastDress, D#designation{status = ?STATUS_USED, dress_time = Now}, NewDsgtMap),
                    lib_designation_util:update_dsgt_status(PlayerId, D#designation{status = ?STATUS_USED, dress_time = Now}),
                    NewPS = PS#player_status{figure = NewFigure, dsgt_status = NewDsgtStatus#dsgt_status{dsgt_map = NewDsgtMap1}}
            end;
        false ->
            {ok, BinData} = pt_411:write(41105, [PlayerId, 0]),
            lib_server_send:send_to_sid(Sid, BinData),
            NewPS = PS#player_status{dsgt_status = NewDsgtStatus}
    end,
    
    {List, _} = get_player_dsgt(NewPS#player_status.dsgt_status),
    {ok, BinData1} = pt_411:write(41101, [NewPS#player_status.figure#figure.designation, List]),
    lib_server_send:send_to_sid(Sid, BinData1),

    %% 更新属性
    NewPSCount = lib_player:count_player_attribute(NewPS),
    mod_scene_agent:update(NewPSCount, [{battle_attr, NewPSCount#player_status.battle_attr}]),
    lib_player:send_attribute_change_notify(NewPSCount, ?NOTIFY_ATTR),
    {ok, NewPSCount}.

get_last_dsgt(DsgtMap, Now) ->
    case maps:to_list(DsgtMap) of
        DsgtL = [_|_] ->
            DsgtL1 = [Designation || {_, #designation{dress_time = DressTime, is_replace = IsReplace, end_time = EndTime} = Designation} <- DsgtL,
                IsReplace == 1 andalso DressTime > 0 andalso (Now < EndTime orelse EndTime == 0)],
            case DsgtL1 of
                [_|_] ->
                    [Designation1|_] = lists:reverse(lists:keysort(#designation.dress_time, DsgtL1)),
                    Designation1;
                _ ->
                    #designation{}
            end;
        _ ->
            #designation{}
    end.

get_best_dsgt(DsgtMap, Now) ->
    case maps:to_list(DsgtMap) of
        DsgtL = [_|_] ->
            DsgtL1 = [Designation || {_, #designation{dress_time = DressTime, is_replace = IsReplace, end_time = EndTime} = Designation} <- DsgtL,
                IsReplace == 1 andalso DressTime > 0 andalso (Now < EndTime orelse EndTime == 0)],
            case DsgtL1 of
                [_|_] ->
                    [DsgtInfo|Rest] = DsgtL1,
                    lists:foldl(fun(NewDsgt = #designation{id = DsgtId}, TempChooseDsgt) ->
                        #designation{id = ChooseDsgtId} = TempChooseDsgt,
                        case need_replace_check(ChooseDsgtId, DsgtId) of
                            true ->
                                NewDsgt;
                            _ ->
                                TempChooseDsgt
                        end
                    end, DsgtInfo, Rest);
                _ -> #designation{}
            end;
        _ -> #designation{}
    end.

%% 是否第一条称号
is_first_dsgt(PlayerId) ->
    case lib_designation_util:get_actived_dgst(PlayerId) of
        [_|_] ->
            false;
        [] ->
            true
    end.

calc_power(Status, _AttrPower, _BaseAttr) ->
    #player_status{dsgt_status = DsgtStatus} = Status,
    #dsgt_status{dsgt_map = DsgtMap} = DsgtStatus,
    AttrL = update_dsgt_attr(DsgtMap, expire_time),
    PartialAttr = attr_conv:to_attr_record(AttrL),
    Power = lib_player_attr:calc_attr_power(PartialAttr),
    NewDsgtStatus = DsgtStatus#dsgt_status{power = Power},
    NewStatus = Status#player_status{dsgt_status = NewDsgtStatus},
    lib_common_rank_api:refresh_rank_by_dsgt(NewStatus),
    {NewStatus, 0}.

calc_expire_time(StartTime, ExpireTime) ->
    EndTime = StartTime + ExpireTime,
    Rest = EndTime rem 60,
    if Rest == 0 ->
            EndTime;
        true ->
            EndTime + (60 - Rest)
    end.

%% 所有已激活称号数量,排除限时称号
get_all_actived_dsgt(Player) ->
    #player_status{dsgt_status = DsgtStatus} = Player,
    #dsgt_status{dsgt_map = DsgtMap} = DsgtStatus,
    case maps:to_list(DsgtMap) of
        DsgtList = [_|_] ->
            lists:foldl(fun({_K, #designation{end_time = EndTime}}, Acc) ->
                if EndTime =/= 0 -> Acc;
                    true -> Acc+1
                end
             end, 0, DsgtList);
        _ ->
            0
    end.

%% 统计所有激活称号品质
calc_all_dsgt(DsgtMap) ->
    F = fun(DsgId, #designation{end_time = EndTime}, TList) when EndTime =< 0 ->
        case lists:member(DsgId, [433014,433015,433016,433017,433018,433019,433020,433021,433022]) of
            true -> TList;
            _ ->
                case data_goods_type:get(DsgId) 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});
                        _ -> lists:keystore({1, Color}, 1, TList, {{1, Color}, 1})
                    end;
                _ -> TList
            end end;
        (_, _, TList) -> TList
    end,
    AllColor = maps:fold(F, [], DsgtMap),
    AllColor.

%% ================================= 日志 =================================
log_delete_designation_list(PlayerId, DeleteLog) ->
    F = fun({Id, Time}) ->
                log_active_dsgt(2, PlayerId, Id, Time, 0, 0)
        end,
    lists:foreach(F, DeleteLog).

%% Op：1 激活，2 取消
%% OpNow：激活和取消的时间戳
%% EndTime：称号的结束时间戳，只有激活操作有值，取消操作值为0
log_active_dsgt(Op, PlayerId, Id, OpNow, ExpireTime, EndTime) ->
    case data_designation:get_by_id(Id) of
        BaseDesignatioon = #base_designation{} ->
            #base_designation{name = Name} = BaseDesignatioon,
            %% 称号移除邮件
            if
                Op =:= 3->
                   lib_mail_api:send_sys_mail([PlayerId], ?MOD_DESIGNATION, utext:get(4110003), utext:get(4110004, [Name]), []);
                true ->
                   skip
            end,
            lib_log_api:log_active_dsgt(PlayerId, Id, Op, OpNow, ExpireTime, EndTime, 0);
        _ ->
            skip
    end.

%称号过期通知
log_cancel_dsgt(Op,PlayerId, Id, OpNow, EndTime) ->
    case data_designation:get_by_id(Id) of
        #base_designation{} ->
            lib_log_api:log_active_dsgt(PlayerId, Id, Op, OpNow, 0, EndTime, 0);
        _ ->
            skip
    end.

%% ================================= private fun =================================
%% 将获取到的称号列表装换为客户端需要的格式
change_to_client_format([], _NowTime, ReplyList, ExpireList) ->
    {ReplyList, ExpireList};
change_to_client_format([{DsgtId, DR}|T], NowTime, ReplyList, ExpireList) ->
    #designation{end_time = EndTime} = DR,
    if
        EndTime == 0 orelse EndTime > NowTime->
            change_to_client_format(T, NowTime, [{DsgtId, EndTime}|ReplyList], ExpireList);
        true ->
            change_to_client_format(T, NowTime, ReplyList, [DsgtId|ExpireList])
    end.

%% 统计所有称号属性
update_dsgt_attr(DstMap) ->
    update_dsgt_attr(DstMap, all).

update_dsgt_attr(DstMap, CalcType) ->
    List = maps:to_list(DstMap),
    Fun = fun({_KId, DR}, TAttrList) ->
      case data_designation:get_by_id(DR#designation.id) of
          #base_designation{attr_list = DefAttr} ->
            if CalcType == all ->
                    ulists:kv_list_plus_extra([DefAttr, TAttrList]);
                true ->
                    if DR#designation.end_time > 0 ->
                            TAttrList;
                        true ->
                            ulists:kv_list_plus_extra([DefAttr, TAttrList])
                    end
            end;
          _ ->
            TAttrList
      end
    end,
    lists:foldl(Fun, [], List).


%% gm
active_all_dsgt(Player) ->
    #player_status{id = RoleId} = Player,
    AllDsgts = data_designation:get_all_dsgt(),
    lists:map(fun(Dsgt) -> 
        case data_designation:get_by_id(Dsgt) of
            #base_designation{main_type = Type, goods_id = GoodsId} when (Type == 1 andalso GoodsId =/= 0) orelse Type == 3 ->
                spawn( fun() -> 
                    timer:sleep(urand:rand(100, 200)),
                    if Type == 1 -> 
                            lib_designation_api:active_dsgt_common(RoleId, GoodsId);
                        Type == 3 ->
                            lib_designation_api:active_dsgt_common(RoleId, Dsgt);
                        true ->
                            skip
                    end
                end);
            _ ->
                skip
        end
    end, AllDsgts),
    Player.