%%-----------------------------------------------------------------------------
%% module  :       mod_guild_tribe_mgr
%% @author  :      zmh
%% Created :       20200225
%% description:    驻地管理
%%-----------------------------------------------------------------------------
-module(mod_guild_tribe_mgr).

-include("errcode.hrl").
-include("common.hrl").
-include("figure.hrl").
-include("predefine.hrl").
-include("server.hrl").
-include("guild.hrl").
-include("def_module.hrl").
-include("guild_tribe.hrl").
-include("guild_labor.hrl").
-include("def_event.hrl").

-export([start_link/0]).
-export ([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]).
-export([
    enter_scene/4,
    exit_scene/3,
    enter_scene_logic/6,
    enter_scene_logic/4,
    activitycalen_start/1,
    get_fire_state/1,
    get_pane_data/2,
    change_dance/2,
    role_lv_up/3,
    role_logout/2,
    gm_change/0,
    calc_state/0,
    get_guild_tribe_num/2,
    get_guild_members/0,
    add_click_exp/3
    ]).

%% 定时器启动
start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

enter_scene(RoleID, GuildId, GName,Rlv)->
    gen_server:cast(?MODULE,{enter_scene,RoleID,GuildId,GName,Rlv}).

exit_scene(RoleId,GuildId,OldSceneInfo)->
    gen_server:cast(?MODULE,{exit_scene,RoleId,GuildId,OldSceneInfo}).


enter_scene_logic(RoleID,GuildId,GName,Rlv)->
    gen_server:cast(?MODULE,{enter_scene_logic,RoleID,GuildId,GName,Rlv}).

enter_scene_logic(RoleID,GuildId,GName,Rlv,Act,SubAct)->
    gen_server:cast(?MODULE,{enter_scene_logic,RoleID, GuildId, GName,Rlv,Act,SubAct}).

activitycalen_start(AcSub)->
    gen_server:cast(?MODULE,{activitycalen_start,AcSub}).

get_fire_state(RoleID)->
    gen_server:cast(?MODULE,{get_fire_state,RoleID}).

get_pane_data(GuildID,RoleID)->
    gen_server:cast(?MODULE,{get_pane_data,GuildID,RoleID}).

change_dance(GuildID,RoleID)->
    gen_server:cast(?MODULE,{change_dance,GuildID,RoleID}).

role_lv_up(GuildID,RoleID,Rlv)->
    gen_server:cast(?MODULE,{role_lv_up,GuildID,RoleID,Rlv}).

role_logout(GuildID,RoleID)->
    gen_server:cast(?MODULE,{role_logout,GuildID,RoleID}).

get_guild_tribe_num(GuildID,RoleID)->
    gen_server:cast(?MODULE,{get_guild_tribe_num,GuildID,RoleID}).

get_guild_members() ->
    gen_server:cast(?MODULE,{get_guild_members}).

gm_change()->
    gen_server:cast(?MODULE,gm_change).

add_click_exp(GuildID,RoleID,Exp)->
    gen_server:cast(?MODULE,{click_exp,GuildID,RoleID,Exp}).

init([]) ->
    {CurE,NextDone,Remain} = calc_state(),
    Ref = erlang:send_after(Remain*1000,self(),NextDone),
    {ok, #guild_tribe_mgr{exchange=CurE,eref=Ref,roles=maps:new()}}.

handle_call(Msg, From, State) ->
    case catch do_handle_call(Msg, From, State) of
        {'EXIT', Error} ->
            ?ERR("handle_call error: ~p~nMsg=~p~n", [Error, Msg]),
            {reply, error, State};
        Return  ->
            Return
    end.

handle_cast(Msg, State) ->
    case catch do_handle_cast(Msg, State) of
        {'EXIT', Error} ->
            ?ERR("handle_cast error: ~p~nMsg=~p~n", [Error, Msg]),
            {noreply, State};
        Return  ->
            Return
    end.

handle_info(Info, State) ->
    case catch do_handle_info(Info, State) of
        {'EXIT', Error} ->
            ?ERR("handle_info error: ~p~nInfo=~p~n", [Error, Info]),
            {noreply, State};
        Return  ->
            Return
    end.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

terminate(_Reason, _State) ->
    ?ERR("~p is terminate:~p", [?MODULE, _Reason]),
    ok.


do_handle_call(_Msg, _From, State) ->
    {reply, ok, State}.


do_handle_cast({enter_scene,RoleID,GuildId,_GName,_Rlv},State)->
    SceneId = lib_guild_api:get_guild_scene(),
    %% 切换场景_
    {X,Y,Radius} = urand:list_rand(data_guild:get(born_coordinate)),
    {RandX, RandY} = umath:rand_point_in_circle(X, Y, Radius, 1),
    #guild_tribe_mgr{
        stime = STime,
        etime = ETime
    }=State,
    NowTime = utime:unixtime(),
    if
        NowTime >= STime andalso NowTime =< ETime ->
            lib_seven_days_goal:finish_task(RoleID, ?MOD_GUILD, 7, 1),
            lib_guild_labor:task_active(RoleID, ?GTASK_TYPE_PARTY);
        true -> ignore
    end,
    lib_scene:change_player_scene(RoleID, SceneId, ?GUILD_REM_POOL(GuildId), GuildId, RandX, RandY, false, []),
    {noreply,State};

do_handle_cast({exit_scene,RoleID,GuildID,OldSceneInfo},State)->
    #guild_tribe_mgr{roles = GuildMap} = State,
    case OldSceneInfo of
        {Scene,PoolID,CopyID,X,Y} ->
            case lib_guild_api:check_in_guild_scene(Scene) of
                true  ->
                    {MainX, MainY} = lib_scene:get_main_city_x_y(),
                    lib_scene:change_player_scene(RoleID, ?MAIN_CITY_SCENE, 0, 0, MainX, MainY, true, [{group, 0}]);
                false ->
                    lib_scene:change_player_scene(RoleID, Scene, PoolID, CopyID,X, Y, true, [{group, 0}])
            end;
        _ ->
            {MainX, MainY} = lib_scene:get_main_city_x_y(),
            lib_scene:change_player_scene(RoleID, ?MAIN_CITY_SCENE, 0, 0, MainX, MainY, true, [{group, 0}])
    end,
    TribeG = maps:get(GuildID,GuildMap,#tribe_guild{id=GuildID}),
    #tribe_guild{members=GRoles}=TribeG,
    RData  = lists:keyfind(RoleID,#tribe_role.id,GRoles),
    case RData of
        #tribe_role{last_time = LastTime, enter_time = EnterTime}->
            %% 退出场景统计活动时间
            case lib_activitycalen_api:check_ac_start(?MOD_GUILD, 7) of
                true ->
                    NewLastTime = LastTime + max(0, utime:unixtime() - EnterTime),
                    %% 记录TA数据
                    mod_glamour_focus:update_role_ta_data(RoleID, NewLastTime);
                _ ->
                    NewLastTime = LastTime
            end,
            NRData = RData#tribe_role{pos=0,in_scene=0,last_time = NewLastTime},
            NGRoles = lib_guild_tribe_mod:update_role(GRoles,NRData),
            NewGuildMap = maps:put(GuildID, TribeG#tribe_guild{members = NGRoles}, GuildMap);
        _ ->
            NewGuildMap = GuildMap
    end,
    {noreply, State#guild_tribe_mgr{roles = NewGuildMap}};

do_handle_cast({enter_scene_logic,RoleID,GuildId,GName,Rlv},State)->
    ActModList = [{?MOD_GUILD,7},{?MOD_GUILD_COLLECT,0},{?MOD_GLAMOUR,0}],
    ActSubList = [{?MOD_GUILD_BOSS,1,1},{?MOD_GUILD_BOSS,1,2}],
    OpenList0 = lib_guild_tribe_mod:calc_open_act(mod,ActModList),
    OpenList1 = lib_guild_tribe_mod:calc_open_act(sub,ActSubList),
    OpenList  = OpenList0++OpenList1,
    %lib_guild_tribe_mod:trigger_act_event(RoleID, ?MOD_GUILD_BOSS, 1),
    [lib_guild_tribe_mod:trigger_act_event(RoleID, Act, SubAct)||{Act,SubAct}<-OpenList],
    #guild_tribe_mgr{
        roles = GuildMap
    }=State,
    TribeG = maps:get(GuildId,GuildMap,#tribe_guild{id=GuildId}),
    #tribe_guild{members=GRoles,max_num=CurMax}=TribeG,
    NGRoles   = lib_guild_tribe_mod:add_role(GRoles,
        #tribe_role{id=RoleID,online=1,lv=Rlv,in_scene=1,enter_time=utime:unixtime()}
    ),
    Num = calc_tribe_member_num(NGRoles,0),
    Max = max(CurMax,Num),
    NTribeG = TribeG#tribe_guild{members=NGRoles,num=Num,max_num=Max},
    NGuildMap = maps:put(GuildId,NTribeG,GuildMap),
    NState = State#guild_tribe_mgr{roles=NGuildMap},
    brocast_tribe_member(GuildId,Num),
    mod_guild_boss_mgr:role_enter(RoleID,Rlv,GuildId,GName),
    {noreply,NState};


do_handle_cast({enter_scene_logic,RoleID,GuildId,GName,Rlv,Act,SubAct},State)  ->
    #guild_tribe_mgr{
        roles = GuildMap
    }=State,
    TribeG = maps:get(GuildId,GuildMap,#tribe_guild{id=GuildId}),
    lib_guild_tribe_mod:trigger_act_event(RoleID, Act, SubAct),
    #tribe_guild{members=GRoles,max_num=CurMax}=TribeG,
    NGRoles   = lib_guild_tribe_mod:add_role(GRoles,
        #tribe_role{id=RoleID,online=1,lv=Rlv,in_scene=1,enter_time=utime:unixtime()}
    ),
    Num = calc_tribe_member_num(NGRoles,0),
    Max = max(CurMax,Num),
    NTribeG = TribeG#tribe_guild{members=NGRoles,num=Num,max_num=Max},
    NGuildMap = maps:put(GuildId,NTribeG,GuildMap),
    NState = State#guild_tribe_mgr{roles=NGuildMap},
    brocast_tribe_member(GuildId,Num),
    mod_guild_boss_mgr:role_enter(RoleID,Rlv,GuildId,GName),
    {noreply,NState};

do_handle_cast({get_guild_members}, State)  ->
    #guild_tribe_mgr{
        roles = GuildMap
    }=State,
    F = fun(GuildId, TribeG, TList) ->
        case TribeG of
            #tribe_guild{members=GRoles} ->
                RoleList = [RoleId||
                    #tribe_role{id = RoleId, in_scene = InScene}<-GRoles,
                    InScene == 1
                ],
                [{GuildId, RoleList}|TList];
            _ -> TList
        end end,
    GuildRoleList = maps:fold(F, [], GuildMap),
    mod_guild_boss_mgr:update_guild_role_list(GuildRoleList),
    {noreply, State};

do_handle_cast({activitycalen_start,0},State)->
    #guild_tribe_mgr{ref=ORef,sref=OSRef}=State,
    util:cancel_timer(ORef),
    util:cancel_timer(OSRef),
    Ref = erlang:send_after(1000,self(),activitycalen_end),
    {noreply,State#guild_tribe_mgr{ref=Ref}};

do_handle_cast({activitycalen_start,AcSub},State)->
    #guild_tribe_mgr{ref=ORef,sref=OSRef}=State,
    util:cancel_timer(ORef),
    util:cancel_timer(OSRef),
    ClickA = data_guild:get(click_add_exp),
    ContinueT = lib_activitycalen_api:calc_continue_time(?MOD_GUILD,7,AcSub),
    Ref = erlang:send_after(ContinueT*1000,self(),activitycalen_end),
    SRef = erlang:send_after(ClickA*1000,self(),click_add_exp),
    Now   = utime:unixtime(),
    ETime = Now+ContinueT,
    NState = #guild_tribe_mgr{
        state = 1,
        stime = Now,
        etime = ETime,
        ltime = Now,
        ref   = Ref,
        sref  = SRef,
        roles = maps:new()
    },
    Scene = lib_guild_api:get_guild_scene(),
    mod_guild:apply_cast(lib_guild_tribe_mod,guild_act_start,[Scene,?MOD_GUILD,7]),
    {ok,Bin} = pt_400:write(40057,[Now,ETime,Now]),
    lib_server_send:send_to_all(Bin),
    lib_chat:send_TV({all}, ?MOD_GUILD, 12, []),
    lib_chat:send_TV({all}, ?MOD_GUILD, 15, []),
    %% 判断托管
    offline_hosting_guild_tribe:check_hosting(),
    {noreply,NState};

do_handle_cast({get_fire_state,RoleID},State)->
    #guild_tribe_mgr{
        stime = STime,
        etime = ETime,
        ltime = LTime
    }=State,
    {ok,Bin} = pt_400:write(40057,[STime,ETime,LTime]),
    lib_server_send:send_to_uid(RoleID,Bin),
    {noreply,State};

do_handle_cast({get_pane_data,GuildID,RoleID},State)->
    #guild_tribe_mgr{roles=GuildMap}=State,
    GRoles = maps:get(GuildID,GuildMap,[]),
    case lists:keyfind(RoleID,#tribe_role.id,GRoles) of
        #tribe_role{exp=Exp}->
            skip;
        _->
            Exp = 0
    end,
    {ok,Bin} = pt_400:write(40058,[Exp]),
    lib_server_send:send_to_uid(RoleID,Bin),
    {noreply,State};

do_handle_cast({role_logout,GuildID,RoleID},State) ->
    #guild_tribe_mgr{dots=UsedDots,roles=GuildMap}=State,
    TribeG = maps:get(GuildID,GuildMap,#tribe_guild{id=GuildID}),
    #tribe_guild{members=GRoles}=TribeG,
    RData  = lists:keyfind(RoleID,#tribe_role.id,GRoles),
    case RData of
        #tribe_role{pos=CurPos}->
            NUsedDots = if CurPos>=100->
                            UsedDots;
                           CurPos>0->
                            UsedDots--[CurPos];
                           true->
                            UsedDots
                        end,
            NRData = RData#tribe_role{online=0,pos=0,in_scene=0},
            NGRoles = lib_guild_tribe_mod:update_role(GRoles,NRData);
        _->
            NUsedDots = UsedDots,
            NGRoles = GRoles
    end,
    Num = calc_tribe_member_num(NGRoles,0),
    NTribeG = TribeG#tribe_guild{num=Num,members=NGRoles},
    NGuildMap = maps:put(GuildID,NTribeG,GuildMap),
    NState = State#guild_tribe_mgr{dots=NUsedDots,roles=NGuildMap},
    brocast_tribe_member(GuildID,Num),
    {noreply,NState};

do_handle_cast({role_lv_up,GuildID,RoleID,Rlv},State) when State#guild_tribe_mgr.state>0 ->
    #guild_tribe_mgr{roles=GuildMap}=State,
    TribeG = maps:get(GuildID,GuildMap,#tribe_guild{id=GuildID}),
    #tribe_guild{members=GRoles}=TribeG,
    RData  = lists:keyfind(RoleID,#tribe_role.id,GRoles),
    case RData of
        #tribe_role{}->
            NRData = RData#tribe_role{lv=Rlv},
            NGRoles = lib_guild_tribe_mod:update_role(GRoles,NRData);
        _->
            NGRoles = GRoles
    end,
    NTribeG   = TribeG#tribe_guild{members=NGRoles},
    NGuildMap = maps:put(GuildID,NTribeG,GuildMap),
    NState = State#guild_tribe_mgr{roles=NGuildMap},
    {noreply,NState};

do_handle_cast({change_dance,GuildID,RoleID},State) when State#guild_tribe_mgr.state>0->
    #guild_tribe_mgr{dots=UsedDots,roles=GuildMap}=State,
    TribeG = maps:get(GuildID,GuildMap,#tribe_guild{id=GuildID}),
    #tribe_guild{members=GRoles}=TribeG,
    RData  = lists:keyfind(RoleID,#tribe_role.id,GRoles),
    case RData of
        #tribe_role{pos=CurPos} when CurPos=<0-> %%
            Dot = filter_useful_dot(UsedDots,data_guild:get_dots()),
            NUsedDots = case Dot>=100 of
                true->
                    UsedDots;
                _->
                    [Dot|UsedDots]
            end,
            NRData = RData#tribe_role{pos=Dot},
            NGRoles = lib_guild_tribe_mod:update_role(GRoles,NRData);
        #tribe_role{pos=CurPos} when CurPos>0->
            Dot = 0,
            NUsedDots = case Dot>=100 of
                true->
                    UsedDots;
                _->
                    UsedDots--[CurPos]
            end,
            NRData = RData#tribe_role{pos=Dot},
            NGRoles = lib_guild_tribe_mod:update_role(GRoles,NRData);
        _->
            Dot = 0,
            NUsedDots = UsedDots,
            NGRoles = GRoles
    end,
    NTribeG   = TribeG#tribe_guild{members=NGRoles},
    NGuildMap = maps:put(GuildID,NTribeG,GuildMap),
    NState = State#guild_tribe_mgr{roles=NGuildMap,dots=NUsedDots},
    {ok,Bin} = pt_400:write(40060,[Dot]),
    lib_server_send:send_to_uid(RoleID,Bin),
    {noreply,NState};


do_handle_cast({get_guild_tribe_num,GuildID,RoleID},State)->
    #guild_tribe_mgr{roles=GuildMap}=State,
    #tribe_guild{num=Num} = maps:get(GuildID,GuildMap,#tribe_guild{id=GuildID}),
    {ok,Bin} = pt_400:write(40063,[Num]),
    lib_server_send:send_to_uid(RoleID,Bin),
    {noreply,State};

do_handle_cast({click_exp,GuildID,RoleID,Exp},State)->
    #guild_tribe_mgr{roles=GuildMap}=State,
    TribeG = maps:get(GuildID,GuildMap,#tribe_guild{id=GuildID}),
    #tribe_guild{members=GRoles}=TribeG,
    RData  = lists:keyfind(RoleID,#tribe_role.id,GRoles),
    case RData of
        #tribe_role{exp=AllExp}-> %%
            NRData = RData#tribe_role{exp=AllExp+Exp},
            NGRoles = lib_guild_tribe_mod:update_role(GRoles,NRData),

            NTribeG = TribeG#tribe_guild{members=NGRoles},
            NGuildMap = maps:put(GuildID,NTribeG,GuildMap),
            NState = State#guild_tribe_mgr{roles=NGuildMap};
        _->
            NState = State
    end,
    {noreply,NState};

do_handle_cast(gm_change,State)->
    #guild_tribe_mgr{
        exchange = CurS,
        eref = ORef
    }=State,
    {NCurs,Msg} = case CurS of
        0->
            {1,enter_night};
        _->
            {0,enter_day}
    end,
    util:cancel_timer(ORef),
    Ref = erlang:send_after(3000,self(),Msg),
    NState = State#guild_tribe_mgr{exchange=NCurs,eref=Ref},
    {noreply,NState};

do_handle_cast(_Msg, State) ->
    {noreply, State}.

do_handle_info(click_add_exp,State)->
    #guild_tribe_mgr{roles=GuildMap}=State,
    ClickA = data_guild:get(click_add_exp),
    SRef = erlang:send_after(ClickA*1000,self(),click_add_exp),
    Now = utime:unixtime(),
    NewGuildMap =
    maps:fold(fun(GuildID,TribeG,TMap)->
        #tribe_guild{members=GRoles,num=Num}=TribeG,
        NGRoles = lib_guild_tribe_mod:add_exp(GRoles,Now,Num,GRoles),
        NewTribeG = TribeG#tribe_guild{members = NGRoles},
        maps:put(GuildID, NewTribeG, TMap)
    end,GuildMap,GuildMap),
    {noreply,State#guild_tribe_mgr{sref=SRef,ltime=Now,roles = NewGuildMap}};

do_handle_info(activitycalen_end,State)->
    #guild_tribe_mgr{ref=ORef,sref=OSRef,roles=GuildMap}=State,
    util:cancel_timer(ORef),
    util:cancel_timer(OSRef),
    {ok,Bin} = pt_400:write(40057,[0,0,0]),
    lib_server_send:send_to_all(Bin),
    UpDataMap =
    maps:fold(fun(GuildID,TribeG,TMap)->
        #tribe_guild{max_num=Num,members=Members}=TribeG,
        F = fun(Member, TList) ->
            case Member of
                #tribe_role{id = RoleId, last_time = LastTime, enter_time = EnterTime, in_scene = IsInScene} ->
                    case IsInScene of
                        1 ->
                            NewLastTime = LastTime + max(0, utime:unixtime() - EnterTime),
                            IsHosting = offline_hosting_api:is_offline_hosting(RoleId, ?MOD_GUILD),
                            [{RoleId, NewLastTime, IsHosting}|TList];
                        _ ->
                            TList
                    end;
                _ ->
                    TList
            end end,
        MemList = lists:foldl(F, [], Members),
        Num>0 andalso mod_guild:cast_to_guild({update_party_num,GuildID,Num}),
        maps:put(GuildID, MemList, TMap)
    end, #{}, GuildMap),
    mod_glamour_focus:update_ta_data(UpDataMap),
    offline_hosting_guild_tribe:notice_logout(),
    {noreply,#guild_tribe_mgr{roles=maps:new()}};

do_handle_info(enter_day,State)->
    #guild_tribe_mgr{eref=OERef}=State,
    util:cancel_timer(OERef),
    ERef = erlang:send_after(43200*1000,self(),enter_night),
    NState = State#guild_tribe_mgr{exchange=0,eref=ERef},
    mod_guild:cast_to_guild({guild_scene_exchange,0}),
    {noreply,NState};

do_handle_info(enter_night,State)->
    #guild_tribe_mgr{eref=OERef}=State,
    util:cancel_timer(OERef),
    ERef = erlang:send_after(43200*1000,self(),enter_day),
    NState = State#guild_tribe_mgr{exchange=1,eref=ERef},
    mod_guild:cast_to_guild({guild_scene_exchange,1}),
    {noreply,NState};

do_handle_info(_Msg, State) ->
    {noreply, State}.


filter_useful_dot(UsedDots,Dots)->
    RemainDots = Dots--UsedDots,
    case lists:sort(RemainDots) of
        [H|_T]->
            H;
        _->
            100
    end.

%%白夜计算
calc_state()->
    [Start,End] = data_guild:get(guild_day_night),
    {Date,_} = utime:localtime(),
    StartT = utime:unixtime({Date, {Start, 0, 0}}),
    EndT   = utime:unixtime({Date, {End, 0, 0}}),
    Now   = utime:unixtime(),
    if Now<StartT ->
            {1,enter_day,max(StartT-Now,1)};
       Now>EndT->
            Tomorrow = utime:tomorrow(Now),
            {1,enter_day,max(Tomorrow+Start*3600-Now,1)};
       true->
            {0,enter_night,max(EndT-Now,1)}
    end.

%%计算在线
calc_tribe_member_num([],Num)->
    Num;
calc_tribe_member_num([H|T],Num)->
    case H of
        #tribe_role{online=1}->
            calc_tribe_member_num(T,Num+1);
        _->
            calc_tribe_member_num(T,Num)
    end.


brocast_tribe_member(GuildID,Num)->
    {ok,Bin} = pt_400:write(40063,[Num]),
    lib_server_send:send_to_guild(GuildID,Bin).