%% -----------------------------------
%% desc     :万物宝典-契约通行证
%% module   :custom_act_contract.erl
%% author   :hh
%% time     :20/08/26
%% -----------------------------------

-module(custom_act_contract).


-include("custom_act.hrl").
-include("server.hrl").
-include("contract.hrl").
-include("common.hrl").
-include("errcode.hrl").
-include("def_fun.hrl").
-include("predefine.hrl").
-include("figure.hrl").
-include("def_goods.hrl").
-include("rec_event.hrl").
-include("def_event.hrl").
-include("def_module.hrl").
-include("goods.hrl").

-export([
    contract_info/2
    ,contract_task_info/2
    ,contract_score_reward/4
    ,contract_lv_reward/4
    ,buy_ace_contract/4
    ,buy_contract_lv/4
    ]).

-export([
    check/1
     ,trigger_contract_task/3
     ,trigger_contract_task/4
     ,do_trigger_contract_task/4
     ,handle_event/2
     ,refresh_task_when_lv_change/4
     ,add_score_by_goods/3
     ,use_goods/3
     ,recharge_ace_contract/2

     ,gm_reset/1
     ,gm_add_score/3
    ,update_score_lv/2
    ]).

handle_event(Player, #event_callback{type_id = ?EVENT_LV_UP}) ->
    #player_status{id = RoleId, figure = #figure{lv = RoleLv}} = Player,
    Type = ?CUSTOM_ACT_TYPE_CONTRACT,
    case lib_custom_act_util:get_open_subtype_list(Type) of
        ActInfos = [_|_] ->
            OpenDay = util:get_open_day(),
            lists:map(fun(#act_info{key = {_, SubType}, stime = Stime, etime = EndTime}) ->
                case lib_custom_act_util:get_act_cfg_info(Type, SubType) of
                    #custom_act_cfg{condition = Cons} ->
                        case lib_custom_act_check:check_act_condtion([lv], Cons) of
                            [LvCfg|_] ->
                                if 
                                    RoleLv >= LvCfg ->
                                        CustomData = lib_custom_act_data:custom_act_data(Type, SubType),
                                        #custom_act_data{act_data = ActData} = CustomData,
                                        NewActData  = check_contract_init(RoleId, ActData, SubType, Stime, EndTime),
                                        TaskActData = check_task_update(NewActData, RoleLv, OpenDay),
                                        ?IF(TaskActData =/= ActData, lib_custom_act_data:save_custom_data(RoleId, CustomData#custom_act_data{act_data = TaskActData}), skip);
                                    true ->
                                        skip
                                end;
                            _ -> skip
                        end;
                    _ -> skip
                end
            end, ActInfos);
        _ -> skip
    end,
    {ok, Player};

handle_event(Player, #event_callback{type_id = ?EVENT_ZERO_CLOCK}) ->
    #player_status{id = RoleId, figure = #figure{lv = RoleLv}} = Player,
    Type = ?CUSTOM_ACT_TYPE_CONTRACT,
    case lib_custom_act_util:get_open_subtype_list(Type) of
        ActInfos = [_|_] ->
            OpenDay = util:get_open_day(),
            lists:map(fun(#act_info{key = {_, SubType}, stime = Stime, etime = EndTime}) ->
                CustomData = lib_custom_act_data:custom_act_data(Type, SubType),
                #custom_act_data{act_data = ActData} = CustomData,
                NewActData  = check_contract_init(RoleId, ActData, SubType, Stime, EndTime),
                TaskActData = check_task_update(NewActData, RoleLv, OpenDay),
                ?IF(TaskActData =/= ActData, lib_custom_act_data:save_custom_data(RoleId, CustomData#custom_act_data{act_data = TaskActData}), skip)
            end, ActInfos);
        _ -> skip
    end,
    {ok, Player};

handle_event(Player, #event_callback{type_id = ?EVENT_LOGIN_CAST}) ->
    #player_status{id = RoleId, figure = #figure{lv = RoleLv}} = Player,
    Type = ?CUSTOM_ACT_TYPE_CONTRACT,
    case lib_custom_act_util:get_open_subtype_list(Type) of
        ActInfos = [_|_] ->
            OpenDay = util:get_open_day(),
            lists:map(fun(#act_info{key = {_, SubType}, stime = Stime, etime = EndTime}) ->
                CustomData = lib_custom_act_data:custom_act_data(Type, SubType),
                #custom_act_data{act_data = ActData} = CustomData,
                NewActData  = check_contract_init(RoleId, ActData, SubType, Stime, EndTime),
                TaskActData = check_task_update(NewActData, RoleLv, OpenDay),
                ?IF(TaskActData =/= ActData, lib_custom_act_data:save_custom_data(RoleId, CustomData#custom_act_data{act_data = TaskActData}), skip)
            end, ActInfos),
            do_trigger_contract_task(Player, login, ?CONTRACT_FIN_TYPE_1, [1]);
        _ -> skip
    end,
    {ok, Player}.

gm_reset(Player) ->
    #player_status{id = RoleId} = Player,
    case lib_custom_act_util:get_open_subtype_list(?CUSTOM_ACT_TYPE_CONTRACT) of
        ActInfos = [_|_] ->
            lists:map(fun(#act_info{key = {_, SubType}}) ->
                CustomData = lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
                #custom_act_data{act_data = ActData} = CustomData,
                case ActData of
                    #role_contract{} ->
                        NewActData = ActData#role_contract{
                            score          = 0,              %% 积分
                            daily_score    = 0,              %% 今日累计积分
                            time           = 0,              %% 积分刷新时间
                            lv             = 0,              %% 等级
                            daily_task     = [],             %% 单日任务进度[{任务id,进度,积分领取状态}, ...]
                            daily_time     = 0,              %% 单日任务刷新时间
                            week_task      = [],             %% 单周任务进度
                            week_time      = 0,              %% 单周任务刷新时间
                            pay_contract   = 0,              %% 0 普通宝典|非0 王牌宝典
                            normal_reward  = [],             %% 普通宝典 已领取等级[lv,...]
                            pay_reward     = []              %% 付费宝典 已领取等级[lv,...]
                        },
                        lib_custom_act_data:save_custom_data(RoleId, CustomData#custom_act_data{act_data = NewActData});
                    _ ->
                        skip
                end
            end, ActInfos);
        _ ->
            skip
    end.

gm_add_score(Player, SubType, ScoreAdd) ->
    #player_status{id = RoleId} = Player,
    case lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_CONTRACT, SubType) of
        #act_info{} ->
            CustomData = lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
            #custom_act_data{act_data = ActData} = CustomData,
            case ActData of
                #role_contract{score = Score, lv = Lv} ->
                    {NewLv, NewScore} = update_score_lv(Score+ScoreAdd, Lv),
                    NewActData = ActData#role_contract{lv = NewLv, score = NewScore},
                    NewCustomData = CustomData#custom_act_data{act_data = NewActData},
                    lib_custom_act_data:save_custom_data(RoleId, NewCustomData);
                _ ->
                    skip
            end;
        _ ->
            skip
    end.

check_task_update(ActData, RoleLv, OpenDay) ->
    #role_contract{daily_task = DailyTask, week_task = WeekTask} = ActData,
    Day = utime:day_of_week(),
    CfgDailyTasks = data_contract:get_tasks(?CONTRACT_DAILY_TASK, Day),
    CfgWeekTasks  = data_contract:get_tasks(?CONTRACT_WEEK_TASK, 0),
    NewDailyTask  = refresh_task_when_lv_change(RoleLv, OpenDay, CfgDailyTasks, DailyTask),
    NewWeekTask   = refresh_task_when_lv_change(RoleLv, OpenDay, CfgWeekTasks, WeekTask),
    % ?ERR("~p ~p", [NewDailyTask, NewWeekTask]),
    ActData#role_contract{daily_task = NewDailyTask, week_task = NewWeekTask}.

refresh_task_when_lv_change(RoleLv, OpenDay, CfgTasks, Tasks) ->
    FilterCfgTasks = lists:filter(fun({TaskId, _Require, _Score}) -> 
        #base_contract_task_type{open_lv = CfgLv, open_day = CfgOpenDay} = data_contract:get_task_type(TaskId),
        RoleLv >= CfgLv andalso  OpenDay >= CfgOpenDay
    end, CfgTasks),
    lists:foldl(fun({TaskIdX, RequireX, _ScoreX}, TempTasks) ->  
        case lists:keyfind(TaskIdX, 1, TempTasks) of
            {_, _Progress, _, _} ->
                TempTasks;
            _ ->
                lists:keystore(TaskIdX, 1, TempTasks, {TaskIdX, 0, RequireX, ?CONTRACT_REWARD_0})
        end
    end, Tasks, FilterCfgTasks).

contract_info(Player, SubType) ->
    #player_status{id = RoleId, sid = Sid} = Player,
    CheckL = [
        {act_is_open, SubType}
    ],
    case util:check_lists(?MODULE, CheckL) of
        true ->
            #custom_act_data{act_data = ActData} = CustomActData = lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
            #act_info{stime = ActStartTime, etime = EndTime} = lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
            NewActData = check_contract_init(RoleId, ActData, SubType, ActStartTime, EndTime),
            ?IF(NewActData =/= ActData, lib_custom_act_data:save_custom_data(RoleId, CustomActData#custom_act_data{act_data = NewActData}), skip),
            {ok, BinData} = pack_contract_info(SubType, NewActData),
            lib_server_send:send_to_sid(Sid, BinData);
        {false, Errcode} ->
            {ok, BinData} = pt_332:write(33232, [Errcode, SubType, 0, 0, 0, 0, "", "", []]),
            lib_server_send:send_to_sid(Sid, BinData)
    end.

contract_task_info(Player, SubType) ->
    #player_status{id = RoleId, sid = Sid} = Player,
    CheckL = [
        {act_is_open, SubType}
    ],
    case util:check_lists(?MODULE, CheckL) of
        true ->
            #custom_act_data{act_data = ActData} = CustomActData = lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
            #act_info{stime = ActStartTime, etime = EndTime} = lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
            NewActData = check_contract_init(RoleId, ActData, SubType, ActStartTime, EndTime),
            ?IF(NewActData =/= ActData, lib_custom_act_data:save_custom_data(RoleId, CustomActData#custom_act_data{act_data = NewActData}), skip),
            {ok, BinData} = pack_contract_task_info(SubType, NewActData),
            lib_server_send:send_to_sid(Sid, BinData),
            ok;
        {false, _Errcode} ->
            {ok, BinData} = pt_332:write(33233, [SubType, 0, []]),
            lib_server_send:send_to_sid(Sid, BinData)
    end.

%% 任务积分奖励领取
contract_score_reward(Player, SubType, Type, TaskId) ->
    #player_status{id = RoleId, sid = Sid, figure = #figure{name = RoleName}} = Player,
    CheckL = [
        {act_is_open, SubType},
        {task_cfg, SubType, Type, TaskId},
        {task_is_finish, SubType, Type, TaskId}
    ],
    case util:check_lists(?MODULE, CheckL) of
        true ->
            #custom_act_data{act_data = ActData} = CustomActData = lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
            #role_contract{
                daily_task = DailyTask, daily_time = DailyTime, 
                week_task = WeekTask, week_time = WeekTime, 
                score = Score, daily_score = OldDailyScore, lv = Lv
            } = ActData,
            Day = utime:day_of_week(),
            Now = utime:unixtime(),
            if Type == ?CONTRACT_DAILY_TASK ->
                    #base_contract_pass_task{point = ScoreAdd} = data_contract:get_task_info(Type, Day, TaskId),
                    {_, _Progress, _Require, _Status} = lists:keyfind(TaskId, 1, DailyTask),
                    NewDailyTask = lists:keystore(TaskId, 1, DailyTask, {TaskId, _Progress, _Require, ?CONTRACT_REWARD_2}),
                    NewDailyTime = Now,
                    NewWeekTask = WeekTask, 
                    NewWeekTime = WeekTime;
                true ->
                    #base_contract_pass_task{point = ScoreAdd} = data_contract:get_task_info(Type, 0, TaskId),
                    {_, _Progress, _Require, _Status} = lists:keyfind(TaskId, 1, WeekTask),
                    NewWeekTask = lists:keystore(TaskId, 1, WeekTask, {TaskId, _Progress, _Require, ?CONTRACT_REWARD_2}),
                    NewWeekTime = Now,
                    NewDailyTask = DailyTask, 
                    NewDailyTime = DailyTime
            end,
            {NewLv, NewScore} = update_score_lv(Score+ScoreAdd, Lv),
            NewDailyScore = round(OldDailyScore+ScoreAdd),
            % ?ERR("old ~p ~p new ~p ~p", [Lv, Score, NewLv, NewScore]),
            NewActData = ActData#role_contract{
                score = NewScore, daily_score = NewDailyScore, time = Now, lv = NewLv, 
                daily_task = NewDailyTask, daily_time = NewDailyTime, week_task = NewWeekTask, week_time = NewWeekTime
            },
            lib_custom_act_data:save_custom_data(RoleId, CustomActData#custom_act_data{act_data = NewActData}),
            {ok, BinData} = pt_332:write(33234, [?SUCCESS, SubType, NewLv, NewScore, NewDailyScore]),
            {ok, BinData1} = pt_332:write(33235, [SubType, [{Type, TaskId, _Progress, ?CONTRACT_REWARD_2}]]),
            if NewLv =/= Lv -> 
                    {ok, BinData2} = pack_contract_info(SubType, NewActData),
                    lib_server_send:send_to_sid(Sid, <<BinData/binary, BinData1/binary, BinData2/binary>>);
                true ->
                    lib_server_send:send_to_sid(Sid, <<BinData/binary, BinData1/binary>>)
            end,
            #custom_act_cfg{name = ActName} = lib_custom_act_util:get_act_cfg_info(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
            case data_contract:get_rewards(SubType, NewLv) of
                #base_contract_rewards{node_rewards = NodeReward} ->
                    NewLv =/= Lv andalso NodeReward == 1 andalso lib_chat:send_TV({all}, ?MOD_AC_CUSTOM, 7, [RoleName, ActName, NewLv, SubType]);
                _ ->
                    skip
            end,
            ok;
        {false, Errcode} ->
            {ok, BinData} = pt_332:write(33234, [Errcode, SubType, 0, 0, 0]),
            lib_server_send:send_to_sid(Sid, BinData)
    end.

%% 等级奖励领取
contract_lv_reward(Player, SubType, Type, Lv) ->
    #player_status{sid = Sid, id = RoleId, figure = #figure{lv = RoleLv, vip = Vip}} = Player,
    CheckL = [
        {act_is_open, SubType},
        {lv_reward_check, Type, Lv, SubType}
    ],
    case util:check_lists(?MODULE, CheckL) of
        true ->
            {RewardLvList, Rewards} = calc_contract_lv_reward(RoleId, Type, Lv, SubType),
            NewPlayer = lib_goods_api:send_reward(Player, Rewards, contract_lv_reward),
            {ok, BinData} = pt_332:write(33236, [?SUCCESS, SubType, RewardLvList]),
            lib_server_send:send_to_sid(Sid, BinData),
            %% 日志
            NowTime = utime:unixtime(),
            WLv     = util:get_world_lv(),
            lib_log_api:log_custom_act_reward(RoleId, Type, SubType, 2, WLv, Rewards, [],
                Vip, RoleLv, Player#player_status.gold, NewPlayer#player_status.gold, Player#player_status.bgold, NewPlayer#player_status.bgold, NowTime),
            {ok, NewPlayer};
        {false, Errcode} ->
            {ok, BinData} = pt_332:write(33236, [Errcode, SubType, []]),
            lib_server_send:send_to_sid(Sid, BinData)
    end.

%% 购买王牌宝典(常规获得)
buy_ace_contract(Player, SubType, Discount, Type) ->
    #player_status{id = RoleId, sid = Sid} = Player,
    case get_role_contract(RoleId, SubType, true) of
        {true, #act_info{key = {_, SubType}, wlv = WLv}, CustomData} ->
            #custom_act_data{act_data = ActData} = CustomData,
            CheckL = [
                {buy_cost_check, Player, Discount, Type, SubType},
                {payed_check, ActData}
            ],
            case util:check_lists(?MODULE, CheckL) of
                true ->
                    buy_ace_contract_do(Player, CustomData, Discount, Type, SubType, WLv);
                {false, Errcode} -> %?ERR("~p", [Errcode]),
                    {ok, BinData} = pt_332:write(33237, [Errcode, SubType, 0]),
                    lib_server_send:send_to_sid(Sid, BinData)
            end;
        {false, Errcode} -> %?ERR("~p", [Errcode]),
            {ok, BinData} = pt_332:write(33237, [Errcode, SubType, 0]),
            lib_server_send:send_to_sid(Sid, BinData)        
    end.

%% 直接购买等级
buy_contract_lv(Player, StartLv, EndLv, SubType) ->
    #player_status{id = RoleId, sid = Sid} = Player,
    case get_role_contract(RoleId, SubType, true) of
        {true, #act_info{key = {_, SubType}}, CustomData} ->
            #custom_act_data{act_data = ActData} = CustomData,
            CheckL = [
                {buy_lv_check, Player, ActData, StartLv, EndLv, SubType}
            ],
            case util:check_lists(?MODULE, CheckL) of
                true -> 
                    buy_contract_lv_do(Player, CustomData, StartLv, EndLv, SubType);
                {false, Errcode} ->
                    {ok, BinData} = pt_332:write(33290, [Errcode, SubType]),
                    lib_server_send:send_to_sid(Sid, BinData)
            end;
        {false, Errcode} ->
            {ok, BinData} = pt_332:write(33290, [Errcode, SubType]),
            lib_server_send:send_to_sid(Sid, BinData)
    end.

buy_ace_contract_do(Player, _, Discount, Type, SubType, WLv) ->
    #player_status{id = RoleId, sid = Sid, figure = #figure{name = Name, vip = Vip, lv = Lv}} = Player,
    {Rewards, CostL} = calc_buy_ace_contract_cost(Discount, Type, SubType),
    % ?ERR("~p ~p", [Rewards, CostL]),
    case lib_goods_api:cost_object_list_with_check(Player, CostL, ace_contract_cost, "") of
        {true, NewPlayer, _} ->
            if Rewards =/= [] ->
                    NewPlayer1 = lib_goods_api:send_reward(NewPlayer, Rewards, ace_contract_luxury_reward);
                true ->
                    NewPlayer1 = NewPlayer
            end,
            #custom_act_data{act_data = ActData} = CustomData = lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
            #role_contract{lv = NewLv, score = NewScore, daily_score = NewDailyScore} = NewActData = ActData#role_contract{pay_contract = 1},
            NewCustomData = CustomData#custom_act_data{act_data = NewActData},
            lib_custom_act_data:save_custom_data(RoleId, NewCustomData),
            {ok, BinData} = pt_332:write(33237, [?SUCCESS, SubType, 1]),
            {ok, BinData1} = pt_332:write(33234, [?SUCCESS, SubType, NewLv, NewScore, NewDailyScore]),
            {ok, BinData2} = pack_contract_info(SubType, NewActData),
            lib_server_send:send_to_sid(Sid, <<BinData/binary, BinData1/binary, BinData2/binary>>),
            #custom_act_cfg{name = ActName} = lib_custom_act_util:get_act_cfg_info(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
            lib_chat:send_TV({all}, ?MOD_AC_CUSTOM, 6, [Name, ActName, SubType]),
            custom_act_mobilize:update_mobilize_task(Player, passport, 1),
            %% 日志
            NowTime = utime:unixtime(),
            lib_log_api:log_custom_act_reward(RoleId, Type, SubType, 1, WLv, Rewards, [],
                Vip, Lv, Player#player_status.gold, NewPlayer#player_status.gold, Player#player_status.bgold, NewPlayer#player_status.bgold, NowTime),
            {ok, NewPlayer1};
        {false, Errcode, _} ->
            {ok, BinData} = pt_332:write(33237, [Errcode, SubType, 0]),
            lib_server_send:send_to_sid(Sid, BinData),
            ok
    end.

buy_contract_lv_do(Player, _CustomData, StartLv, EndLv, SubType) ->
    #player_status{sid = Sid} = Player,
    CostList = calc_buy_lv_cost(StartLv, EndLv, SubType),
    case lib_goods_api:cost_object_list_with_check(Player, CostList, contract_lv_buy, "") of
        {true, NewPlayer, _} ->
            Rewards = calc_contract_lv_score(StartLv, EndLv, SubType),
            NewPlayer1 = lib_goods_api:send_reward(NewPlayer, Rewards, contract_lv_score_reward),
            lib_server_send:send_to_sid(Sid, pt_332, 33290, [?SUCCESS, SubType]),
            {ok, NewPlayer1};
        {false, Errcode, _} ->
            {ok, BinData} = pt_332:write(33290, [Errcode, SubType]),
            lib_server_send:send_to_sid(Sid, BinData),
            ok
    end.

%% 契约直购(直购获得)
recharge_ace_contract(Player, Condition) ->
    #player_status{id = RoleId, sid = Sid, figure = #figure{name = Name, vip = Vip, lv = Lv}} = Player,
    Type = ?CUSTOM_ACT_TYPE_CONTRACT,
    case Condition of
        [{custom_act, Type, SubType, Grade}|_] ->
            case get_role_contract(RoleId, SubType, true) of
                {true, #act_info{key = {_, SubType}, wlv = WLv}, CustomData} ->
                    #custom_act_data{act_data = ActData} = CustomData,
                    #role_contract{pay_contract = IsPay} = ActData,
                    if IsPay =/= 0 ->
                            ?ERR("role ~p ace contract have payed", [RoleId]),
                            {ok, Player};
                        true ->
                            #base_contract_pass_buy{
                                luxury_rewards = LuxuryReward, buy_type = BuyType
                            } = data_contract:get_pass_buy_info(SubType),
                            if BuyType == 1 ->
                                    if Grade == 2 -> %% 至尊
                                            Rewards = LuxuryReward,
                                            NewPlayer = lib_goods_api:send_reward(Player, Rewards, ace_contract_luxury_reward);
                                        true -> %% 普通
                                            Rewards = [],
                                            NewPlayer = Player
                                    end,
                                    NewCustomData = lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
                                    #custom_act_data{act_data = NewActData} = NewCustomData,
                                    NewActData1 = NewActData#role_contract{pay_contract = 1},
                                    NewCustomData1 = NewCustomData#custom_act_data{act_data = NewActData1},
                                    lib_custom_act_data:save_custom_data(RoleId, NewCustomData1),
                                    #custom_act_cfg{name = ActName} = lib_custom_act_util:get_act_cfg_info(Type, SubType),
                                    {ok, BinData} = pt_332:write(33237, [?SUCCESS, SubType, 1]),
                                    {ok, BinData1} = pt_332:write(33234, [?SUCCESS, SubType, NewActData1#role_contract.lv, NewActData1#role_contract.score, NewActData1#role_contract.daily_score]),
                                    {ok, BinData2} = pack_contract_info(SubType, NewActData1),
                                    lib_server_send:send_to_sid(Sid, <<BinData/binary, BinData1/binary, BinData2/binary>>),
                                    % lib_server_send:send_to_sid(Sid, pt_332, 33237, [?SUCCESS, SubType, 1]),
                                    lib_chat:send_TV({all}, ?MOD_AC_CUSTOM, 6, [Name, ActName, SubType]),
                                    custom_act_mobilize:update_mobilize_task(Player, passport, 1),
                                    %% 日志
                                    NowTime = utime:unixtime(),
                                    lib_log_api:log_custom_act_reward(RoleId, Type, SubType, 1, WLv, Rewards, [],
                                        Vip, Lv, Player#player_status.gold, NewPlayer#player_status.gold, Player#player_status.bgold, NewPlayer#player_status.bgold, NowTime),
                                    {ok, NewPlayer};
                                true -> %% 常规购买
                                    ?ERR("rechare cfg ~p and contract cfg ~p conflict", [Condition, SubType]),
                                    {ok, Player}
                            end
                    end;
                {false, _Errcode} ->
                    ?ERR("recharge contract error ~p", [{RoleId, _Errcode}]),
                    {ok, Player}
            end;
        _ ->
            ?ERR("recharge contract error ~w", [{RoleId, Condition}]),
            {ok, Player}
    end.

%% 计算王牌宝典消耗
calc_buy_ace_contract_cost(Discount, Type, SubType) ->
    #base_contract_pass_buy{
        normal_price = NormalPrice, luxury_price = LuxuryPrice, luxury_rewards = LuxuryReward, discount = DiscountL
    } = data_contract:get_pass_buy_info(SubType),
    Price = ?IF(Type == 0, NormalPrice, LuxuryPrice),
    if Discount == 0 ->
            CostL = [{?TYPE_GOLD, 0, Price}];
        true ->
            [DisGoodsId, DiscountArg|_] = DiscountL,
            NewPrice = umath:floor(DiscountArg*Price),
            CostL = [{?TYPE_GOLD, 0, NewPrice}, {?TYPE_GOODS, DisGoodsId, 1}]
    end,
    Rewards = ?IF(Type == 1, LuxuryReward, []),
    {Rewards, CostL}.

get_role_contract(RoleId, SubType, UpdateType) ->
    case lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_CONTRACT, SubType) of
        ActInfo = #act_info{key = {_, SubType}, stime = Stime, etime = EndTime} ->
            CustomData = lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
            #custom_act_data{act_data = ActData} = CustomData,
            case ActData of
                #role_contract{} ->
                    {true, ActInfo, CustomData};
                _ ->
                    NewActData = #role_contract{},
                    NewActData1 = check_contract_init(RoleId, NewActData, SubType, Stime, EndTime),
                    NewCustomData = CustomData#custom_act_data{act_data = NewActData1},
                    ?IF(NewActData1 =/= NewActData andalso UpdateType == true, lib_custom_act_data:save_custom_data(RoleId, NewCustomData), skip),
                    {true, ActInfo, NewCustomData}
            end;
        _ ->
            {false, ?ERRCODE(err331_act_closed)}
    end.

calc_contract_lv_reward(RoleId, Type, Lv, SubType) ->
    #custom_act_data{act_data = ActData} = CustomActData = lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
    #role_contract{
        normal_reward  = NormalReward, 
        pay_reward     = PayReward,
        pay_contract   = IsPay,
        lv             = NowLv
    } = ActData,
    {NewLvChangeL, NewLvRewards} =
    if
        Type == 1 ->
            #base_contract_rewards{normal_rewards = Rewards1, luxury_rewards = Rewards2} = data_contract:get_rewards(SubType, Lv),
            if IsPay =/= 0 ->
                    NormalCanReward = calc_per_lv_reward(Lv, NormalReward),
                    PayCanReward    = calc_per_lv_reward(Lv, PayReward),
                    NewRewards  = update_lv_reward(NormalCanReward, Rewards1, []),
                    NewRewards1 = update_lv_reward(PayCanReward, Rewards2, NewRewards),
                    LvChangeL   = update_lv_change_list(Lv, NormalCanReward, PayCanReward, []),
                    {LvChangeL, NewRewards1};
                true ->
                    NormalCanReward = calc_per_lv_reward(Lv, NormalReward),
                    NewRewards  = update_lv_reward(NormalCanReward, Rewards1, []),
                    LvChangeL   = update_lv_change_list(Lv, NormalCanReward, false, []),
                    {LvChangeL, NewRewards}
            end;
        true ->
            LvList = lists:seq(1, NowLv),
            lists:foldl(fun(LvX, {TempLvChangeL, TempRewards}) ->
                #base_contract_rewards{normal_rewards = Rewards1, luxury_rewards = Rewards2} = data_contract:get_rewards(SubType, LvX),
                if IsPay =/= 0 ->
                        NormalCanReward = calc_per_lv_reward(LvX, NormalReward),
                        PayCanReward    = calc_per_lv_reward(LvX, PayReward),
                        NewTempRewards  = update_lv_reward(NormalCanReward, Rewards1, TempRewards),
                        NewTempRewards1 = update_lv_reward(PayCanReward, Rewards2, NewTempRewards),
                        NewTempLvChangeL = update_lv_change_list(LvX, NormalCanReward, PayCanReward, TempLvChangeL),
                        {NewTempLvChangeL, NewTempRewards1};
                    true ->
                        NormalCanReward = calc_per_lv_reward(LvX, NormalReward),
                        NewTempRewards  = update_lv_reward(NormalCanReward, Rewards1, TempRewards),
                        NewTempLvChangeL = update_lv_change_list(LvX, NormalCanReward, false, TempLvChangeL),
                        {NewTempLvChangeL, NewTempRewards}
                end
            end, {[], []}, LvList)
    end,
    NewActData = update_contract_lv_reward_state(NewLvChangeL, ActData),
    ?IF(NewActData =/= ActData, lib_custom_act_data:save_custom_data(RoleId, CustomActData#custom_act_data{act_data = NewActData}), skip),
    {NewLvChangeL, NewLvRewards}.

update_contract_lv_reward_state(LvChangeL, ActData) ->
    #role_contract{normal_reward = NormalReward, pay_reward = PayReward} = ActData,
    {NewNormalReward, NewPayReward} = lists:foldl(fun({Lv, ChangeType}, {TempNormalReward, TempPayReward}) -> 
        if ChangeType == 1 -> %% 普通
                {[Lv|TempNormalReward], TempPayReward};
            ChangeType == 2 -> %% 王牌
                {TempNormalReward, [Lv|TempPayReward]};
            true -> %% 都有
                {[Lv|TempNormalReward], [Lv|TempPayReward]}
        end
    end, {NormalReward, PayReward}, LvChangeL),
    ActData#role_contract{normal_reward = NewNormalReward, pay_reward = NewPayReward}.

calc_per_lv_reward(Lv, LvRewarded) ->
    case lists:member(Lv, LvRewarded) of
        false -> true;
        _     -> false
    end.

update_lv_reward(CanReward, AddRewards, Rewards) ->
    if CanReward ->
            AddRewards ++ Rewards;
        true ->
            Rewards
    end.

update_lv_change_list(LvX, NormalCanReward, PayCanReward, LvChangL) ->
    if NormalCanReward andalso PayCanReward ->
            [{LvX, 3}|LvChangL];
        NormalCanReward ->
            [{LvX, 1}|LvChangL];
        PayCanReward ->
            [{LvX, 2}|LvChangL];
        true ->
            LvChangL
    end.

update_score_lv(TotalScore, Lv) ->
    NewLv = Lv+1,
    LvScoreNeed = data_contract:get_lv_exp(NewLv),
    if
        LvScoreNeed == 0 ->
            PreLvScoreNeed = data_contract:get_lv_exp(Lv),
            {Lv, min(TotalScore, PreLvScoreNeed)};
        TotalScore >= LvScoreNeed ->
            NewScore1 = max(0, TotalScore - LvScoreNeed),
            update_score_lv(NewScore1, NewLv);
        true ->
            {Lv, TotalScore}
    end.


pack_contract_info(SubType, ActData) ->
    #role_contract{
        score          = Score,          %% 积分
        daily_score    = DailyScore,
        lv             = Lv,             %% 等级
        pay_contract   = IsPay,          %% 0 普通宝典|非0 王牌宝典
        normal_reward  = NormalReward,   %% 普通宝典 已领取等级[lv,...]
        pay_reward     = PayReward       %% 付费宝典 已领取等级[lv,...]
    } = ActData,
    RewardInfo = join_reward_info(SubType, Lv, IsPay, NormalReward, PayReward),
    case lib_custom_act_util:get_act_cfg_info(?CUSTOM_ACT_TYPE_CONTRACT, SubType) of
        #custom_act_cfg{desc = Desc, condition = Cons} ->
            case lists:keyfind(show, 1, Cons) of
                false ->
                    Show = [];
                ShowArg ->
                    Show = [ShowArg]
            end,
            case lists:keyfind(goods_id, 1, Cons) of
                false ->
                    Goods = [];
                GoodsArg ->
                    Goods = [GoodsArg]
            end,
            ConsArgs = util:term_to_bitstring(Show ++ Goods);
        _A -> %?ERR("~p", [_A]),
            Desc = "", ConsArgs = ""
    end,
    pt_332:write(33232, [?SUCCESS, SubType, Score, Lv, DailyScore, IsPay, ConsArgs, Desc, RewardInfo]).

pack_contract_task_info(SubType, ActData) ->
    #act_info{stime = Stime, etime = Etime} = lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
    #role_contract{
        daily_task = DailyTask,
        week_task  = WeekTask,
        week_time  = _WeekTime
    } = ActData,
    Now = utime:unixtime(),
    {_MinWeekTime, MaxWeekTime} = calc_this_week_time_range(Stime, Etime, Now),
    % LastDays = utime:day_span(Stime, WeekTime),
    % RemDay = LastDays rem 7,
    % if
    %     LastDays =/= 0 andalso RemDay == 0 ->
    %         WeekTimeMid = utime:tomorrow(WeekTime),
    %         UpdateTime = min(WeekTimeMid, Etime);
    %     true ->
    %         AfterDay = (7 - RemDay),
    %         {{Y, M, D}, _} = utime:unixtime_to_localtime(WeekTime),
    %         {EY, EM, ED} = utime:get_before_day({Y, M, D}, AfterDay, 1),
    %         UpdateTime = utime:unixtime({{EY, EM, ED}, {23, 59, 59}})
    % end,
    Tasks = join_task_info(DailyTask, WeekTask),
    % ?ERR("~p", [utime:unixtime_to_localtime(MaxWeekTime)]),
    pt_332:write(33233, [SubType, MaxWeekTime, Tasks]).

join_task_info(DailyTask, WeekTask) ->
    DailyTask1 = lists:foldl(fun({TaskId, Progress, _Require, Status}, Acc) -> 
        [{TaskId, Progress, Status}|Acc]
    end, [], DailyTask),
    WeekTask1 = lists:foldl(fun({TaskId1, Progress1, _Require1, Status1}, Acc1) -> 
        [{TaskId1, Progress1, Status1}|Acc1]
    end, [], WeekTask),
    [{?CONTRACT_DAILY_TASK, DailyTask1}, {?CONTRACT_WEEK_TASK, WeekTask1}].

join_reward_info(SubType, Lv, IsPay, NormalReward, PayReward) ->
    Lvs = data_contract:get_sub_type_lv(SubType),
    lists:foldl(fun(LvX, Acc) ->
        if LvX =< Lv -> 
                NormalStatus =
                case lists:member(LvX, NormalReward) of
                    true -> ?CONTRACT_REWARD_2;
                    _    -> ?CONTRACT_REWARD_1
                end,
                PayStatus = 
                case lists:member(LvX, PayReward) of
                    true -> ?CONTRACT_REWARD_2;
                    _ ->
                        if IsPay == 1 -> ?CONTRACT_REWARD_1;
                            true      -> ?CONTRACT_REWARD_0
                        end
                end,
                [{LvX, NormalStatus, PayStatus}|Acc];
            true ->
                Acc
        end
    end, [], Lvs).

%% 任务初始化
contract_init(SubType, RoleLv, _ActStartTime) ->
    DailyTask = update_task_info(?CONTRACT_DAILY_TASK, SubType, RoleLv),
    WeekTask  = update_task_info(?CONTRACT_WEEK_TASK, SubType, RoleLv),
    % ?ERR("~p ~p ~p ~p", [SubType, RoleLv, DailyTask, WeekTask]),
    Now = utime:unixtime(),
    #role_contract{
        time           = Now,
        daily_task     = DailyTask,
        daily_time     = Now,
        week_task      = WeekTask,
        week_time      = Now
    }.

check_contract_init(RoleId, ActData, SubType, ActStartTime, EndTime) ->
    RoleLv = lib_role:get_role_lv(RoleId),
    case ActData of
        #role_contract{
            daily_task = DailyTask, week_task = WeekTask,
            daily_time = DailyTime, week_time = WeekTime,
            daily_score = DailyScore, time = ScoreTime} ->
            Now = utime:unixtime(),
            IsSameDay = utime:is_same_day(Now, DailyTime),
            if DailyTask == [] orelse not IsSameDay ->
                    NewDailyTask = update_task_info(?CONTRACT_DAILY_TASK, SubType, RoleLv),
                    NewDailyTime = Now;
                true ->
                    NewDailyTask = DailyTask,
                    NewDailyTime = DailyTime
            end,
            % DiffDay = utime:day_span(ActStartTime, Now),
            {WeekMinTime, WeekMaxTime} = calc_this_week_time_range(ActStartTime, EndTime, Now),
            % ?ERR("~p ~p ~p", [WeekMinTime, WeekMaxTime, Now]),
            if 
                WeekMinTime == WeekMaxTime andalso WeekMinTime == 0 ->
                    NewWeekTask = WeekTask,
                    NewWeekTime = WeekTime;
                WeekTask == [] orelse WeekTime < WeekMinTime orelse WeekTime >= WeekMaxTime ->
                    NewWeekTask = update_task_info(?CONTRACT_WEEK_TASK, SubType, RoleLv),
                    NewWeekTime = Now;
                true ->
                    NewWeekTask = WeekTask,
                    NewWeekTime  = WeekTime
            end,
            IsSameDay1 = utime:is_same_day(Now, ScoreTime),
            if not IsSameDay1 ->
                    NewDailyScore = 0, NewScoreTime = Now;
                true ->
                    NewDailyScore = DailyScore, NewScoreTime = ScoreTime
            end,
            % ?ERR("~p ~p", [IsSameDay, DiffDay]),
            ActData#role_contract{
                daily_task = NewDailyTask, daily_time = NewDailyTime,
                week_task = NewWeekTask, week_time = NewWeekTime,
                daily_score = NewDailyScore, time = NewScoreTime
            };
        _ -> %% 未初始化
            contract_init(SubType, RoleLv, ActStartTime)
    end.

%% 计算当前区间所处范围
calc_this_week_time_range(ActStartTime, ActEndTime, Now) ->
    {{Y, M, D}, _} = utime:unixtime_to_localtime(ActStartTime),
    AfterDay = utime:get_before_day({Y, M, D}, 6, 1),
    AfterTime = utime:unixtime({AfterDay, {23, 59, 59}}),
    TarEndTime = min(AfterTime, ActEndTime),
    calc_this_week_time_range_do(ActStartTime, TarEndTime, Now, ActEndTime).

calc_this_week_time_range_do(StartTime, EndTime, Now, ActEndTime) ->
    if
        Now >= StartTime andalso Now < EndTime ->
            {StartTime, EndTime};
        true ->
            if ActEndTime == EndTime ->
                    {0, 0};
                true ->
                    NewStartTime = utime:tomorrow(EndTime),
                    {{Y, M, D}, _} = utime:unixtime_to_localtime(NewStartTime),
                    AfterDay = utime:get_before_day({Y, M, D}, 6, 1),
                    NewEndTime = utime:unixtime({AfterDay, {23, 59, 59}}),
                    TarEndTime = min(NewEndTime, ActEndTime),
                    calc_this_week_time_range_do(NewStartTime, TarEndTime, Now, ActEndTime)
            end
    end.

update_task_info(Type, _SubType, RoleLv) ->
    Day     = utime:day_of_week(),
    OpenDay = util:get_open_day(),
    CfgType =
    if
        Type == ?CONTRACT_DAILY_TASK ->
            Day;
        true ->
            0
    end,
    %% 任务类型id,任务进度,任务积分
    CfgTask = data_contract:get_tasks(Type, CfgType),
    lists:foldl(fun({TaskId, Require, _Score}, Acc) -> 
        case data_contract:get_task_type(TaskId) of
            #base_contract_task_type{open_lv = LvCfg, open_day = OpenDayCfg} ->
                case OpenDay >= OpenDayCfg andalso RoleLv >= LvCfg of
                    true ->
                        [{TaskId, 0, Require, ?CONTRACT_REWARD_0}|Acc];
                    _ ->
                        Acc
                end;
            _ ->
                Acc
        end
    end, [], CfgTask).

trigger_contract_task(RoleId, TaskType, Args) ->
    trigger_contract_task(RoleId, TaskType, ?CONTRACT_FIN_TYPE_1, Args).

-spec trigger_contract_task(integer(), atom(), integer(), list()) -> 
    atom().
trigger_contract_task(RoleId, TaskType, FinishType, Args) ->
    lib_player:apply_cast(RoleId, ?APPLY_CAST_SAVE, ?MODULE, do_trigger_contract_task, [TaskType, FinishType, Args]).

do_trigger_contract_task(Player, TaskType, FinishType, Args) ->
    #player_status{id = RoleId, sid = Sid, figure = #figure{lv = RoleLv}} = Player,
    {OpenResult, ActInfos} = check_contract_open(),
    TaskIds = data_contract:get_task_id(TaskType),
    if
        not OpenResult ->
            skip;
        TaskIds == [] ->
            skip;
        true ->
            [TaskId|_] = TaskIds,
            ConfigTaskType = data_contract:get_task_type(TaskId),
            OpenDay        = util:get_open_day(),
            if
                not is_record(ConfigTaskType, base_contract_task_type) ->
                    skip;
                ConfigTaskType#base_contract_task_type.open_lv > RoleLv orelse
                    ConfigTaskType#base_contract_task_type.open_day > OpenDay ->
                    skip; %% 等级不足或开服天数不足
                true ->
                    lists:map(fun(#act_info{key = {_, SubType}, stime = ActStartTime, etime = EndTime}) ->
                        #custom_act_data{
                            act_data = ActData
                        } = CustomData = lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_CONTRACT, SubType),
                        NewActData = check_contract_init(RoleId, ActData, SubType, ActStartTime, EndTime),
                        Day = utime:day_of_week(),
                        {MatchDaily, MatchWeek}    = check_task_match(SubType, Day, TaskType),
                        {ChangeTasks, NewActData1} = trigger_contract_task_logic(FinishType, RoleId, MatchDaily, MatchWeek, NewActData, Args),
                        NewCustomData = CustomData#custom_act_data{act_data = NewActData1},
                        ?IF(ActData =/= NewActData1,
                            lib_custom_act_data:save_custom_data(RoleId, NewCustomData), ok),
                        {ok, BinData} = pt_332:write(33235, [SubType, ChangeTasks]),
                        lib_server_send:send_to_sid(Sid, BinData),
                        ok
                    end, ActInfos)
            end
    end,
    {ok, Player}.

check_task_match(SubType, Day, TaskType) ->
    %% 当前时间阶段对应任务
    DailyTasks = data_contract:get_tasks(?CONTRACT_DAILY_TASK, Day),
    WeekTasks  = data_contract:get_tasks(?CONTRACT_WEEK_TASK, 0),
    MatchDaily = check_task_match_do(SubType, DailyTasks, TaskType),
    MatchWeek  = check_task_match_do(SubType, WeekTasks, TaskType),
    {MatchDaily, MatchWeek}.

check_task_match_do(_SubType, Tasks, TaskType) ->
    case data_contract:get_task_id(TaskType) of
        [TaskId|_] ->
            case lists:keyfind(TaskId, 1, Tasks) of
                {_, Require, Score} ->
                    [{TaskId, Require, Score}];
                _ ->
                    []
            end;
        _ ->
            []
    end.

trigger_contract_task_logic(FinishType, _RoleId, MatchDaily, MatchWeek, ActData, Args) ->
    #role_contract{
        daily_task = DailyTasks, week_task = WeekTasks,
        daily_time = DailyTime, week_time = WeekTime 
    } = ActData,
    {ChangeDaily, NewDailyTasks} = trigger_task_do(FinishType, MatchDaily, DailyTasks, Args, []),
    {ChangeWeek, NewWeekTasks}   = trigger_task_do(FinishType, MatchWeek, WeekTasks, Args, []),
    Now = utime:unixtime(),
    if 
        ChangeDaily == [] -> NewDailyTime = DailyTime;
        true -> NewDailyTime = Now
    end,
    if
        ChangeWeek == [] -> NewWeekTime = WeekTime;
        true -> NewWeekTime = Now
    end,
    ChangeTasks = join_change_task(ChangeDaily, ChangeWeek),
    NewActData = ActData#role_contract{
        daily_task = NewDailyTasks, daily_time = NewDailyTime, 
        week_task = NewWeekTasks, week_time = NewWeekTime
    },
    {ChangeTasks, NewActData}.

join_change_task(ChangeDaily, ChangeWeek) ->
    ChangeTasks1 = lists:foldl(fun({TaskId, Progress, Status}, Acc) -> 
        [{?CONTRACT_DAILY_TASK, TaskId, Progress, Status}|Acc]
    end, [], ChangeDaily),
    ChangeTasks2 = lists:foldl(fun({TaskIdX, ProgressX, StatusX}, AccX) ->
        [{?CONTRACT_WEEK_TASK, TaskIdX, ProgressX, StatusX}|AccX]
    end, ChangeTasks1, ChangeWeek),
    ChangeTasks2.

trigger_task_do(_, [], Tasks, _, ChangeTasks) ->
    {ChangeTasks, Tasks};
trigger_task_do(?CONTRACT_FIN_TYPE_1, [{TaskId, _Require0, _Score}|Rest], Tasks, [Add|_] = Args, ChangeTasks) ->
    case lists:keyfind(TaskId, 1, Tasks) of
        {_, Progress, Require, Status} when Status == ?CONTRACT_REWARD_0 ->
            NewProgress = Progress + Add,
            NewStatus = ?IF(NewProgress >= Require, ?CONTRACT_REWARD_1, Status),
            NewTasks = lists:keyreplace(TaskId, 1, Tasks, {TaskId, NewProgress, Require, NewStatus}),
            trigger_task_do(?CONTRACT_FIN_TYPE_1, Rest, NewTasks, Args, [{TaskId, NewProgress, NewStatus}|ChangeTasks]);
        _ ->
            trigger_task_do(?CONTRACT_FIN_TYPE_1, Rest, Tasks, Args, ChangeTasks)
    end;
trigger_task_do(?CONTRACT_FIN_TYPE_2, [{TaskId, _Require0, _Score}|Rest], Tasks, [NewTotal|_] = Args, ChangeTasks) ->
    case lists:keyfind(TaskId, 1, Tasks) of
        {_, _Progress, Require, Status} when Status == ?CONTRACT_REWARD_0 ->
            NewProgress = NewTotal,
            NewStatus = ?IF(NewProgress >= Require, ?CONTRACT_REWARD_1, Status),
            NewTasks = lists:keyreplace(TaskId, 1, Tasks, {TaskId, NewProgress, Require, NewStatus}),
            trigger_task_do(?CONTRACT_FIN_TYPE_2, Rest, NewTasks, Args, [{TaskId, NewProgress, NewStatus}|ChangeTasks]);
        _ ->
            trigger_task_do(?CONTRACT_FIN_TYPE_2, Rest, Tasks, Args, ChangeTasks)
    end;
trigger_task_do(?CONTRACT_FIN_TYPE_3, [{TaskId, _Require0, _Score}|Rest], Tasks, [Add|_] = Args, ChangeTasks) ->
    case lists:keyfind(TaskId, 1, Tasks) of
        {_, Progress, Require, Status} when Status == ?CONTRACT_REWARD_0 ->
            NewProgress = Progress + Add,
            {NewStatus, NewProgress1} = ?IF(NewProgress >= Require, {?CONTRACT_REWARD_1, NewProgress}, {Status, Progress}),
            NewTasks = lists:keyreplace(TaskId, 1, Tasks, {TaskId, NewProgress1, Require, NewStatus}),
            trigger_task_do(?CONTRACT_FIN_TYPE_3, Rest, NewTasks, Args, [{TaskId, NewProgress1, NewStatus}|ChangeTasks]);
        _ ->
            trigger_task_do(?CONTRACT_FIN_TYPE_3, Rest, Tasks, Args, ChangeTasks)
    end; 
trigger_task_do(_FinishType, [_|Rest], Tasks, _Args, _ChangeTasks) ->
    trigger_task_do(_FinishType, Rest, Tasks, _Args, _ChangeTasks).

check_contract_open() ->
    BaseType       = ?CUSTOM_ACT_TYPE_CONTRACT,
    case lib_custom_act_util:get_open_subtype_list(BaseType) of
        ActInfos = [#act_info{}|_] -> %% key = {_, SubType}, stime = Stime
            {true, ActInfos};
        _ ->
            {false, []}
    end.

add_score_by_goods(RoleId, GoodsId, Val) ->
    Type = ?CUSTOM_ACT_TYPE_CONTRACT,
    case lib_custom_act_util:get_open_subtype_list(Type) of
        ActInfos = [_|_] ->
            case lists:filter(fun(#act_info{key = {_, SubTypeX}}) -> 
                case lib_custom_act_util:get_act_cfg_info(Type, SubTypeX) of
                    #custom_act_cfg{condition = Cons} ->
                        case lists:keyfind(goods_id, 1, Cons) of
                            {_, GoodsId} -> true;
                            _            -> false
                        end;
                    _ -> false
                end
            end, ActInfos) of
                [#act_info{key = {_, SubType}, stime = ActStartTime, etime = EndTime}|_] ->
                    CustomData = lib_custom_act_data:custom_act_data(Type, SubType),
                    #custom_act_data{act_data = ActData} = CustomData,
                    #role_contract{score = Score, lv = Lv, daily_score = DailyScore} = NewActData = check_contract_init(RoleId, ActData, SubType, ActStartTime, EndTime),
                    {NewLv, NewScore} = update_score_lv(Score+Val, Lv),
                    RoleName = lib_role:get_role_name(RoleId),
                    #custom_act_cfg{name = ActName} = lib_custom_act_util:get_act_cfg_info(Type, SubType),
                    case data_contract:get_rewards(SubType, NewLv) of
                        #base_contract_rewards{node_rewards = NodeReward} ->    
                            NewLv =/= Lv andalso NodeReward == 1 andalso lib_chat:send_TV({all}, ?MOD_AC_CUSTOM, 7, [RoleName, ActName, NewLv, SubType]);
                        _ ->
                            skip
                    end,
                    NewActData1 = NewActData#role_contract{score = NewScore, lv = NewLv, daily_score = round(DailyScore+Val)},
                    NewCustomData = CustomData#custom_act_data{act_data = NewActData1},
                    lib_custom_act_data:save_custom_data(RoleId, NewCustomData),
                    ok;
                _ ->
                    ok
            end;
        _ ->
            skip
    end.

use_goods(Player, GoodsInfo, GoodsNum) ->
    Type = ?CUSTOM_ACT_TYPE_CONTRACT,
    #goods{goods_id = GoodsId, player_id = RoleId} = GoodsInfo,
    case lib_custom_act_util:get_open_subtype_list(Type) of
        ActInfos = [_|_] ->
            case lists:filter(fun(#act_info{key = {_, SubTypeX}}) -> 
                case lib_custom_act_util:get_act_cfg_info(Type, SubTypeX) of
                    #custom_act_cfg{condition = Cons} ->
                        case lists:keyfind(goods_id, 1, Cons) of
                            {_, GoodsId} -> true;
                            _            -> false
                        end;
                    _ -> false
                end
            end, ActInfos) of
                [#act_info{key = {_, SubType}, stime = ActStartTime, etime = EndTime}|_] ->
                    lib_goods_api:cost_object_list(Player, [{0, GoodsInfo#goods.goods_id, GoodsNum}], goods_use, ""),
                    CustomData = lib_custom_act_data:custom_act_data(Type, SubType),
                    #custom_act_data{act_data = ActData} = CustomData,
                    #role_contract{score = Score, lv = Lv} = NewActData = check_contract_init(RoleId, ActData, SubType, ActStartTime, EndTime),
                    {NewLv, NewScore} = update_score_lv(Score+GoodsNum, Lv),
                    RoleName = lib_role:get_role_name(RoleId),
                    #custom_act_cfg{name = ActName} = lib_custom_act_util:get_act_cfg_info(Type, SubType),
                    case data_contract:get_rewards(SubType, NewLv) of
                        #base_contract_rewards{node_rewards = NodeReward} ->
                            NewLv =/= Lv andalso NodeReward == 1 andalso lib_chat:send_TV({all}, ?MOD_AC_CUSTOM, 7, [RoleName, ActName, NewLv, SubType]);
                        _ ->
                            skip
                    end,
                    NewActData1 = NewActData#role_contract{score = NewScore, lv = NewLv},
                    {ok, BinData} = pack_contract_info(SubType, NewActData1),
                    lib_server_send:send_to_uid(RoleId, BinData),
                    NewCustomData = CustomData#custom_act_data{act_data = NewActData1},
                    lib_custom_act_data:save_custom_data(RoleId, NewCustomData),
                    {ok, Player, GoodsNum};
                _ ->
                    {fail, ?FAIL}
            end;
        _ ->
            {fail, ?ERRCODE(err331_act_closed)}
    end.


check({act_is_open, SubType}) ->
    case lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_CONTRACT, SubType) of
        #act_info{} ->
            true;
        _ ->
            {false, ?ERRCODE(err331_act_closed)}
    end;
check({task_cfg, _SubType, Type, TaskId}) ->
    Day = utime:day_of_week(),
    CfgDay = 
    if Type == 1 -> Day;
        true -> 0
    end,
    case data_contract:get_task_info(Type, CfgDay, TaskId) of
        #base_contract_pass_task{} ->
            true;
        _ ->
            {false, ?ERRCODE(missing_config)}
    end;
check({task_is_finish, SubType, Type, TaskId}) ->
    case lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_CONTRACT, SubType) of
        ActInfo = #act_info{} ->
            case lib_custom_act_data:custom_act_data(ActInfo) of
                #custom_act_data{act_data = #role_contract{daily_task = DailyTasks, week_task = WeekTasks}} ->
                    if
                        Type == ?CONTRACT_DAILY_TASK ->
                            case lists:keyfind(TaskId, 1, DailyTasks) of
                                {_, _, _, ?CONTRACT_REWARD_1} ->
                                    true;
                                {_, _, _, ?CONTRACT_REWARD_2} ->
                                    {false, ?ERRCODE(err331_have_contract_reward)};
                                {_, _, _, ?CONTRACT_REWARD_0} ->
                                    {false, ?ERRCODE(err331_contract_task_not_fin)};
                                _ ->
                                    {false, ?FAIL}
                            end;
                        Type == ?CONTRACT_WEEK_TASK ->
                            case lists:keyfind(TaskId, 1, WeekTasks) of
                                {_, _, _, ?CONTRACT_REWARD_1} ->
                                    true;
                                {_, _, _, ?CONTRACT_REWARD_2} ->
                                    {false, ?ERRCODE(err331_have_contract_reward)};
                                {_, _, _, ?CONTRACT_REWARD_0} ->
                                    {false, ?ERRCODE(err331_contract_task_not_fin)};
                                _ ->
                                    {false, ?FAIL}
                            end;
                        true ->
                            {false, ?ERRCODE(err331_task_type_error)} 
                    end;
                _ ->
                    {false, ?ERRCODE(err331_contract_task_not_fin)}
            end;
        _ ->
            {false, ?ERRCODE(err331_act_closed)}
    end;
check({lv_reward_check, Type, Lv, SubType}) ->
    case lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_CONTRACT, SubType) of
        ActInfo = #act_info{} ->
            CustomData = lib_custom_act_data:custom_act_data(ActInfo),
            case CustomData of
                #custom_act_data{act_data = #role_contract{
                    lv = NowLv, normal_reward = NormalReward, 
                    pay_reward = PayReward, pay_contract = IsPay}
                } ->
                    if 
                        Type == 1 ->
                            if NowLv >= Lv andalso Lv =/= 0 ->
                                    IsNormalRewarded = lists:member(Lv, NormalReward),
                                    IsPayReward      = lists:member(Lv, PayReward),
                                    if (not IsNormalRewarded) orelse (IsPay =/= 0 andalso (not IsPayReward)) ->
                                            true;
                                        true ->
                                            {false, ?ERRCODE(err331_contract_lv_rewarded)}
                                    end;
                                true ->
                                    {false, ?ERRCODE(err331_contract_lv_limit)}
                            end;
                        Type == 2 ->
                            if NowLv == 0 ->
                                    {false, ?ERRCODE(err331_contract_lv_limit)};
                                true ->
                                    LvList = lists:seq(1, NowLv),
                                    case lists:any(fun(LvX) -> 
                                        not lists:member(LvX, NormalReward) orelse
                                        not lists:member(LvX, PayReward)
                                    end, LvList) of
                                        true ->
                                            true;
                                        _ ->
                                            {false, ?ERRCODE(err331_contract_lv_rewarded)}
                                    end
                            end;
                        true ->
                            {false, ?ERRCODE(data_error)}
                    end;
                _ ->
                    {false, ?ERRCODE(err331_contract_task_not_fin)}
            end;
        _ ->
            {false, ?ERRCODE(err331_act_closed)}
    end;
check({buy_cost_check, Player, Discount, Type, SubType}) ->
    if Type =/= 0 andalso Type =/= 1 ->
            {false, ?ERRCODE(data_error)};
        true ->
             case data_contract:get_pass_buy_info(SubType) of
                #base_contract_pass_buy{
                    normal_price = NormalPrice, luxury_price = LuxuryPrice, discount = DiscountL, buy_type = BuyType} ->
                        if BuyType == 0 ->
                                Price = ?IF(Type == 0, NormalPrice, LuxuryPrice),
                                if 
                                    Discount == 1 ->
                                        case DiscountL of
                                            [DisGoodsId, DiscountArg] ->
                                                DisPrice = umath:ceil(Price*DiscountArg),
                                                CostL = [{?TYPE_GOLD, 0, DisPrice}, {?TYPE_GOODS, DisGoodsId, 1}],
                                                lib_goods_api:check_object_list(Player, CostL);
                                            _ -> 
                                                {false, ?ERRCODE(err331_dicount_limit)}
                                        end;
                                    true ->
                                        lib_goods_api:check_object_list(Player, [{?TYPE_GOLD, 0, Price}])
                                end;
                            true ->
                                {false, ?ERRCODE(err331_cannot_buy_directly)}
                        end;
                _ ->
                    {false, ?ERRCODE(config_err)}
            end
    end;
check({payed_check, ActData}) ->
    #role_contract{pay_contract = IsPay} = ActData,
    if IsPay =/= 0 ->
            {false, ?ERRCODE(err331_contract_haved_buy)};
        true ->
            true
    end;
check({buy_lv_check, Player, ActData, StartLv, EndLv, SubType}) ->
    #role_contract{lv = Lv} = ActData,
    NextLv = Lv + 1,
    LvConfigs = data_contract:get_sub_type_lv(SubType),
    MaxLv = lists:max(LvConfigs),
    LvCheck     = StartLv == 0 orelse EndLv == 0 orelse StartLv > EndLv orelse StartLv =/= NextLv,
    HaveActived = Lv >= EndLv orelse StartLv < Lv,
    MaxLvCheck  = Lv >= MaxLv,
    if
        MaxLvCheck ->
            {false, ?ERRCODE(err331_contract_lv_max)};
        LvCheck ->
            {false, ?ERRCODE(err331_contract_lv_error)};
        HaveActived ->
            {false, ?ERRCODE(err331_lv_have_actived)};
        true ->
            CostList = calc_buy_lv_cost(StartLv, EndLv, SubType),
            case lib_goods_api:check_object_list(Player, CostList) of
                true ->
                    true;
                {false, Errcode} ->
                    {false, Errcode}
            end
    end;
check(_A) ->
    {false, ?FAIL}.

calc_buy_lv_cost(StartLv, EndLv, SubType) ->
    case data_contract:get_pass_buy_info(SubType) of
        #base_contract_pass_buy{price = Price} when Price =/= 0 ->
            calc_buy_lv_cost_1(StartLv, EndLv, Price, 0);
        _ ->
            {false, ?FAIL}
    end.

calc_buy_lv_cost_1(MaxLv, MaxLv, Price, AccPrice) ->
    [{?TYPE_GOLD, 0, Price + AccPrice}];
calc_buy_lv_cost_1(MinLv, MaxLv, Price, AccPrice) ->
    calc_buy_lv_cost_1(MinLv+1, MaxLv, Price, Price+AccPrice).

calc_contract_lv_score(StartLv, EndLv, SubType) ->
    calc_contract_lv_score_1(StartLv, EndLv, SubType, 0).

calc_contract_lv_score_1(EndLv, EndLv, SubType, AccScore) ->
    case data_contract:get_rewards(SubType, EndLv) of
        #base_contract_rewards{lv_exp = Exp} ->
            [{?TYPE_CONTRACT_SCORE, ?CONTRACT_GOODS_1, Exp+AccScore}];
        _ ->
            [{?TYPE_CONTRACT_SCORE, ?CONTRACT_GOODS_1, AccScore}]
    end;
calc_contract_lv_score_1(StartLv, EndLv, SubType, AccScore) ->
    case data_contract:get_rewards(SubType, StartLv) of
        #base_contract_rewards{lv_exp = Exp} ->
            calc_contract_lv_score_1(StartLv+1, EndLv, SubType, AccScore+Exp);
        _ ->
            calc_contract_lv_score_1(StartLv+1, EndLv, SubType, AccScore)
    end.