%%%-------------------------------------------------------------------
%%% @doc 境界学位
%%% Module: lib_degree.erl
%%% Created : 2019-11-25
%%% @Author: tyl
%%%-------------------------------------------------------------------
-module(lib_degree).

-include("common.hrl").
-include("server.hrl").
-include("degree.hrl").
-include("figure.hrl").
-include("predefine.hrl").
-include("errcode.hrl").
-include("def_module.hrl").
-include("scene.hrl").
-include("rec_event.hrl").
-include("def_event.hrl").

-compile(export_all).

-export([
	login/1,
	handle_event/2,                 %% 升级自动开启学位系统
	get_degree_state/1,             %% 获取学位状态信息
	get_daily_classwork_state/1,    %% 获取日常功课状态信息
	daily_timer/1,                  %% 日常功课4点定时清理
	upgrade_degree/1,               %% 学位晋升
	receive_classwork_reward/2      %% 领取日常功课奖励
]).

%% 升级自动激活学位系统
handle_event(Player, #event_callback{type_id = ?EVENT_LV_UP}) when is_record(Player, player_status) ->
	#player_status{figure = Figure, degree = Degree} = Player,
	case Figure#figure.lv == ?DegreeOpenLv andalso Degree#degree.degree_id == 0 of
		true ->
			NewPlayer = init_degree(Player),
			{ok, NewPlayer};
		_ -> {ok, Player}
	end.

make_record(degree, Args) ->
	[DegreeId, DegreeExp, DunState] = Args,
	MaxDegreeExp = get_degree_upgrade_exp(DegreeId+1) * ?ExpRatio,
	case MaxDegreeExp of
		0 -> TMaxDegreeExp = get_degree_upgrade_exp(DegreeId) * ?ExpRatio;
		_ -> TMaxDegreeExp = MaxDegreeExp
	end,
	AttrList = count_attr_list(DegreeId),
	#degree{
		degree_id       = DegreeId,
		degree_exp      = min(DegreeExp, TMaxDegreeExp),
		degree_max_exp  = TMaxDegreeExp,
		dun_state       = util:bitstring_to_term(DunState),
		attr_list       = AttrList
		,power          = calc_degree_power(AttrList)
	};
make_record(degree_classwork, Args) ->
	F = fun([ClassId, Content, _Time], DailyClass) ->
		#daily_class{
			class_status_map   = ClassStatusMap,
			class_progress_map = ClassProgressMap
		} = DailyClass,
		[ClassType, ClassWorkId, Num, State] = util:bitstring_to_term(Content),
		case data_degree:get_degree_timetable_cfg(ClassId) of
			#degree_timetable_cfg{target = [{_Type, _Id, NeedNum}]} -> NeedNum;
			_ -> NeedNum = 99 %% 没有配置直接赋值99
		end,
		if
			State >= ?ClassUnReceive -> NewState = State;
			Num >= NeedNum           -> NewState = ?ClassUnReceive;  %% 如果功课已经完成则State改变
			true                     -> NewState = ?ClassUnFinish
		end,
		NewClassStatusMap   = ClassStatusMap#{ClassId => NewState},
		NewClassProgressMap = ClassProgressMap#{ClassId => [ClassType, ClassWorkId, Num]},
		DailyClass#daily_class{
			class_status_map   = NewClassStatusMap,
			class_progress_map = NewClassProgressMap
		}
	end,
	lists:foldl(F, #daily_class{}, Args);
make_record(_, _Args) ->
	?ERR("<<Error make record>>").

%% API
login(Player) ->
	#player_status{id = RoleId} = Player,
	PlayerDegree    = load_degree(RoleId),
	DailyClassWorks = load_classwork(RoleId),
	NewClassWorks   =
	case length(maps:keys(
		DailyClassWorks#daily_class.class_progress_map)
	) of
		?RandClassWorkCount -> DailyClassWorks;
		_ ->
			fix_daily_classwork(
				RoleId, DailyClassWorks, PlayerDegree#degree.degree_id)
	end,
	NewPlayerDegree  = PlayerDegree#degree{daily_class = NewClassWorks},
	NewPlayerDegree1 =
		fix_dun_state(RoleId, NewPlayerDegree, NewPlayerDegree#degree.degree_id),
	NewPlayerDegree2 = count_degree_attribute(NewPlayerDegree1),
	NewPlayer = Player#player_status{degree = NewPlayerDegree2},
	#daily_class{class_progress_map = DailyProgressMap} = NewClassWorks,
	case maps:keys(DailyProgressMap) of
		[] ->
			{ok, RefreshPlayer} = refresh_daily_classwork(NewPlayer),
			RefreshPlayer;
		_ -> NewPlayer
	end.

load_degree(RoleId) ->
	Sql = io_lib:format(?SQL_SELECT_PLAYER_DEGREE, [RoleId]),
	case db:get_row(Sql) of
		[] ->   %% 未激活/刚激活学位系统
			#degree{};
		DegreeList ->
			make_record(degree, DegreeList)
	end.

load_classwork(RoleId) ->
	Sql = io_lib:format(?SQL_SELECT_PLAYER_CLASSWORK, [RoleId]),
	case db:get_all(Sql) of
		[] ->   %% 未激活/刚激活学位系统、日清
			#daily_class{};
		DegreeList ->
			make_record(degree_classwork, DegreeList)
	end.

%% 获取学位状态信息
get_degree_state(Player) ->
	#player_status{id = RoleId} = Player,
	case get_dict_degree(Player) of
		#degree{degree_id = 0} ->   %% 学位系统刚开启/内存数据没加载
			case get_db_degree(RoleId) of
				[] ->
					NewPlayer = init_degree(Player),
					NewPlayer;
				DegreeList ->
					Degree = make_record(degree, DegreeList),
					Player#player_status{degree = Degree}
			end;
		_DegreeStatus -> Player
	end.

%% 获取每日功课状态信息
get_daily_classwork_state(Player) ->
	#player_status{id = RoleId, degree = Degree} = Player,
	#degree{degree_id = DegreeId} = Degree,
	DailyClassWorks = get_dict_degree_classwork(Player),
	ClassStatusMap = DailyClassWorks#daily_class.class_status_map,
	case length(maps:keys(ClassStatusMap)) of
		0 ->  %% 学位系统刚开启/内存数据没加载
			case get_db_degree_classwork(RoleId) of
				[] ->
					%% 随机日常任务
					case DegreeId of
						0 -> RandClassIds = get_rand_classwork_list();
						_ -> RandClassIds = get_rand_classwork_list(?RandClassWorkCount, DegreeId)
					end,
					NewDailyClassWorks = trans_to_daily_class_record(RandClassIds),
					batch_refresh_db_classwork(RoleId, NewDailyClassWorks),
					NewDailyClassWorks;
				ClassWorksList ->
					case length(ClassWorksList) =/= ?RandClassWorkCount of
						true ->
							DailyClassWorksR = make_record(degree_classwork, ClassWorksList),
							NewDailyClassWorks = fix_daily_classwork(RoleId, DailyClassWorksR, DegreeId), %% 修正日常功课,不足3个补足
							NewDailyClassWorks;
						_ ->
							make_record(degree_classwork, ClassWorksList)
					end
			end;
		_ ->
			case length(maps:keys(ClassStatusMap)) =/= ?RandClassWorkCount of
				true ->
					NewDailyClassWorks = fix_daily_classwork(RoleId, DailyClassWorks, DegreeId),
					NewDailyClassWorks;
				_ -> DailyClassWorks
			end
	end.

%% 4点刷新功课状态信息
daily_timer(_DelaySec) ->
	util:rand_time_to_delay(1000, ?MODULE, refresh_midnight, []).

refresh_midnight() ->
	Sql = io_lib:format(?SQL_DELETE_PLAYER_CLASSWORK, []),
	db:execute(Sql),
	Pids = lib_player:get_online_pids(),
	[refresh_daily_classwork_state(Pid) || Pid <- Pids].

refresh_daily_classwork_state(RoleId) ->
	lib_player:apply_cast(RoleId, ?APPLY_CAST_SAVE, ?MODULE, refresh_daily_classwork, []).

%% 玩家进程操作
refresh_daily_classwork(Player) ->
	#player_status{figure = Figure, id = RoleId} = Player,
	#figure{lv = Lv} = Figure,
	case Lv < ?DegreeOpenLv of
		true -> {ok, Player};   %% 未达到等级不刷新
		_ ->
			#degree{degree_id = DegreeId} = Player#player_status.degree,
			RandClassWorksList = get_rand_classwork_list(?RandClassWorkCount, DegreeId) ,
			DailyClassWorks = trans_to_daily_class_record(RandClassWorksList),
			NewPlayer = save_dict_degree_classwork(Player, DailyClassWorks),
			{ok, Bin} = pt_166:write(16605, [1]),
			lib_server_send:send_to_sid(Player#player_status.sid, Bin),
			%% 存库
			batch_refresh_db_classwork(RoleId, DailyClassWorks),
			{ok, NewPlayer}
	end.

%% 学位晋升
upgrade_degree(Player) ->
	#player_status{degree = Degree, id = RoleId, figure = Figure} = Player,
	#degree{degree_id = NowDegreeId, degree_exp = NowExp,
			degree_max_exp = _MaxExp, dun_state = DunState} = Degree,
	CheckList = [
		{need_exp, NowExp}, {finish_dun, DunState, NowDegreeId}, {collect_material, NowDegreeId}
	],
	case lib_degree_check:check_upgrade(CheckList, Player) of
		{true, _Player1} ->
			case do_upgrade_degree(Player) of
				{true, NewPlayer} ->
					%% 重新计算属性
					#player_status{degree = NewDegree} = NewPlayer,
					RefreshPlayer = refresh_attr(NewPlayer, upgrade_degree),
					mod_scene_agent:update(RefreshPlayer, [{battle_attr, RefreshPlayer#player_status.battle_attr}]),
					%% 传闻
					#degree_cfg{degree_name = DegreeName, is_tv = IsTV} = data_degree:get_degree_cfg(NewDegree#degree.degree_id),
					IsTV == 1 andalso lib_chat:send_TV({all}, ?MOD_DEGREE, 1, [Figure#figure.name, RoleId, DegreeName]),
					%% 日志
					lib_log_api:log_degree_upgrade(
						RoleId, NewDegree#degree.degree_id, NewDegree#degree.degree_exp, NewDegree#degree.dun_state
					),
					{true, RefreshPlayer};
				{false, Err, _NewPlayer} ->
					{false, Err, Player}
			end;
		{false, Res} ->
			{false, Res, Player}
	end.

%% 领取功课奖励
receive_classwork_reward(Player, ClassId) ->
	#player_status{degree = Degree, id = RoleId} = Player,
	#degree{daily_class = DailyClasses} = Degree,
	#daily_class{
		class_status_map   = ClassStatusMap,
		class_progress_map = ClassProgressMap
	} = DailyClasses,
	CheckList = [
		{has_receive, ClassId, ClassStatusMap},
		{finish_classwork, ClassId, ClassProgressMap}
	],
	case lib_degree_check:check_receive(CheckList, Player) of
		{true, _Player1} ->
			case do_receive_reward(RoleId, ClassId, Degree) of
				{true, NewDegree}        -> {true, NewDegree};
				{false, Error, _TDegree} -> {false, Error, Degree}
			end;
		{false, Res} -> {false, Res, Degree}
	end.

%% ------------------------ builtin function ---------------------------
%% 开启系统初始化
init_degree(Player) ->
	#player_status{id = RoleId} = Player,
	InitialDegreeId = 1,
	case get_degree_upgrade_exp(InitialDegreeId+1) of
		0 -> DegreeMaxExp = get_degree_upgrade_exp(InitialDegreeId) * ?ExpRatio;
		Exp when is_integer(Exp) -> DegreeMaxExp = Exp * ?ExpRatio;
		_ -> DegreeMaxExp = get_degree_upgrade_exp(InitialDegreeId) * ?ExpRatio
	end,
	%% 随机日常任务
	RandClassIds = get_rand_classwork_list(),
	DailyClassWorks = trans_to_daily_class_record(RandClassIds),
	%% 计算当前学位属性列表
	AttrList = count_attr_list(InitialDegreeId),
	DunState = [{get_dun_id(InitialDegreeId+1), 0}],
	Degree =
		#degree{degree_id   = InitialDegreeId,
			degree_max_exp  = DegreeMaxExp,
			daily_class     = DailyClassWorks,
			attr_list       = AttrList,
			dun_state       = DunState
			,power          = calc_degree_power(AttrList)
		},
	save_db_degree(RoleId, InitialDegreeId, 0, util:term_to_string(DunState)),
	batch_refresh_db_classwork(RoleId, DailyClassWorks),
	RefreshPlayer = refresh_attr(Player#player_status{degree = Degree}, upgrade_degree),
	mod_scene_agent:update(RefreshPlayer, [{battle_attr, RefreshPlayer#player_status.battle_attr}]),
	RefreshPlayer.

%% 补全日常功课
fix_daily_classwork(_RoleId, DailyClass, 0) -> DailyClass;
fix_daily_classwork(RoleId, DailyClass, DegreeId) ->
	#daily_class{
		class_progress_map = ClassProgressMap,
		class_status_map   = ClassStatusMap
	} = DailyClass,
	%% 删除与等级不匹配和冗余的功课
	{FClassStatusMap, FClassProgressMap} = filter_daily_classwork(
		DegreeId, ClassStatusMap, ClassProgressMap
	),
	NewDailyClass = DailyClass#daily_class{
		class_status_map   = FClassStatusMap,
		class_progress_map = FClassProgressMap
	},
	OldLength = length(maps:keys(FClassProgressMap)),
	OldClassIdList = maps:keys(FClassProgressMap),
	do_fix_daily_classwork(RoleId, DegreeId, NewDailyClass, OldClassIdList, OldLength).

do_fix_daily_classwork(RoleId, _DegreeId, DailyClass, ClassIdList, ?RandClassWorkCount) ->
	#daily_class{
		class_progress_map = ClassProgressMap,
		class_status_map   = ClassStatusMap
	} = DailyClass,
	OldClassIdList = maps:keys(ClassProgressMap),
	FilterList = [ClassId || ClassId <- ClassIdList, not lists:member(ClassId, OldClassIdList)],
	F = fun(TClassId, {TClassProgressMap, TClassStatusMap}) ->
		#degree_timetable_cfg{target = [{Type, Id, _Num}]} = data_degree:get_degree_timetable_cfg(TClassId),
		ClassStatusMap1   = maps:put(TClassId, ?ClassUnFinish, TClassStatusMap),
		ClassProgressMap1 = maps:put(TClassId, [Type, Id, 0], TClassProgressMap),
		{ClassProgressMap1, ClassStatusMap1}
		end,
	{NewClassProgressMap, NewClassStatusMap} = lists:foldl(F, {ClassProgressMap, ClassStatusMap}, FilterList),
	MewDailyClass = DailyClass#daily_class{
		class_progress_map = NewClassProgressMap,
		class_status_map   = NewClassStatusMap
	},
	%% 存库
	spawn(fun() -> batch_refresh_db_classwork(RoleId, MewDailyClass) end),
	MewDailyClass;
do_fix_daily_classwork(RoleId, DegreeId, DailyClass, OldClassIdList, OldLength) ->
	RandClassIds = get_rand_classwork_list(?RandClassWorkCount - OldLength, DegreeId),
	FilterClassIds = [ClassId || ClassId <- RandClassIds, not lists:member(ClassId, OldClassIdList)],
	NewClassIdList = OldClassIdList ++ FilterClassIds,
	do_fix_daily_classwork(RoleId, DegreeId, DailyClass, NewClassIdList, length(NewClassIdList)).

filter_daily_classwork(DegreeId, ClassStatusMap, ClassProgressMap) ->
	F = fun(ClassId, _State, {TClassStatusMap, TClassProgressMap}) ->
		case data_degree:get_degree_timetable_cfg(ClassId) of
			#degree_timetable_cfg{need_degree = NeedDegreeId} ->
				if
					NeedDegreeId > DegreeId  ->
						TClassStatusMap1   = maps:remove(ClassId, TClassStatusMap),
						TClassProgressMap1 = maps:remove(ClassId, TClassProgressMap),
						{TClassStatusMap1, TClassProgressMap1};
					true -> {TClassStatusMap, TClassProgressMap}
				end;
			_ ->
				TClassStatusMap1   = maps:remove(ClassId, TClassStatusMap),
				TClassProgressMap1 = maps:remove(ClassId, TClassProgressMap),
				{TClassStatusMap1, TClassProgressMap1}
		end
	    end,
	{FClassStatusMap, FClassProgressMap} =
		maps:fold(F, {ClassStatusMap, ClassProgressMap}, ClassStatusMap),
	ClassWorkIds = maps:keys(FClassStatusMap),
	{LClassStatusMap, LClassProgressMap} =
		remove_redundant_classwork(ClassWorkIds, FClassProgressMap, FClassStatusMap),
	{LClassStatusMap, LClassProgressMap}.

%% 去除冗余的功课(某些极特殊情况下当日功课多于3门)
remove_redundant_classwork(ClassWorkIds, ClassProgressMap, ClassStatusMap) ->
	case length(ClassWorkIds) > ?RandClassWorkCount of
		true ->
			LeftClassWorkIds = lists:sublist(ClassWorkIds, ?RandClassWorkCount),
			RemoveClassIdList = [ClassId || ClassId <- ClassWorkIds, not lists:member(ClassId, LeftClassWorkIds)],
			F = fun(TRemoveId, {TClassProgressMap, TClassStatusMap}) ->
				TClassProgressMap1 = maps:remove(TRemoveId, TClassProgressMap),
				TClassStatusMap1   = maps:remove(TRemoveId, TClassStatusMap),
				{TClassProgressMap1, TClassStatusMap1}
			end,
			{ClassProgressMap1, ClassStatusMap1} =
				lists:foldl(F, {ClassProgressMap, ClassStatusMap}, RemoveClassIdList);
		_ ->
			ClassProgressMap1 = ClassProgressMap,
			ClassStatusMap1   = ClassStatusMap
	end,
	{ClassStatusMap1, ClassProgressMap1}.

%% 学位等级配置增加后修正副本数据
fix_dun_state(_RoleId, Degree, 0) -> Degree;
fix_dun_state(RoleId, Degree, DegreeId) ->
	#degree{dun_state = DunState} = Degree,
	case DunState == [{0, 0}] orelse DunState == []  of   %% 修正非正常数据
		true ->
			NDunId = get_dun_id(DegreeId + 1),
			case NDunId of
				0 -> NewDunState = [{get_dun_id(DegreeId), 0}];
				_ -> NewDunState = [{NDunId, 0}]
			end;
		_ ->
			NDunId = get_dun_id(DegreeId + 1),
			[{DunId, _State}] = DunState,
			if
				DunId == NDunId -> NewDunState = DunState;
				DunId =/= NDunId andalso NDunId =/= 0 ->   %% 配置增加后,副本Id和状态改变
					NewDunState = [{NDunId, 0}];
				true -> NewDunState = DunState
			end
	end,
	NewDegree = Degree#degree{dun_state = NewDunState},
	NewDunState /= DunState andalso
		update_db_degree_dun_state(RoleId, util:term_to_string(NewDunState)),
	NewDegree.

%% 获取随机的日常功课(系统开启调用)
get_rand_classwork_list() ->
	ClassworkList = data_degree:get_all_classwork(),
	Filter = fun(ClassWorkId, TList) ->
		#degree_timetable_cfg{need_degree = NeedDegree} = data_degree:get_degree_timetable_cfg(ClassWorkId),
		if
			NeedDegree == 1 -> [ClassWorkId|TList];
			true -> TList
		end
	end,
	FilterList = lists:foldl(Filter, [], ClassworkList),
	case length(FilterList) >= ?RandClassWorkCount of
		true -> RandList = urand:get_rand_list(?RandClassWorkCount, FilterList);
		_ -> RandList = FilterList
	end,
	RandList.

%% 获取随机的日常功课(每日刷新调用)
get_rand_classwork_list(Num, DegreeId) ->
	ClassworkList = data_degree:get_all_classwork(),
	Filter = fun(ClassWorkId, TList) ->
		#degree_timetable_cfg{need_degree = NeedDegree} = data_degree:get_degree_timetable_cfg(ClassWorkId),
		if
			DegreeId >= NeedDegree -> [ClassWorkId|TList];
			true -> TList
		end
	end,
	FilterList = lists:foldl(Filter, [], ClassworkList),
	RandList = urand:get_rand_list(Num, FilterList),
	RandList.

%% 获取当前学位等级晋升需要经验值
get_degree_upgrade_exp(DegreeId) ->
	case data_degree:get_degree_cfg(DegreeId) of
		[] -> 0;  %% 满级了
		#degree_cfg{} = DegreeCfg -> DegreeCfg#degree_cfg.need_exp;
		_ -> 0
	end.

%% 获取当前学位等级晋升需要通过的副本Id
get_dun_id(DegreeId) ->
	case data_degree:get_degree_cfg(DegreeId) of
		[] -> 0;  %% 满级了
		#degree_cfg{} = DegreeCfg -> DegreeCfg#degree_cfg.finish_dungeon;
		_ -> 0
	end.

%% 批量更新数据库中日常功课数据
batch_refresh_db_classwork(RoleId, DailyClassWorks) ->
	#daily_class{
		class_status_map   = ClassStatusMap,
		class_progress_map = ClassProgressMap
	} = DailyClassWorks,
	F = fun(ClassId, State, Ack) ->
		Progress = maps:get(ClassId, ClassProgressMap, []),
		Content = lib_degree_task:make_content(Progress, State),
		save_db_degree_classwork(RoleId, ClassId, Content),
		Ack
	end,
	maps:fold(F, true, ClassStatusMap).

trans_to_daily_class_record(ClassWorkList) ->
	F = fun(ClassWorkId, [ClassStatusMap, ClassProgressMap]) ->
		#degree_timetable_cfg{target = [{Type, Id, _Num}]} = data_degree:get_degree_timetable_cfg(ClassWorkId),
		ClassStatusMap1   = ClassStatusMap#{ClassWorkId => ?ClassUnFinish},
		ClassProgressMap1 = ClassProgressMap#{ClassWorkId => [Type, Id, 0]},
		[ClassStatusMap1, ClassProgressMap1]
	end,
	[NewClassStatusMap, NewClassProgressMap] = lists:foldl(F, [#{}, #{}], ClassWorkList),
	#daily_class{class_status_map = NewClassStatusMap, class_progress_map = NewClassProgressMap}.

%% --------------------------------- 属性相关 -----------------------------
%% 计算当前学位等级属性列表
count_degree_attribute(Degree) ->
	#degree{degree_id = DegreeId} = Degree,
	AttrList = count_attr_list(DegreeId),
	Degree#degree{attr_list = AttrList}.

%% 计算属性列表
count_attr_list(DegreeId) ->
	case data_degree:get_degree_cfg(DegreeId) of
		[] -> [];
		#degree_cfg{attr_list = AttrList} ->
			NewAttrList = lib_player_attr:partial_attr_convert(AttrList),
			NewAttrList
	end.

%% 计算战力
calc_degree_power(AttrList) ->
	AttrR = attr_conv:to_attr_record(AttrList),
	Power = lib_player_attr:calc_attr_power(AttrR),
	Power.

%% 刷新战力
refresh_attr(Player, Type) ->
	#player_status{degree = Degree} = Player,
	NewDegree = count_degree_attribute(Degree),
	NewPlayer = save_dict_degree(Player, NewDegree),
	RefreshPlayer = lib_player:count_player_attribute(NewPlayer, ?MOD_DEGREE, Type),
	lib_player:send_attribute_change_notify(RefreshPlayer, ?NOTIFY_ATTR),
	RefreshPlayer.
%% -------------------------------------------------------------------------------------

do_upgrade_degree(Player) ->
	#player_status{degree = Degree} = Player,
	#degree{degree_id = DegreeId, degree_exp = NowExp} = Degree,
	NewDegreeId = DegreeId + 1,
	case data_degree:get_degree_cfg(NewDegreeId) of
		#degree_cfg{need_exp = NeedExp, attr_list = _AttrList, need_matrial = NeedMaterial} ->
		case cost_material(Player, NeedMaterial) of
			{false, ErrCode, NewPlayer} -> {false, ErrCode, NewPlayer};
			{true, NewPlayer} ->
				#player_status{degree = Degree1} = NewPlayer,
				NewExp = NowExp - NeedExp,
				case data_degree:get_degree_cfg(NewDegreeId+1) of
					#degree_cfg{need_exp = NextDegreeNeedExp} ->
						DunState = [{get_dun_id(NewDegreeId), 0}],
						MaxExp   = NextDegreeNeedExp * ?ExpRatio;
					_ ->
						DunState = [{get_dun_id(NewDegreeId), 0}],
						MaxExp   = NeedExp * ?ExpRatio  %% 本级是满级
				end,
				NewAttrList = count_attr_list(NewDegreeId),
				NewDegree = Degree1#degree{
					degree_id       = NewDegreeId,
					degree_exp      = max(0, NewExp),
					degree_max_exp  = MaxExp,
					dun_state       = DunState,
					attr_list       = NewAttrList
					,power          = calc_degree_power(NewAttrList)
				},
				F = fun() ->
					update_db_degree_state(NewPlayer#player_status.id, NewDegreeId, NewExp, DunState),
					ok
				end,
				case catch db:transaction(F) of
					ok -> {true, NewPlayer#player_status{degree = NewDegree}};
					_Error -> {false, ?FAIL, NewPlayer}
				end
			end;
		_ ->
			{false, ?ERRCODE(err166_max_degree), Player}
	end.

do_receive_reward(RoleId, ClassId, Degree) ->
	#degree{
		daily_class     = DailyClasses,
		degree_exp      = NowExp,
		degree_max_exp  = MaxExp
	} = Degree,
	%% 增加学位经验
	#degree_timetable_cfg{
		award_exp = AwardExp,
		target    = [{TargetType, TargetId, _Num}]
	} = data_degree:get_degree_timetable_cfg(ClassId),
	NewExp = min(NowExp + AwardExp, MaxExp),
	#daily_class{class_status_map = ClassStatusMap} = DailyClasses,
	NewSate = ?ClassFinish,
	NewClassStatusMap = maps:put(ClassId, NewSate, ClassStatusMap),
	NewDailyClasses = DailyClasses#daily_class{
		class_status_map = NewClassStatusMap
	},
	NewDegree = Degree#degree{
		daily_class = NewDailyClasses,
		degree_exp  = NewExp
	},
	%% 数据库操作
	F = fun() ->
		update_db_classwork_receive_state(RoleId, NewDailyClasses, ClassId),
		update_db_degree_exp(RoleId, NewExp),
		ok
	end,
	case db:transaction(F) of
		ok ->
			%% 日志
			lib_log_api:log_receive_degree_reward(RoleId, ClassId, TargetType, TargetId, NowExp, NewExp),
			{true, NewDegree};
		_Error -> {false, ?FAIL, Degree}
	end.

cost_material(Player, NeedMaterial) ->
	case lib_goods_api:cost_object_list_with_check(Player, NeedMaterial, upgrade_degree_cost, "") of
		{true, NewPlayer, _}-> {true, NewPlayer};
		{false, ErrCode, _NewPlayer} -> {false, ErrCode, Player}
	end.

get_dict_degree(Player) ->
	case Player#player_status.degree of
		undefined -> #degree{};
		Degree = #degree{} -> Degree;
		_ -> #degree{}
	end.

save_dict_degree(Player, NewDegree) ->
	NewPlayer = Player#player_status{degree = NewDegree},
	NewPlayer.

get_dict_degree_classwork(Player) ->
	Degree = Player#player_status.degree,
	Degree#degree.daily_class.

save_dict_degree_classwork(Player, NewClassWork) ->
	Degree = Player#player_status.degree,
	NewDegree = Degree#degree{daily_class = NewClassWork},
	Player#player_status{degree = NewDegree}.

%% -------------------------------- db操作 -------------------------------
get_db_degree(RoleId) ->
	Sql = io_lib:format(?SQL_SELECT_PLAYER_DEGREE, [RoleId]),
	DegreeList = db:get_row(Sql),
	DegreeList.

save_db_degree(RoleId, DegreeId, DegreeExp, DunState) ->
	Sql = io_lib:format(?SQL_INSERT_PLAYER_DEGREE, [RoleId, DegreeId, DegreeExp, DunState, utime:unixtime()]),
	db:execute(Sql).

get_db_degree_classwork(RoleId) ->
	Sql = io_lib:format(?SQL_SELECT_PLAYER_CLASSWORK, [RoleId]),
	ClassWorksList = db:get_all(Sql),
	ClassWorksList.

save_db_degree_classwork(RoleId, ClassId, Content) ->
	Sql = io_lib:format(?SQL_INSERT_PLAYER_CLASSWORK, [RoleId, ClassId, util:term_to_string(Content), utime:unixtime()]),
	db:execute(Sql).

%% 更新操作
update_db_degree_state(RoleId, NewDegreeId, NewExp, DunState) ->
	Sql = io_lib:format(?SQL_UPDATE_PLAYER_DEGREE,
		[NewDegreeId, NewExp, util:term_to_string(DunState), utime:unixtime(), RoleId]),
	db:execute(Sql).

update_db_classwork_state(RoleId, Content) ->
	Sql = io_lib:format(?SQL_UPDATE_PLAYER_CLASSWORK_CONTENT, [util:term_to_string(Content), utime:unixtime(), RoleId]),
	db:execute(Sql).

update_db_classwork_receive_state(RoleId, DailyClasses, ClassId) ->
	#daily_class{class_progress_map = ClassProgressMap} = DailyClasses,
	[TargetType, TargetId, Num] = maps:get(ClassId, ClassProgressMap),   %% TODO
	Content = [TargetType, TargetId, Num, ?ClassFinish],
	Sql = io_lib:format(?SQL_UPDATE_PLAYER_CLASSWORK_CONTENT, [util:term_to_string(Content), utime:unixtime(), RoleId, ClassId]),
	db:execute(Sql).

update_db_degree_exp(RoleId, Exp) ->
	Sql = io_lib:format(?SQL_UPDATE_PLAYER_DEGREE_EXP, [Exp, utime:unixtime(), RoleId]),
	db:execute(Sql).

update_db_degree_dun_state(RoleId, DunState) ->
	Sql = io_lib:format(?SQL_UPDATE_PLAYER_DEGREE_DUN_STATE, [DunState, utime:unixtime(), RoleId]),
	db:execute(Sql).

%% -------------------------------- 秘籍操作 -------------------------------
%% 清理当天领取完奖励的功课任务的进度(数据库数据也会清理)
gm_clear_received_classwork(Player) ->
	#player_status{degree = Degree, id = RoleId} = Player,
	#degree{daily_class = DailyClassWorks} = Degree,
	#daily_class{
		class_status_map   = ClassStatusMap,
		class_progress_map = ClassProgressMap
	} = DailyClassWorks,
	ReceiveClassList = [ClassId || {ClassId, State} <- maps:to_list(ClassStatusMap), State == ?ClassFinish],
	F = fun(TClassId, {TClassStatusMap, TClassProgressMap}) ->
		ClassStatusMap1    = maps:put(TClassId, ?ClassUnFinish, TClassStatusMap),
		[Type, Id, _Num]   = maps:get(TClassId, TClassProgressMap),
		ClassProgressMap1  = maps:put(TClassId, [Type, Id, 0], TClassProgressMap),
		{ClassStatusMap1, ClassProgressMap1}
	end,
	{NewClassStatusMap, NewClassProgressMap} =
		lists:foldl(F, {ClassStatusMap, ClassProgressMap}, ReceiveClassList),
	DailyClassWorks1 = DailyClassWorks#daily_class{
		class_status_map   = NewClassStatusMap,
		class_progress_map = NewClassProgressMap
	},
	%% 存库
	batch_refresh_db_classwork(RoleId, DailyClassWorks1),
	NewPlayer = save_dict_degree_classwork(Player, DailyClassWorks1),
	%% 刷新数据
	F1 = fun(DailyClassId, Ack) ->
		StatusList = [?RefreshType2, DailyClassId, 0, ?ClassUnFinish],
		{ok, Bin} = pt_166:write(16604, StatusList),
		lib_server_send:send_to_sid(Player#player_status.sid, Bin),
		Ack
	end,
	lists:foldl(F1, true, ReceiveClassList),
	NewPlayer.

%% 快速完成指定的功课任务
gm_quick_finish_classwork(Player, ClassId) ->
	#player_status{degree = Degree, id = RoleId} = Player,
	#degree{daily_class = DailyClassWorks} = Degree,
	#daily_class{
		class_status_map   = ClassStatusMap,
		class_progress_map = ClassProgressMap
	} = DailyClassWorks,
	case data_degree:get_degree_timetable_cfg(ClassId) of
		#degree_timetable_cfg{target = [{TargetType, TargetId, NeedNum}]} ->
			NewClassStatusMap   = maps:put(ClassId, ?ClassUnReceive, ClassStatusMap),
			NewClassProgressMap = maps:put(
				ClassId, [TargetType, TargetId, NeedNum], ClassProgressMap
			),
			NewDailyClassWorks = DailyClassWorks#daily_class{
				class_status_map   = NewClassStatusMap,
				class_progress_map = NewClassProgressMap
			},
			%% 存库
			batch_refresh_db_classwork(RoleId, NewDailyClassWorks),
			NewPlayer = save_dict_degree_classwork(Player, NewDailyClassWorks),
			%% 刷新数据
			StatusList = [?RefreshType2, ClassId, NeedNum, ?ClassUnReceive],
			{ok, Bin} = pt_166:write(16604, StatusList),
			lib_server_send:send_to_sid(Player#player_status.sid, Bin),
			NewPlayer;
		_ -> Player
	end.
