%%%===================================================================
%%% @author zmh
%%% @doc 征途活动

-module(custom_act_journey).
-include("server.hrl").
-include("predefine.hrl").
-include("def_goods.hrl").
-include("goods.hrl").
-include("custom_act.hrl").
-include("custom_journey.hrl").
-include("def_module.hrl").
-include("rec_event.hrl").
-include("def_event.hrl").
-include("common.hrl").
-include("errcode.hrl").
-include("figure.hrl").
-compile(export_all).

-export([
	gm/1,
	gm/2,
	login/1,
	get_act_tasks/2,
	save_act_journey/2,
	trigger_act_task/2,
	trigger_act_task/3,
	notify_other_module/5
]).

gm(RoleID)->
	Opens = lib_custom_act_util:get_open_subtype_list(?CUSTOM_ACT_TYPE_JOURNEY),
	[gm(RoleID,SubType)||#act_info{key={_,SubType}}<-Opens].

gm(RoleID,SubType)->
	lib_player:apply_cast(RoleID,?APPLY_CAST_STATUS, ?MODULE, gm_print, [SubType]).

gm_print(Status,SubType)->
	ActJourney = get_act_journey(SubType),
	?INFO("cur act journey data:~p~n",[ActJourney]),
	{ok,Status}.

get_act_tasks(RoleID,Act)->
	ActJourney = get_act_journey(RoleID,Act),
	ActJourney#act_journey.map_tasks.

%%登录加载
login(RoleID)->
	Sql = io_lib:format(?SQL_PLAYER_JOURNEY_GET, [RoleID]),
	case db:get_all(Sql) of
        Datas when is_list(Datas)  ->
            split_datas(Datas,[]);
        _ ->
        	[]
    end.

split_datas([],ActJourneys)->
	[save_act_journey(ActJourney#act_journey.act,ActJourney)||ActJourney<-ActJourneys];
split_datas([[Act,Map,Tasks0]|T],ActJourneys)->
	Tasks1  = util:bitstring_to_term(Tasks0),
	Tasks   = [#journey_task{id=Id,state=CurS,value=CurV,grade=Grade}||{Id,CurS,CurV,Grade}<-Tasks1],
	Fin = length([Id||#journey_task{id=Id,state=CurS}<-Tasks,CurS>=?JSTATUS_FIN]),
	MapJourney = #map_journey{map=Map,finish=Fin,tasks=Tasks},
	case lists:keyfind(Act,#act_journey.act,ActJourneys) of
		ActJourney when is_record(ActJourney,act_journey)->
			#act_journey{map_tasks=OMapTasks}=ActJourney,
			MapTasks = lists:keystore(Map,#map_journey.map,OMapTasks,MapJourney),
			NActJourney = ActJourney#act_journey{map_tasks=MapTasks},
			NActJourneys = lists:keystore(Act,#act_journey.act,ActJourneys,NActJourney),
			split_datas(T,NActJourneys);
		_->
			ActJourney = #act_journey{act=Act,map_tasks=[MapJourney]},
			split_datas(T,[ActJourney|ActJourneys])
	end.


%%付费任务
custom_task(SubType,Map,Id,Status)->
	#player_status{id=RoleID}=Status,
	Type = ?CUSTOM_ACT_TYPE_JOURNEY,
	case lib_custom_act_util:get_custom_act_open_info(Type,SubType) of
		#act_info{stime=STime}->
			Day = util:get_open_day(STime),
			ActJourney = get_act_journey(RoleID,SubType),
			#act_journey{map_tasks=ActTasks}=ActJourney,
			Suit = get_key_value(SubType,suit),
			MapJourney = lists:keyfind(Map,#map_journey.map,ActTasks),
			case MapJourney of
				#map_journey{finish=CurFin,tasks=MapTasks}->
					skip;
				_->
					CurFin=0,MapTasks=[]
			end,
			Task = lists:keyfind(Id,#journey_task.id,MapTasks),
			All0 = data_custom_journey:get_map_award(SubType,Map),
			All  = filter_day_out(SubType,Map,Day,All0,[]),
			GradeUsed = [Used||#journey_task{grade=Used}<-MapTasks,Used>0],
			Remains   = All--GradeUsed,
			CkList = [
				{check_map,MapJourney},
				{check_task_idle,Task},
				{check_awards,Remains},
				{check_lock_map,Map,ActTasks},
				{check_lock_task,Suit,Map,Id,CurFin}
			],
			case util:check_lists(?MODULE,CkList) of
				true->
					#base_journey_task{
						task_type=TaskType,
						cons=Cons
					} = data_custom_journey:get_task(Suit,Map,Id),
					Cost = case TaskType of
						1->
							[{?TYPE_GOLD,?GOODS_ID_GOLD,Cons}];
						2->
							[{?TYPE_BGOLD,?GOODS_ID_BGOLD,Cons}];
						3->
							[{?TYPE_COIN,?GOODS_ID_COIN,Cons}];
						_->
							[{?TYPE_HONOUR,?GOODS_ID_HONOR,Cons}]
					end,
					case lib_goods_api:cost_object_list_with_check(Status,Cost,custom_journey,"征途活动") of
						{true, NewStatus0, _ObjectList} ->
							WeigtList = get_weight_list(SubType,Map,Remains),
							{Grade,Awards} = urand:rand_with_weight(WeigtList),

							Produce = #produce{type = custom_journey, reward=Awards},
            				{ok,NewStatus} = lib_goods_api:send_reward_with_mail(NewStatus0, Produce, ?MOD_JOURNEY),

							NTask = Task#journey_task{state=?JSTATUS_OVER,value=Cons,grade=Grade},
							NewMapTasks = lists:keystore(Id,#journey_task.id,MapTasks,NTask),
							NewFin = length([I||#journey_task{id=I,state=Cs}<-NewMapTasks,Cs>=?JSTATUS_FIN]),
							NewMapJourney = MapJourney#map_journey{finish=NewFin,tasks=NewMapTasks},
							NewActTasks = lists:keystore(Map,#map_journey.map,ActTasks,NewMapJourney),
							NewActJourney = ActJourney#act_journey{map_tasks=NewActTasks},
							notice_task(RoleID,SubType,Map,NTask),
							save_act_journey(SubType,NewActJourney),
							db_save_act_map(RoleID,SubType,Map,NewMapTasks),
							lib_log_api:custom_act_reward_log(NewStatus, Status, Type, SubType, Map*1000+Grade, io_lib:format("task:~w", [Id]), Awards),
							{ok,NewStatus};
						{false,Err,_}->
							{false,Err}
					end;
				{false,Err}->
					{false,Err}
			end;
		_->
			{false,?ERRCODE(err331_act_closed)}
	end.

%%领取
fetch_award(SubType,Map,Id,Status)->
	#player_status{id=RoleID}=Status,
	Type = ?CUSTOM_ACT_TYPE_JOURNEY,
	case lib_custom_act_util:get_custom_act_open_info(Type,SubType) of
		#act_info{stime=STime}->
			Day = util:get_open_day(STime),
			ActJourney = get_act_journey(RoleID,SubType),
			#act_journey{map_tasks=ActTasks}=ActJourney,
			MapJourney = lists:keyfind(Map,#map_journey.map,ActTasks),
			case MapJourney of
				#map_journey{tasks=MapTasks}->
					skip;
				_->
					MapTasks=[]
			end,
			Task = lists:keyfind(Id,#journey_task.id,MapTasks),
			All0 = data_custom_journey:get_map_award(SubType,Map),
			All  = filter_day_out(SubType,Map,Day,All0,[]),
			GradeUsed = [Used||#journey_task{grade=Used}<-MapTasks,Used>0],
			Remains   = All--GradeUsed,
			CkList = [
				{check_map,MapJourney},
				{check_task_fin,Task},
				{check_awards,Remains}
			],
			case util:check_lists(?MODULE,CkList) of
				true->
					WeigtList = get_weight_list(SubType,Map,Remains),
					{Grade,Awards} = urand:rand_with_weight(WeigtList),

					Produce = #produce{type = custom_journey, reward=Awards},
            		{ok,NStatus} = lib_goods_api:send_reward_with_mail(Status, Produce, ?MOD_JOURNEY),

					NTask = Task#journey_task{state=?JSTATUS_OVER,grade=Grade},
					NewMapTasks = lists:keystore(Id,#journey_task.id,MapTasks,NTask),
					NewMapJourney = MapJourney#map_journey{tasks=NewMapTasks},
					NewActTasks = lists:keystore(Map,#map_journey.map,ActTasks,NewMapJourney),
					NewActJourney = ActJourney#act_journey{map_tasks=NewActTasks},
					notice_task(RoleID,SubType,Map,NTask),
					save_act_journey(SubType,NewActJourney),
					db_save_act_map(RoleID,SubType,Map,NewMapTasks),
					lib_log_api:custom_act_reward_log(NStatus, Status, Type, SubType, Map*1000+Grade, io_lib:format("task:~w", [Id]), Awards),
					{ok,NStatus};
				{false,Err}->
					{false,Err}
			end;
		_->
			{false,?ERRCODE(err331_act_closed)}
	end.


filter_day_out(_Act,_Map,_Day,[],All)->
	All;
filter_day_out(Act,Map,Day,[H|T],All)->
	#base_journey_award{
		days = [Min,Max]
	}=data_custom_journey:get_award(Act,Map,H),
	case Min=<Day andalso Day=<Max of
		true->
			filter_day_out(Act,Map,Day,T,[H|All]);
		_->
			filter_day_out(Act,Map,Day,T,All)
	end.


get_weight_list(Act,Map,Remains)->
	get_weight_list(Act,Map,Remains,[]).
get_weight_list(_Act,_Map,[],Weigts)->
	Weigts;
get_weight_list(Act,Map,[H|T],Weigts)->
	#base_journey_award{
		weight=W,
		award=Awards
	}=data_custom_journey:get_award(Act,Map,H),
	get_weight_list(Act,Map,T,[{W,{H,Awards}}|Weigts]).

erase_act_journey(Act)->
	erase({act_journey,Act}).

save_act_journey(Act,ActJourney)->
	put({act_journey,Act},ActJourney).
get_act_journey(Act)->
	case get({act_journey,Act}) of
		ActJourney when is_record(ActJourney,act_journey)->
			ActJourney;
		_->
			[]
	end.

get_act_journey(RoleID,Act)->
	case get_act_journey(Act) of
		ActJourney when is_record(ActJourney,act_journey)->
			ActJourney;
		_->
			Suit = get_key_value(Act,suit),
			MapTasks = init_act_tasks(Suit),
			ActJourney  = #act_journey{act=Act,map_tasks=MapTasks},
			save_act_journey(Act,ActJourney),
			db_save_act_map_batch(RoleID,ActJourney),
			ActJourney
	end.

init_act_tasks(Suit)->
	Maps = data_custom_journey:get_suit_map(Suit),
	MapTasks = init_map_tasks(Suit,Maps,[]),
	MapTasks.

init_map_tasks(_Suit,[],MapTasks)->
	MapTasks;
init_map_tasks(Suit,[H|T],MapTasks)->
	Lists = data_custom_journey:get_suit_map_task(Suit,H),
	Tasks = init_tasks(Suit,H,Lists,[]),
	init_map_tasks(Suit,T,[#map_journey{map=H,tasks=Tasks}|MapTasks]).

init_tasks(_Suit,_Map,[],Tasks)->
	Tasks;
init_tasks(Suit,Map,[H|T],Tasks)->
	case data_custom_journey:get_task(Suit,Map,H) of
		#base_journey_task{}->
			init_tasks(Suit,Map,T,[#journey_task{id=H}|Tasks]);
		_->
			init_tasks(Suit,Map,T,Tasks)
	end.

get_key_value(SubType,Key)->
	get_key_value(SubType,Key,1).
get_key_value(SubType,Key,DefV)->
	Type = ?CUSTOM_ACT_TYPE_JOURNEY,
	Cfg = lib_custom_act_util:get_act_cfg_info(Type,SubType),
	case Cfg of
		#custom_act_cfg{condition=Cons}->
			case lists:keyfind(Key,1,Cons) of
				{_,Value}->
					skip;
				_->
					Value = DefV
			end;
		_->
			Value = DefV
	end,
	Value.


%%被动类触发
trigger_act_task(RoleID,Key)->
	trigger_act_task(RoleID,Key,1).
trigger_act_task(RoleID,Key,Times) when is_integer(RoleID)->
	lib_player:apply_cast(RoleID, ?APPLY_CAST_STATUS, ?MODULE, trigger_act_task, [Key,Times]);
trigger_act_task(Status,Key,Times)->
	Type = data_custom_journey:get(Key),
	Opens = lib_custom_act_util:get_open_subtype_list(?CUSTOM_ACT_TYPE_JOURNEY),
	trigger_act_task(Status,Opens,Type,Times).

trigger_act_task(Status,[],_Type,_Times)->
	{ok,Status};
trigger_act_task(Status,[#act_info{key={_,Act}}|T],Type,Times)->
	#player_status{id=RoleID}=Status,
	ActJourney = get_act_journey(RoleID,Act),
	#act_journey{
		map_tasks = MapTasks
	}=ActJourney,
	Suit = get_key_value(Act,suit),
	NMapTasks = trigger_map_task(RoleID,Act,Suit,MapTasks,Type,Times),
	NActJourney = ActJourney#act_journey{map_tasks=NMapTasks},
	save_act_journey(Act,NActJourney),
	trigger_act_task(Status,T,Type,Times).

trigger_map_task(RoleID,Act,Suit,MapTasks,Type,Times)->
	trigger_map_task(RoleID,Act,Suit,MapTasks,MapTasks,Type,Times,[]).

trigger_map_task(_RoleID,_Act,_Suit,[],_MapTasks,_Type,_Times,NMapTasks)->
	NMapTasks;
trigger_map_task(RoleID,Act,Suit,[H|T],MapTasks,Type,Times,NMapTasks)->
	#map_journey{
		map = Map,
		finish = CurFin,
		tasks = Tasks
	}=H,
	{IsUpdate,NTasks} = case data_custom_journey:get_map(Map) of
		#base_journey_map{unlock_map=UMap} when UMap=<0->
			trigger_task(RoleID,Act,Suit,Map,Tasks,CurFin,Type,Times);
		#base_journey_map{unlock_map=UMap,unlock_tasks=UTask}->
			case lists:keyfind(UMap,#map_journey.map,MapTasks) of
				#map_journey{finish=UFin} when UFin>=UTask->
					trigger_task(RoleID,Act,Suit,Map,Tasks,CurFin,Type,Times);
				_->
					{false,Tasks}
			end;
		_->
			{false,Tasks}
	end,
	NewFin = length([Id||#journey_task{id=Id,state=CurS}<-NTasks,CurS>=?JSTATUS_FIN]),
	NewH = H#map_journey{finish=NewFin,tasks=NTasks},
	IsUpdate andalso db_save_act_map(RoleID,Act,Map,NTasks),
	trigger_map_task(RoleID,Act,Suit,T,MapTasks,Type,Times,[NewH|NMapTasks]).



trigger_task(RoleID,Act,Suit,Map,Tasks,Fin,Type,Times)->
	trigger_task(RoleID,Act,Suit,Map,Tasks,Fin,Type,Times,[],false).
trigger_task(_RoleID,_Act,_Suit,_Map,[],_Fin,_Type,_Times,NTasks,IsUpdate)->
	{IsUpdate,NTasks};
trigger_task(RoleID,Act,Suit,Map,[H|T],Fin,Type,Times,NTasks,IsUpdate)->
	#journey_task{id=TaskId,state=CurS,value=CurV}=H,
	case CurS>=?JSTATUS_FIN of
		true->
			trigger_task(RoleID,Act,Suit,Map,T,Fin,Type,Times,[H|NTasks],IsUpdate);
		_->
			case data_custom_journey:get_task(Suit,Map,TaskId) of
				 #base_journey_task{task_type=Type,cons=Max,unlock=ULock} when Fin>=ULock->
				 	NewV = min(CurV+Times,Max),
				 	NewS = case NewV>=Max of
				 		true->
				 			case lists:member(Type,[1,2,3,4]) of
				 				true->
				 					?JSTATUS_OVER;
				 				_->
									?JSTATUS_FIN
							end;
				 		_->
				 			CurS
				 	end,
				 	NewH = H#journey_task{state=NewS,value=NewV},
				 	notice_task(RoleID,Act,Map,NewH),
				 	trigger_task(RoleID,Act,Suit,Map,T,Fin,Type,Times,[NewH|NTasks],true);
				 _->
					trigger_task(RoleID,Act,Suit,Map,T,Fin,Type,Times,[H|NTasks],IsUpdate)
			end
	end.


%%数据保存
db_save_act_map(RoleID,SubType,Map,MapTasks)->
	Tasks0 = [{Id,CurS,CurV,Grade}||#journey_task{id=Id,state=CurS,value=CurV,grade=Grade}<-MapTasks],
	Tasks  = util:term_to_string(Tasks0),
	Sql = io_lib:format(?SQL_PLAYER_JOURNEY_REPLACE,[RoleID,SubType,Map,Tasks]),
	db:execute(Sql).

%%批量保存
db_save_act_map_batch(RoleID,ActJourney)->
	#act_journey{
		act = SubType,
		map_tasks = ActTasks
	}=ActJourney,
	db_save_act_map_batch(RoleID,SubType,ActTasks).

db_save_act_map_batch(_RoleID,_SubType,[])->
	ignore;
db_save_act_map_batch(RoleID,SubType,ActTasks)->
	DataStrList = [format_bin_string(RoleID,SubType,MapJourney) || MapJourney <- ActTasks],
    DataStrAll  = util:join_bitstring(DataStrList, <<",">>),
    Sql = util:fmt_bin(?SQL_PLAYER_JOURNEY_BATCH, [DataStrAll]),
    db:execute(Sql).


format_bin_string(RoleID,SubType,MapJourney)->
	#map_journey{
		map = Map,
		tasks = MapTasks
	}=MapJourney,
	Tasks0 = [{Id,CurS,CurV,Grade}||#journey_task{id=Id,state=CurS,value=CurV,grade=Grade}<-MapTasks],
	Tasks  = util:term_to_string(Tasks0),
	util:fmt_bin(<<"(~w,~w,~w,'~ts')">>, [RoleID,SubType,Map,Tasks]).


notice_task(RoleID,Act,Map,Task)->
	#journey_task{
		id = Id,
		state = CurS,
		value = CurV,
		grade = Grade
	}=Task,
	{ok,Bin} = pt_138:write(13801,[Act,Map,Id,CurS,CurV,Grade]),
	lib_server_send:send_to_uid(RoleID,Bin).

%% ---------------------------------------------------------------------------
%% @doc 活动派发接口：定制活动开启或者关闭 默认处理
%% ---------------------------------------------------------------------------
%% @doc 活动派发接口：定制活动开启或者关闭 默认处理
notify_other_module(?CUSTOM_ACT_STATUS_OPEN,_IsKfActType,ActInfo, _Stime, _Etime) ->
    lib_custom_act:broadcast_act_info(?CUSTOM_ACT_STATUS_OPEN, [ActInfo]);
notify_other_module(ActStatus,_IsKfActType, #act_info{key = {ActType, SubType}} = ActInfo,_Stime, _Etime)
    when ActStatus =:= ?CUSTOM_ACT_STATUS_CLOSE
    orelse ActStatus =:= ?CUSTOM_ACT_STATUS_MANUAL_CLOSE ->
	    lib_custom_act:broadcast_act_info(ActStatus, [ActInfo]),
	    %% 活动结束清理数据的才需要
	    Sql = io_lib:format(<<"delete from `custom_act_journey` where `act`=~p">>,[SubType]),
	    db:execute(Sql),
	    %% 玩家身上清除活动数据
	    lib_player:cast_online_role_apply(?APPLY_CAST_STATUS, ?MODULE, clear_custom_data, [ActType, SubType]);
notify_other_module(_, _, _, _, _) -> ok.

clear_custom_data(Status,_ActType,SubType)->
	erase_act_journey(SubType),
	{ok,Status}.

check({check_lock_map,Map,ActTasks})->
	case data_custom_journey:get_map(Map) of
		#base_journey_map{unlock_map=UMap} when UMap=<0->
			true;
		#base_journey_map{unlock_map=UMap,unlock_tasks=UTask}->
			MapJourney = lists:keyfind(UMap,#map_journey.map,ActTasks),
			case MapJourney of
				#map_journey{finish=UFin}->
					skip;
				_->
					UFin=0
			end,
			case UFin>=UTask of
				true->
					true;
				_->
					{false,?ERRCODE(err138_journey_map_locked)}
			end;
		_->
			{false,?ERRCODE(err138_journey_map_locked)}
	end;
check({check_lock_task,Suit,Map,Id,CurFin})->
	case data_custom_journey:get_task(Suit,Map,Id) of
		#base_journey_task{unlock=UTask} when UTask=<0->
			true;
		#base_journey_task{unlock=UTask} when CurFin>=UTask->
			true;
		_->
			{false,?ERRCODE(err138_journey_task_locked)}
	end;
check({check_awards,Remains})->
	case length(Remains)>0 of
		true->
			true;
		_->
			{false,?ERRCODE(err_config)}
	end;
check({check_map,MapJourney})->
	case is_record(MapJourney,map_journey) of
		true->
			true;
		_->
			{false,?ERRCODE(err138_journey_map_err)}
	end;
check({check_task_idle,Task})->
	case Task of
		#journey_task{state=?JSTATUS_INIT}->
			true;
		_->
			{false,?ERRCODE(err138_journey_has_finished)}
	end;
check({check_task_fin,Task})->
	case Task of
		#journey_task{state=?JSTATUS_FIN}->
			true;
		#journey_task{state=?JSTATUS_OVER}->
			{false,?ERRCODE(err138_journey_cant_fetch_again)};
		_->
			{false,?ERRCODE(err138_journey_need_finish)}
	end.