%%%------------------------------------
%%% module  : mod_server_cast
%%% @author  : xyao
%%% email   : jiexiaowen@gmail.com
%%% Created : 2011.12.16
%%% description: 角色cast处理
%%%------------------------------------
-module(mod_server_cast).
-export([handle_cast/2,set_data/2,set_data_sub/2]).
-include("server.hrl").
-include("common.hrl").
-include("daily.hrl").
-include("predefine.hrl").
-include("attr.hrl").
-include("team.hrl").
-include("figure.hrl").
-include("scene.hrl").
-include("battle.hrl").
-include("def_event.hrl").
-include("errcode.hrl").
-include("def_module.hrl").
-include("def_daily.hrl").
-include("language.hrl").
-include("goods.hrl").
-include("skill.hrl").
-include("buff.hrl").
-include("dungeon.hrl").

%% 设置玩家属性(按属性列表更新)远程调用
%% @spec set_data(AttrKeyValueList,Pid) -> noprc | ok
%% AttrKeyValueList 属性列表 [{Key,Value},{Key,Value},...] Key为原子类型，Value为所需参数数据
%% Pid 玩家进程
%% @end
set_data(AttrKeyValueList, RoleId) when is_integer(RoleId) ->
    Pid = misc:get_player_process(RoleId),
    case misc:is_process_alive(Pid) of
        true -> set_data(AttrKeyValueList, Pid);
        false -> noprc
    end;
set_data(AttrKeyValueList, Pid) ->
    case is_pid(Pid) andalso misc:is_process_alive(Pid) of
        true  -> gen_server:cast(Pid, {'set_data', AttrKeyValueList});
        false -> noprc
    end.

%% 设置玩家属性(按属性列表更新)
%% @param AttrKeyValueList 属性列表 [{Key,Value},{Key,Value},...] Key为原子类型，Value为所需参数数据
%% @param Status 当前玩家状态
%% @return NewStatus 新玩家状态
set_data_sub(AttrKeyValueList, Status)->
    case AttrKeyValueList of
        [{Key,Value}|T] ->
            case Key of
                cost_money ->
                    {Cost, MoneyType, Type, About} = Value,
                    NewStatus = lib_goods_api:cost_money(Status, MoneyType, Cost, Type, About);
                hp ->
                    BattleAttr = Status#player_status.battle_attr,
                    NewBattleAttr =  BattleAttr#battle_attr{hp = Value},
                    NewStatus = Status#player_status{battle_attr = NewBattleAttr};
                hp_lim ->
                    BattleAttr = Status#player_status.battle_attr,
                    NewBattleAttr =  BattleAttr#battle_attr{hp_lim = Value},
                    NewStatus = Status#player_status{battle_attr = NewBattleAttr};
                xy ->
                    {X, Y} = Value,
                    NewStatus = Status#player_status{x=X, y=Y};
                group ->
                    BattleAttr = Status#player_status.battle_attr,
                    NewBattleAttr =  BattleAttr#battle_attr{group = Value},
                    NewStatus = Status#player_status{battle_attr = NewBattleAttr};
                hide ->
                    BattleAttr = Status#player_status.battle_attr,
                    NewBattleAttr =  BattleAttr#battle_attr{hide = Value},
                    NewStatus = Status#player_status{battle_attr = NewBattleAttr};
                team_flag ->
                    NewStatus = lib_team:update_team_flag(Status, Value);
                team_leader_name ->
                    NewStatus = lib_team:update_team_leader_name(Status, Value);
                team_skill ->
                    {SetSkillId, SkillNum} = Value,
                    #player_status{team = Team, skill = SkillStatus} = Status,
                    TeamStatus = Status#player_status{team = Team#status_team{team_skill=SetSkillId, skill_num = SkillNum}},
                    lib_team:when_team_change(TeamStatus, Team),
                    if
                        SetSkillId == 0 andalso SkillNum == 0 ->
                            %% 特殊的技能临时buff:展示
                            case lists:keyfind(?SP_SKILL_KILLING, 1, SkillStatus#status_skill.skill_passive) of
                                false ->
                                    NewStatus = lib_goods_buff:remove_goods_temp_buff(TeamStatus, ?BUFF_TEAM_SHOW);
                                _ ->
                                    NewStatus = lib_goods_buff:add_goods_temp_buff(TeamStatus, ?BUFF_TEAM_SHOW, [{?SPBUFF_REBOUND, 1}], 0)
                            end;
                        true ->
                            NewStatus = lib_goods_buff:add_goods_temp_buff(TeamStatus, ?BUFF_TEAM_SHOW, [{?SPBUFF_REBOUND, SkillNum}], 0)
                    end,
                    lib_goods_buff:send_buff_notice(NewStatus),
                    NewStatus;
                copy_id ->
                    NewStatus = Status#player_status{copy_id = Value};
                pk ->
                    {Type, IsForce} = Value,
                    case lib_player:change_pkstatus_do(Status, Type, IsForce) of
                        {true, _, ChangePkPS} -> NewStatus = ChangePkPS;
                        _ -> NewStatus = Status
                    end;
                pk_protect_time ->
                    BA = Status#player_status.battle_attr,
                    PK = BA#battle_attr.pk,
                    NewPk = PK#pk{pk_protect_time = Value},
                    NewStatus = Status#player_status{battle_attr = BA#battle_attr{pk = NewPk}};
                status_dungeon ->
                    NewStatus = lib_dungeon:update_status_dungeon(Value, Status);
                ghost ->
                    #player_status{battle_attr = BA} = Status,
                    NewStatus = Status#player_status{battle_attr = BA#battle_attr{ghost = Value}};
                killed_pos ->
                    NewStatus = Status#player_status{killed_pos = Value};
                %% 切换场景回满血（先在原场景回满血，再切换场景）
                change_scene_hp_lim ->
                    BattleAttr = Status#player_status.battle_attr,
                    HpLim   = BattleAttr#battle_attr.hp_lim,

                    SceneId = Status#player_status.scene,
                    X       = Status#player_status.x,
                    Y       = Status#player_status.y,
                    SceneName = lib_scene:get_scene_name(SceneId),
                    Hp      = HpLim,
                    Gold    = Status#player_status.gold,
                    BGold   = Status#player_status.bgold,
                    AttProtected = 0,
                    %%切场景播放动作,位置偏移，设置复活方式为0
                    Status2 = lib_revive:send_revive(Status, 0, SceneId, X, Y, SceneName, Hp, Gold, BGold, AttProtected),

                    NewBattleAttr =  BattleAttr#battle_attr{hp = HpLim},
                    NewStatus = Status2#player_status{battle_attr = NewBattleAttr};
                %% 切换场景回血（先在原场景回血，再切换场景）
                change_scene_hp ->
                    BattleAttr = Status#player_status.battle_attr,

                    SceneId = Status#player_status.scene,
                    X       = Status#player_status.x,
                    Y       = Status#player_status.y,
                    SceneName = lib_scene:get_scene_name(SceneId),
                    Hp      = Value,
                    Gold    = Status#player_status.gold,
                    BGold   = Status#player_status.bgold,
                    AttProtected = 0,
                    %%切场景播放动作,位置偏移，设置复活方式为0
                    Status2 = lib_revive:send_revive(Status, 0, SceneId, X, Y, SceneName, Hp, Gold, BGold, AttProtected),

                    NewBattleAttr =  BattleAttr#battle_attr{hp = Value},
                    NewStatus = Status2#player_status{battle_attr = NewBattleAttr};
                %% act_title ->
                %%     NewStatus = Status#player_status{act_title = Value};
                follows ->
                    NewStatus = Status#player_status{follows = Value};
                change_location ->
                    [SceneId, ScenePoolId, CopyId, X, Y] = Value,
                    #player_status{scene=NowSceneId, scene_pool_id=NowScenePoolId, copy_id=NowCopyId, x=Xnow, y=Ynow,
                        follow_target_conut=FTargetCount, battle_attr=BA} = Status,
                    case Status#player_status.online == ?ONLINE_ON of
                        true -> NewStatus = Status;
                        false ->
                            NewStatus
                            = case SceneId /= NowSceneId orelse ScenePoolId /= NowScenePoolId orelse CopyId /= NowCopyId of
                                true when BA#battle_attr.hp =< 0 -> %% 血量为0不切换
                                    IsSendTeam=false,
                                    Status;

                                true ->
                                    IsTrans = case data_scene:get(SceneId) of
                                        #ets_scene{type=SceneType}
                                        when SceneType == ?SCENE_TYPE_NORMAL
                                        orelse SceneType == ?SCENE_TYPE_OUTSIDE ->
                                            true;
                                        _ ->
                                            false
                                    end,
                                    case IsTrans of
                                        true ->
                                            IsSendTeam=true,
                                            TmpStatus = lib_scene:change_scene(Status, SceneId, ScenePoolId, CopyId, X, Y, [{follow_target_xy, {X,Y}}]),
                                            TmpStatus;
                                        false ->
                                            IsSendTeam=false
                                    end;

                                false ->
                                    case abs(X-Xnow) > 300 orelse abs(Y-Ynow) > 150 of
                                        true ->
                                            {IsSend, FTargetCount1} = case FTargetCount > 4 of
                                                true  -> {true, 0};
                                                false -> {false, FTargetCount+1}
                                            end,
                                            IsSendTeam=IsSend,
                                            case IsSend of
                                                true  -> mod_scene_agent:update(Status, [{follow_target_xy,{X,Y}}]);
                                                false -> skip
                                            end,
                                            Status#player_status{follow_target_conut=FTargetCount1};
                                        false ->
                                            IsSendTeam=false,
                                            Status
                                    end
                            end,
                            case IsSendTeam andalso Status#player_status.team#status_team.team_id > 0 of
                                true ->
                                    mod_team:cast_to_team(Status#player_status.team#status_team.team_id,
                                        {'change_location', Status#player_status.id, SceneId, ScenePoolId, CopyId, X, Y});
                                false ->
                                    skip
                            end
                    end;
                follow_target_xy ->
                    NewStatus = Status#player_status{follow_target_xy=Value};
                talk_lim ->
                    NewStatus = Status#player_status{talk_lim = Value};
                talk_lim_type ->
                    NewStatus = Status#player_status{talk_lim_type = Value};
                talk_lim_time ->
                    NewStatus = Status#player_status{talk_lim_time = Value};
                leave_scene_sign ->
                    NewStatus = Status#player_status{leave_scene_sign = Value};
                action_lock -> %% 注意 Value只能是错误码整数值
                    NewStatus = lib_player:setup_action_lock(Status, Value);
                action_free ->
                    NewStatus = lib_player:break_action_lock(Status, Value);
                forbid_pk_etime ->
                    NewStatus = Status#player_status{forbid_pk_etime = Value};
                add_exp -> %% 增加玩家经验
                    case Value of
                        {Exp, ProduceType} -> NewStatus = lib_player:add_exp(Status, Exp, ProduceType);
                        _ -> NewStatus = lib_player:add_exp(Status, Value)
                    end;
                %% last_task_id ->
                %%     lib_server_send:send_to_sid(Status#player_status.sid, pt_300, 30012, [Value]),
                %%     NewStatus = Status#player_status{last_task_id = Value};
                mate_role_id ->
                    NewStatus = Status#player_status{mate_role_id = Value},
                    mod_scene_agent:update(NewStatus, [{mate_role_id, Value}]);
                %% 复活坐标
                revive_coord ->
                    #player_status{revive_status = ReviveStatus} = Status,
                    NewReviveStatus = ReviveStatus#revive_status{revive_coord = Value},
                    NewStatus = Status#player_status{revive_status = NewReviveStatus};
                %% 增加buff
                add_buff ->
                    {SkillId, SkillLv} = Value,
                    {ok, NewStatus} = lib_skill_buff:add_buff(Status, SkillId, SkillLv);
                %% 清理buff
                clean_buff ->
                    SkillId = Value,
                    {ok, NewStatus} = lib_skill_buff:clean_buff(Status, SkillId);
                %% 增加buff(物品buff版)
                add_skill_buff ->
                    {SkillId, SkillLv} = Value,
                    NewStatus = lib_goods_buff:add_skill_buff(Status, SkillId, SkillLv);
                %% 清理buff(物品buff版)
                remove_skill_buff ->
                    SkillId = Value,
                    NewStatus = lib_goods_buff:remove_skill_buff(Status, SkillId);
                guild_war ->
                    NewStatus = Status#player_status{guild_war = Value};
                act_label ->
                    OActLabel = Status#player_status.act_label,
                    F = fun({K,V}, List) when V == 0-> lists:keydelete(K, 1, List);
                        ({K,V}, List) -> [{K,V}|lists:keydelete(K, 1, List)]
                    end,
                    ActLabel = lists:foldl(F, OActLabel, Value),
                    NewStatus = Status#player_status{act_label = ActLabel};
                team ->
                    NewStatus = Status#player_status{team = Value};
                kf_1vn ->
                    {DefType, Turn, Area} = Value,
                    NewStatus = lib_kf_1vn:set_player_1vn(Status, DefType, Turn, Area);
                %% 玩家完成活动用于记录玩家活跃度
                role_success_end_activity ->
                    {Module, ModuleSub} = Value,
                    NewStatus = lib_activitycalen_api:role_success_end_activity(Status, Module, ModuleSub);
                dun_multi_pid ->
                    NewStatus = lib_dun_multi:update_status_dungeon([{dun_multi_pid, Value}], Status);
                scene_att_objects ->
                    NewStatus = Status#player_status{scene_att_objects = Value};
                turn_task_id ->
                    NewStatus = Status#player_status{turn_task_id = Value};
                anger ->
                    BattleAttr = Status#player_status.battle_attr,
                    case Value of
                        {notice, Anger} ->
                            NewStatus = Status#player_status{battle_attr = BattleAttr#battle_attr{anger = Anger}},
                            pp_player:handle(13086, NewStatus, []);
                        _ when is_integer(Value) ->
                            NewStatus = Status#player_status{battle_attr = BattleAttr#battle_attr{anger = Value}};
                        _ ->
                            NewStatus = Status
                    end;
                motion_energy ->
                    NewStatus = motion_energy:sync_motion_energy(Status, Value);
                recalc_speed ->
                    NewStatus = lib_player:update_speed(Status);
                _->
                    NewStatus = Status
            end,
            set_data_sub(T, NewStatus);
        _ -> Status
    end.

%% 分类设置/更新玩家信息|(因为各属性更改规则不一致，故需要特殊写规则，请维护上面set_data_sub()函数)
%% @param Type 更新数据的类型_根据此类型来对Info解包
%% @param Info 更新所包含的数据
handle_cast({'set_data', AttrKeyValueList}, Status) ->
    %% 根据类型_调用各个功能自己的组合函数来更新玩家信息，请维护set_data_sub()函数。
    case set_data_sub(AttrKeyValueList,Status) of
        NewPlayerStatus when is_record(NewPlayerStatus, player_status) ->
            {noreply, NewPlayerStatus};
        Other ->
            ?ERR("mod_server_cast set_data error:~p~n", [Other]),
            throw(error_player_status),
            {noreply, Status}
    end;

handle_cast({'update_player_scene_info', AttrKeyValueList}, Status) ->
    mod_scene_agent:update(Status, AttrKeyValueList),
    {noreply, Status};

%% 调用模块函数
handle_cast({'apply_cast', CastType, Moudle, Method, Args}, Status) ->
    if
        CastType == ?APPLY_CAST -> NewArgs = Args;
        CastType == ?APPLY_CAST_STATUS orelse
        CastType == ?APPLY_CAST_SAVE -> NewArgs = [Status|Args];
        true ->
            NewArgs = Args,
            ?ERR("apply_cast error:~p~n", [{Status#player_status.id, CastType, Moudle, Method, Args}]),
            throw(error_apply_cast)
    end,
    case apply(Moudle, Method, NewArgs) of
        NewStatus when is_record(NewStatus, player_status) ->
            {noreply, NewStatus};
        {ok, NewStatus} when is_record(NewStatus, player_status) ->
            {noreply, NewStatus};
        {ok, Value, NewStatus} ->
            if
                Value == battle_attr -> %% 更新战斗属性,更新装备属性
                    mod_scene_agent:update(NewStatus, [{battle_attr, NewStatus#player_status.battle_attr}]);
                true ->
                    skip
            end,
            {noreply, NewStatus};
        _Info when CastType == ?APPLY_CAST_SAVE ->
            ?ERR("mod_server_cast apply_cast error CastType = ~p, Moudle = ~p, Method = ~p, Args = ~p, Reason = ~p~n",
                [CastType, Moudle, Method, Args, _Info]),
            throw(error_player_status),
            {noreply, Status};
        _R ->
            {noreply, Status}
    end;

handle_cast({'send_reward', RewardList, ProduceType, SubType}, Status) ->
    NewStatus = lib_goods_api:send_reward(Status, RewardList, ProduceType, SubType),
    lib_player:send_attribute_change_notify(NewStatus, ?NOTIFY_MONEY),
    {noreply, NewStatus};

handle_cast({'send_reward', Produce}, Status) ->
    NewStatus = lib_goods_api:send_reward(Status, Produce),
    lib_player:send_attribute_change_notify(NewStatus, ?NOTIFY_MONEY),
    {noreply, NewStatus};

%% handle_cast({'send_cluster_drop_reward', DropArgs, DropReward, ProduceType}, Status) ->
%%     NewStatus = lib_drop:send_cluster_drop_reward_ps(Status, DropArgs, DropReward, ProduceType),
%%     lib_player:send_attribute_change_notify(NewStatus, ?NOTIFY_MONEY),
%%     {noreply, NewStatus};

%% 发送掉落物品(跨服和本服都是这里)
handle_cast({'send_drop_reward', DropArgs, DropReward, ProduceType}, Status) ->
    NewStatus = lib_drop:send_drop_reward_helper(Status, DropArgs, DropReward, ProduceType),
    lib_player:send_attribute_change_notify(NewStatus, ?NOTIFY_MONEY),
    {noreply, NewStatus};

%% [通用]玩家离开场景处理
handle_cast({'leave_scene', _LeaveType, _Args}, Status) ->
    {noreply, Status};

handle_cast({'leave_scene'}, Status) ->
    lib_scene:leave_scene(Status),
    {noreply, Status};

%% 更改玩家场景
handle_cast({'change_scene', SceneId, ScenePoolId, CopyId, X, Y, KeyValueList}, Status) ->
    AutoSelect = true,
    handle_cast({'change_scene', SceneId, ScenePoolId, CopyId, X, Y, AutoSelect, KeyValueList}, Status);

handle_cast({'change_scene', SceneId, ScenePoolId, CopyId, X, Y, AutoSelect, KeyValueList}, Status) ->
    NewStatus = case lib_scene:is_dungeon_scene(Status#player_status.scene) of
        true ->
            #player_status{dungeon = StatusDungeon} = Status,
            DunType =
            case StatusDungeon of
                #status_dungeon{dun_id = DunId} -> data_dungeon:get_dun_type(DunId);
                _ -> 0
            end,
            if DunType =/= ?DUNGEON_TYPE_EXP ->
                    %% 副本地图无法传送.
                    ErrorCode = ?ERRCODE(err120_cannot_transfer_in_dungeon),
                    {ok, BinData} = pt_120:write(12005, [0, X, Y, ErrorCode, 0, 0, 0]),
                    lib_server_send:send_to_sid(Status#player_status.sid, BinData),
                    Status;
                true ->
                    lib_scene:change_scene(Status, SceneId, ScenePoolId, CopyId, X, Y, AutoSelect, KeyValueList)                    
            end;
        _ ->
            lib_scene:change_scene(Status, SceneId, ScenePoolId, CopyId, X, Y, AutoSelect, KeyValueList)
    end,
    {noreply, NewStatus};

%% 更改玩家到默认场景
handle_cast({'change_default_scene', KeyValueList}, Status) ->
    NewStatus = lib_scene:change_default_scene(Status, KeyValueList),
    {noreply, NewStatus};

%% 更改玩家到relogin场景
handle_cast({'change_relogin_scene', KeyValueList}, Status) ->
    NewStatus = lib_scene:change_relogin_scene(Status, KeyValueList),
    {noreply, NewStatus};

%% 标志排队换线
handle_cast({'change_scene_sign', [SceneId, ScenePoolId, CopyId, X, Y, KeyValueList]}, Status) ->
    #player_status{id = RoleId, pid = RolePid, scene = RoleScene} = Status,
    case lib_scene:is_dungeon_scene(RoleScene) of
        true ->
            %% 副本地图无法传送.
            % {ok, BinData} = pt_120:write(12005, [0, 0, 0, ?ERRCODE(err120_transtype), 0, CallBackValue, TransType]),
            % lib_server_send:send_to_sid(Status#player_status.sid, BinData),
            {noreply, Status};
        _ ->
            case SceneId == RoleScene of    %% 如果已经在这个场景了就不要使用排队传送了
                true -> {noreply, Status};
                false ->
                    %% 发送到排队进程
                    scene_enter_queue:enter(SceneId, {RoleId, RolePid, SceneId, ScenePoolId, CopyId, X, Y, KeyValueList}),
                    {noreply, Status#player_status{change_scene_sign = 1}}
            end
    end;

%% 刷新日常(凌晨12点)
handle_cast({'refresh_and_clear_daily', ?TWELVE}, Status)  ->
    {ok, SupplicationStatus} = lib_supplication_api:daily_reset(Status),
    {ok, LimitGoalStatus} = lib_limit_goal_api:trigger(SupplicationStatus, {login_days}),
    {ok, RechargeCumulationStatus} = lib_recharge_cumulation:update(LimitGoalStatus),
    lib_custom_act_api:player_daily_update_12(RechargeCumulationStatus),
    LastStatus = lib_player_tomorrow:tomorrow(RechargeCumulationStatus, ?TWELVE),
    {ok, LastStatus1} = lib_player_event:dispatch(LastStatus, ?EVENT_ZERO_CLOCK),
    lib_player:set_online_time_log(LastStatus1#player_status.id),
    {noreply, LastStatus1};

%% 刷新日常(凌晨4点)
handle_cast({'refresh_and_clear_daily', ?FOUR}, Status)  ->
    DungeonStatus = lib_dungeon:daily_reset(Status, ?FOUR),
    LastStatus = lib_player_tomorrow:tomorrow(DungeonStatus, ?FOUR),
    {ok, LastStatus1} = lib_player_event:dispatch(LastStatus, ?EVENT_FOUR_CLOCK),
    {noreply, LastStatus1};

%% 清理在线玩家的任务数据
handle_cast({'refresh_and_clear_task', Time}, Status)  ->
    lib_role_task:each_daily_clear(lib_task_api:ps2task_args(Status), Time),
    {noreply, Status};

%% 刷新世界等级
handle_cast({'refresh_average_lv'}, State)->
    pp_player:handle(13011, State, []),
    pp_race_rank:handle(33300, State, []),
    {noreply, State};

%% 删除场景玩家残留数据
handle_cast({'delete_scene_user', TargetScene, TargetPoolId}, Status)->
    #player_status{id = RoleId, scene = Scene} = Status,
    if
        TargetScene == Scene -> skip;
        true -> mod_scene_agent:cast_to_scene(TargetScene, TargetPoolId, {'delete_scene_user', RoleId})
    end,
    {noreply, Status};

%% gm删除任务秘籍
handle_cast({'gm_del_task', RoleId, TaskId}, State)->
    #player_status{id = Id} = State,
    if
        RoleId =/= Id ->
            ?ERR("~p ~p RoleId error:~p~n", [?MODULE, ?LINE, [RoleId, Id]]),
            skip;
        true ->
            case data_task:get(TaskId) of
                null ->
                    ?ERR("~p ~p TaskId error:~p~n", [?MODULE, ?LINE, [TaskId]]),
                    skip;
                _    ->
                    ok
            end
    end,
    {noreply, State};

handle_cast(_Event, Status) ->
    {noreply, Status}.
