%%-----------------------------------------------------------------------------
%% module  :       mod_unite_buff_mgr
%% @author  :      zmh
%% Created :       20210113
%% description:    总进度管理
%%% 注意 本活动单开
%%-----------------------------------------------------------------------------
-module(mod_unite_buff_mgr).

-include("common.hrl").
-include("def_module.hrl").
-include("errcode.hrl").
-include("custom_act.hrl").
-include("custom_unite_buff.hrl").
-compile(export_all).
-export ([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]).
-export ([
    start_link/0,
    cast/1,
    gm/3,
    timer_check/0,
    min_check/0,
    get_server_pro/1
]).

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

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

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

gm(RoleID,SubType,Add)->
    Type = ?CUSTOM_ACT_TYPE_UNITE_BUFF,
    {_,Suit}  =  lib_custom_act_util:keyfind_act_condition(Type,SubType,suit),
    Now = utime:unixtime(),
    {_,{_,Min,_}}=utime:unixtime_to_localtime(Now),
    Zone = Min div ?CALC_ZONE,
    mod_unite_buff_mgr:cast({submit_items,SubType,Suit,Zone,RoleID,100,200,Add,Now}).

%% API
start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

get_server_pro(SubType)->
    gen_server:call(?MODULE,{server_pro,SubType}).

init([]) ->
    Sql = io_lib:format(?SQL_UNITE_ACT_GET, []),
    State = case db:get_all(Sql) of
        State0 when is_list(State0)  ->
            make_act_mgr(State0,[]);
        _ ->
            []
    end,
    {ok, State}.

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({server_pro,SubType},_From,State)->
    case lists:keyfind(SubType,#act_pro_mgr.act,State) of
        #act_pro_mgr{process=SerPro}->
            skip;
        _->
            SerPro = 0
    end,
    {reply,SerPro,State};

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

do_handle_cast({close_act,SubType},State)->
    NewState = lists:keydelete(SubType,#act_pro_mgr.act,State),
    {noreply,NewState};

do_handle_cast({get_pane_info,SubType,RoleID,RoleUniteB},State)->
    case lists:keyfind(SubType,#act_pro_mgr.act,State) of
        #act_pro_mgr{process=SerPro}->
            skip;
        _->
            SerPro = 0
    end,
    #act_unite_buff{
        process=RolePro,
        role_awards=RAwards,
        server_awards=SAwards,
        buffs=BuffsTmp
    }=RoleUniteB,
    Buffs = [{Dot,Bid,STime,ETime,Useds}||#unite_buff{dot=Dot,bid=Bid,stime=STime,etime=ETime,used=Useds}<-BuffsTmp],
    {ok,Bin} = pt_141:write(14100,[SubType,SerPro,RolePro,SAwards,Buffs,RAwards]),
    lib_server_send:send_to_uid(RoleID,Bin),
    {noreply,State};

do_handle_cast({submit_items,SubType,Suit,Zone,RoleID,Live,RolePro,Add,Now},State)->
    Type = ?CUSTOM_ACT_TYPE_UNITE_BUFF,
    case lists:keyfind(SubType,#act_pro_mgr.act,State) of
        ActProMgr when is_record(ActProMgr,act_pro_mgr)->
            skip;
        _->
            #act_info{stime=STime,etime=ETime} = lib_custom_act_util:get_custom_act_open_info(Type, SubType),
            ActProMgr = #act_pro_mgr{act=SubType,stime=STime,etime=ETime},
            db_save_act_mgr(ActProMgr)
    end,
    #act_pro_mgr{
        process = SerPro,
        buffs   = Buffs,
        etime   = Max
    }=ActProMgr,
    TimeZones = get_time_zones(SubType),
    NewSerPro = SerPro+Add,
    Dots  =  data_custom_unite_buff:get_suit_dot(Suit),
    {NewBuffs,Bids,Bin} = active_buff(Buffs,Dots,SubType,Suit,NewSerPro,SerPro,Now,Max,[],<<>>),
    NewTimeZones = update_time_zones(Zone,RoleID,Live,Add,TimeZones),
    NActProMgr = ActProMgr#act_pro_mgr{process=NewSerPro,buffs=NewBuffs},
    NewState = lists:keystore(SubType,#act_pro_mgr.act,State,NActProMgr),
    Bids /= [] andalso lib_player:apply_cast_online(custom_unite_buff, sync_unite_buff, [SubType,Now,NewBuffs,Bin]),
    {ok,Bin0} = pt_141:write(14103,[SubType,NewSerPro]),
    lib_server_send:send_to_all(Bin0),
    {ok,Bin1} = pt_141:write(14101,[?SUCCESS,SubType,NewSerPro,RolePro]),
    lib_server_send:send_to_uid(RoleID,Bin1),
    save_time_zones(SubType,NewTimeZones),
    db_update_act_mgr(NActProMgr),
    trigger_effect(Bids),
    {noreply,NewState};

do_handle_cast({timer_check},State)->
    Now = utime:unixtime(),
    {_,{_,Min,_}}=utime:unixtime_to_localtime(Now),
    Zone = Min div ?CALC_ZONE,
    LZone = case Zone=<0 of
        true->
            ?MAX_ZONE;
        _->
            Zone-1
    end,
    Day  = util:get_open_day(),
    NewState = calc_zone_data(State,LZone,Day),
    %io:format("---------------:~p~n",[?LINE]),
    {noreply,NewState};

do_handle_cast({min_check},State)->
    Now = utime:unixtime(),
    Day  = util:get_open_day(),
    %io:format("---------------:~p~n",[?LINE]),
    NewState = click_auto_add(State,Now,Day),
    {noreply,NewState};


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

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


active_buff(Buffs,[],_SubType,_Suit,_NewSerPro,_SerPro,_Now,_ETime,Bids,Bin)->
    {Buffs,Bids,Bin};
active_buff(Buffs,[{Dot,Need}|T],SubType,Suit,NewSerPro,SerPro,Now,ETime,Bids,OBin)->
    case SerPro<Need andalso NewSerPro>=Need of
        true->
            case data_custom_unite_buff:get_role_dot_awards(Suit,Dot) of
                #base_unite_buff_awards{type=1,awards=BuffId}->
                    case data_custom_unite_buff:get_buff(BuffId) of
                        #base_unite_buff{type=1,time=BTime}->
                            NewETime = min(Now+BTime,ETime),
                            Buf = #unite_buff{dot=Dot,bid=BuffId,stime=Now,etime=NewETime},
                            NewBuffs = lists:keystore(Dot,#unite_buff.dot,Buffs,Buf),
                            {ok,Bin} = pt_141:write(14104,[SubType,Dot,BuffId,Now,NewETime]),
                            NewBin = <<OBin/binary,Bin/binary>>,
                            NBids = [BuffId|Bids],
                            active_buff(NewBuffs,T,SubType,Suit,NewSerPro,SerPro,Now,ETime,NBids,NewBin);
                        #base_unite_buff{type=2}->
                            NewETime = min(utime:tomorrow(),ETime),
                            Buf = #unite_buff{dot=Dot,bid=BuffId,stime=Now,etime=NewETime},
                            NewBuffs = lists:keystore(Dot,#unite_buff.dot,Buffs,Buf),
                            {ok,Bin} = pt_141:write(14104,[SubType,Dot,BuffId,Now,NewETime]),
                             NewBin = <<OBin/binary,Bin/binary>>,
                            NBids = [BuffId|Bids],
                            active_buff(NewBuffs,T,SubType,Suit,NewSerPro,SerPro,Now,ETime,NBids,NewBin);
                        _->
                            active_buff(Buffs,T,SubType,Suit,NewSerPro,SerPro,Now,ETime,Bids,OBin)
                    end;
                #base_unite_buff_awards{type=3,awards=[BuffId,_]}->
                    case data_custom_unite_buff:get_buff(BuffId) of
                        #base_unite_buff{type=1,time=BTime}->
                            NewETime = min(Now+BTime,ETime),
                            Buf = #unite_buff{dot=Dot,bid=BuffId,stime=Now,etime=NewETime},
                            NewBuffs = lists:keystore(Dot,#unite_buff.dot,Buffs,Buf),
                            {ok,Bin} = pt_141:write(14104,[SubType,Dot,BuffId,Now,NewETime]),
                            NewBin = <<OBin/binary,Bin/binary>>,
                            NBids = [BuffId|Bids],
                            active_buff(NewBuffs,T,SubType,Suit,NewSerPro,SerPro,Now,ETime,NBids,NewBin);
                        #base_unite_buff{type=2}->
                            NewETime = min(utime:tomorrow(),ETime),
                            Buf = #unite_buff{dot=Dot,bid=BuffId,stime=Now,etime=NewETime},
                            NewBuffs = lists:keystore(Dot,#unite_buff.dot,Buffs,Buf),
                            {ok,Bin} = pt_141:write(14104,[SubType,Dot,BuffId,Now,NewETime]),
                            NewBin = <<OBin/binary,Bin/binary>>,
                            NBids = [BuffId|Bids],
                            active_buff(NewBuffs,T,SubType,Suit,NewSerPro,SerPro,Now,ETime,NBids,NewBin);
                        _->
                            active_buff(Buffs,T,SubType,Suit,NewSerPro,SerPro,Now,ETime,Bids,OBin)
                    end;
                _->
                    active_buff(Buffs,T,SubType,Suit,NewSerPro,SerPro,Now,ETime,Bids,OBin)
            end;
        _->
            active_buff(Buffs,T,SubType,Suit,NewSerPro,SerPro,Now,ETime,Bids,OBin)
    end.

%%自动提交
click_auto_add(State,Now,Day)->
    click_auto_add(State,Now,Day,[]).

click_auto_add([],_Now,_Day,State)->
    State;
click_auto_add([H|T],Now,Day,State)->
    #act_pro_mgr{
        act = SubType,
        process = SerPro,
        stime = STime,
        etime = ETime,
        click = TotalHas,
        need_add = NeedAdd,
        add_per = AddPer,
        buffs = Buffs
    }=H,
    Type = ?CUSTOM_ACT_TYPE_UNITE_BUFF,
    case lib_custom_act_util:keyfind_act_condition(Type,SubType,suit) of
        {_,Suit}->
            skip;
        _->
            Suit = 1
    end,
    case data_custom_unite_buff:get_ctrl_cfg(Suit,Day) of
        #base_unite_buff_ctrl{check_time=CheckT,check_cum=CheckC,check_max=CheckM}->
            skip;
        _->
            CheckT=100,CheckC=0,CheckM=0
    end,

    TotalT = ETime-STime,
    MaxNeed = data_custom_unite_buff:get_max_dot(Suit),
    BeginT = STime+util:ceil(TotalT*CheckT/100),
    EndCheckC = util:ceil(CheckC/100*MaxNeed),
    EndCheckM = util:ceil(CheckM/100*MaxNeed),
    RealAdd = min(AddPer,NeedAdd),
    case Now>=BeginT andalso TotalHas<EndCheckC of
        true when SerPro < EndCheckM andalso NeedAdd>0 ->
            NewTotalHas = TotalHas+AddPer,
            NewSerPro   = SerPro+RealAdd,
            NewNeedAdd  = max(NeedAdd-RealAdd,0),
            IsAdd = true;
        _->
            NewTotalHas = TotalHas,
            NewSerPro   = SerPro,
            NewNeedAdd  = NeedAdd,
            IsAdd = false
    end,

    case IsAdd of
        true->
            Dots  =  data_custom_unite_buff:get_suit_dot(Suit),
            {NewBuffs,Bids,Bin} = active_buff(Buffs,Dots,SubType,Suit,NewSerPro,SerPro,Now,ETime,[],<<>>),
            Bids /= [] andalso lib_player:apply_cast_online(custom_unite_buff, sync_unite_buff, [SubType,Now,NewBuffs,Bin]),
            {ok,Bin0} = pt_141:write(14103,[SubType,NewSerPro]),
            lib_server_send:send_to_all(Bin0),
            trigger_effect(Bids);
        _->
            NewBuffs = Buffs
    end,
    NewH = H#act_pro_mgr{process=NewSerPro,buffs=NewBuffs,click=NewTotalHas,need_add=NewNeedAdd},
    IsAdd>0 andalso db_save_act_mgr(NewH),
    click_auto_add(T,Now,Day,[NewH|State]).

%%汇总下次需要自动提交数据
calc_zone_data(State,LZone,Day)->
    calc_zone_data(State,LZone,Day,[]).

calc_zone_data([],_LZone,_Day,NState)->
    NState;
calc_zone_data([H|T],LZone,Day,State)->
    #act_pro_mgr{
        act = SubType
    }=H,
    TimeZones = get_time_zones(SubType),
    Type = ?CUSTOM_ACT_TYPE_UNITE_BUFF,
    case lib_custom_act_util:keyfind_act_condition(Type,SubType,suit) of
        {_,Suit}->
            skip;
        _->
            Suit = 1
    end,
    case data_custom_unite_buff:get_ctrl_cfg(Suit,Day) of
        #base_unite_buff_ctrl{ratio=Ratio}->
            skip;
        _->
            Ratio = 0
    end,

    case lists:keytake(LZone,#time_zone.zone,TimeZones) of
        {value,#time_zone{roles=Roles},NewTimeZones}->
            NeedAdd = calc_add(Roles,Ratio,0);
        _->
            NeedAdd = 0,NewTimeZones=TimeZones
    end,
    AddPer = util:ceil(NeedAdd/?CALC_ZONE),
    NewH = H#act_pro_mgr{need_add=NeedAdd,add_per=AddPer},
    save_time_zones(SubType,NewTimeZones),
    NeedAdd>0 andalso db_save_act_mgr(NewH),
    calc_zone_data(T,LZone,Day,[NewH|State]).


calc_add([],_,Add)->
    Add;
calc_add([H|T],Ratio,Add)->
    #zone_log{
        live = Live,
        add = RAdd
    }=H,
    Value = util:ceil(RAdd*Ratio/100*Live/75),
    calc_add(T,Ratio,Add+Value).

%%更新提交日志
update_time_zones(Zone,RoleID,Live,Add,TimeZones)->
    case lists:keyfind(Zone,#time_zone.zone,TimeZones) of
        #time_zone{roles=Roles}->
            NRoles = case lists:keyfind(RoleID,#zone_log.rid,Roles) of
                #zone_log{add=OldV}->
                    lists:keystore(RoleID,#zone_log.rid,Roles,#zone_log{rid=RoleID,live=Live,add=Add+OldV});
                _->
                    [#zone_log{rid=RoleID,live=Live,add=Add}|Roles]
            end,
            TZone = #time_zone{zone=Zone,roles=NRoles},
            lists:keystore(Zone,#time_zone.zone,TimeZones,TZone);
        _->
            [#time_zone{zone=Zone,roles=[#zone_log{rid=RoleID,live=Live,add=Add}]}|TimeZones]
    end.

get_time_zones(SubType)->
    case get({time_zones,SubType}) of
        TimeZones when is_list(TimeZones)->
            TimeZones;
        _->
            []
    end.

save_time_zones(SubType,TimeZones)->
    put({time_zones,SubType},TimeZones).


make_act_mgr([],State)->
    State;
make_act_mgr([[Act,STime,ETime,SerPro,Click,NeedAdd,AddPer,Buffs0]|T],State)->
    Buffs1 = util:to_term(Buffs0),
    Buffs  = to_buffs(Buffs1,[]),
    ActProMgr = #act_pro_mgr{
        act = Act,
        stime = STime,
        etime = ETime,
        process = SerPro,
        click = Click,
        need_add = NeedAdd,
        add_per = AddPer,
        buffs = Buffs
    },
    make_act_mgr(T,[ActProMgr|State]).

to_buffs([],Buffs)->
    Buffs;
to_buffs([{Dot,Bid,STime,ETime}|T],Buffs)->
    to_buffs(T,[#unite_buff{dot=Dot,bid=Bid,stime=STime,etime=ETime}|Buffs]).

db_save_act_mgr(ActProMgr)->
    #act_pro_mgr{
        act = Act,
        stime = STime,
        etime = ETime,
        process = SerPro,
        click = Click,
        need_add = NeedAdd,
        add_per = AddPer,
        buffs = Buffs0
    }=ActProMgr,
    Buffs1  =  [{Dot,Bid,STimeB,ETimeB}||#unite_buff{dot=Dot,bid=Bid,stime=STimeB,etime=ETimeB}<-Buffs0],
    Buffs   =   util:term_to_string(Buffs1),
    Sql = io_lib:format(?SQL_UNITE_ACT_INSERT,[Act,STime,ETime,SerPro,Click,NeedAdd,AddPer,Buffs]),
    db:execute(Sql).

db_update_act_mgr(ActProMgr)->
    #act_pro_mgr{
        act = Act,
        process = SerPro,
        buffs = Buffs0
    }=ActProMgr,
    Buffs1  =  [{Dot,Bid,STime,ETime}||#unite_buff{dot=Dot,bid=Bid,stime=STime,etime=ETime}<-Buffs0],
    Buffs   =   util:term_to_string(Buffs1),
    Sql = io_lib:format(?SQL_UNITE_ACT_UPDATE,[SerPro,Buffs,Act]),
    db:execute(Sql).

trigger_effect([])->
    ok;
trigger_effect([Bid|T])->
    case data_custom_unite_buff:get_buff(Bid) of
        #base_unite_buff{kind=11}->
            reward_boss_mgr:reborn_all_boss();
        _->
            skip
    end,
    trigger_effect(T).