%%-----------------------------------------------------------------------------
%% @Module  :       lib_fishing
%% @Author  :       lhy
%% @Email   :       liuhouying73@qq.com
%% @Created :       2018-09-28
%% @Description:    钓鱼系统
%%-----------------------------------------------------------------------------
-module(lib_fishing).
-include("fishing.hrl").
-include("server.hrl").
-include("common.hrl").
-include("goods.hrl").
-include("def_goods.hrl").
-include("def_module.hrl").
-include("rec_event.hrl").
-include("def_event.hrl").
-include("def_fun.hrl").
-include("vip.hrl").
-include("errcode.hrl").
-include("def_daily.hrl").
-include("figure.hrl").
-include("live_skill.hrl").
-include("def_act_label.hrl").
-include("predefine.hrl").

-export([
	      login/1                            %% 登录初始化
        ,offline_login/1                   %% 离线数据

        %% ---------- 协议 --------------
        ,send_fishing_info/1               %% 发送钓鱼信息
        ,send_fishing_gear_info/1          %% 发送渔具信息
        ,select_fishing_gear/2             %% 选择渔具
        ,buy_fishing_gear/2                %% 购买渔具
        ,goods_check/2                     %% 道具检查
        ,throw_fishing_gear/2              %% 甩杆
        ,take_back_fishing_rod/2           %% 收杆
        ,send_fish_illu_map/1              %% 鱼类图鉴
        ,upgrade_fish_illu_map/2           %% 激活升级鱼鉴
        ,transfer_to_other_scene/2         %% 切换钓鱼区
        ,send_fish_basket_info/1           %% 鱼筐信息

        %% ---------- 其它 -------------------
        ,return_error_code/2               %% 返回错误码
        ,get_fishing_lv_mastery/1          %% 获取玩家钓鱼等级、熟练度
        ,handle_event/2                    %% 监听鱼竿增加的派发处理
        ,add_log_buy_fishing_gear/3        %% 写入购买渔具日志
        ,sell_fishing_rod/2                %% 判断出售列表是否含有鱼竿
        ,update_fishing_rod_usage_count/2  %% 秘籍清理背包同步鱼竿使用次数
        ,db_player_fishing_replace/1       %% 写入数据库
        ,add_fishing_mastery/2             %% 秘籍添加钓鱼熟练度
        ,check_args/1                      %% 检测
]).

%% 登录加载
login(RoleId) ->
    [Lv, Mastery, FishRodUsageCount] = db_player_fishing_select(RoleId),
    FishIlluList = db_fish_illustrated_select(RoleId),
    F = fun([IlluId, IlluLv], {FishIlluMap, AttrList}) ->
      case data_fishing:get_fish_illu_lv_cfg(IlluId, IlluLv) of
        #base_fish_illustrated_lv{attr = Attr} ->
          skip;
        _ ->
          Attr = []
      end,
      {maps:put(IlluId, #illu_info{illu_id = IlluId, illu_lv = IlluLv}, FishIlluMap), AttrList ++ Attr}
    end,
    {FishIlluMap, TmpAttrList} = lists:foldl(F, {#{}, []}, FishIlluList),
    AttrList = util:combine_list(TmpAttrList),
    #fishing{lv = Lv, mastery = Mastery, fish_rod_usage_count = FishRodUsageCount, fish_illu_map = FishIlluMap, attr = AttrList}.

offline_login(RoleId) ->
    FishIlluList = db_fish_illustrated_select(RoleId),
    F = fun([IlluId, IlluLv], AttrList) ->
      case data_fishing:get_fish_illu_lv_cfg(IlluId, IlluLv) of
        #base_fish_illustrated_lv{attr = Attr} ->
          skip;
        _ ->
          Attr = []
      end,
      AttrList ++ Attr
    end,
    TmpAttrList = lists:foldl(F, [], FishIlluList),
    AttrList = util:combine_list(TmpAttrList),
    #fishing{attr = AttrList}.

%% 钓鱼信息
send_fishing_info(PS) ->
    #player_status{sid = Sid, id = RoleId} = PS,
    [FishLv, Mastery] = get_fishing_lv_mastery(PS),
    EnergyValue = lib_counter:get_count(RoleId, ?MOD_ACTIVITY, ?POWER_LIVE_DAILY),
    {ok, BinData} = pt_177:write(17700, [EnergyValue, FishLv, Mastery]),
    lib_server_send:send_to_sid(Sid, BinData).

%% 渔具信息
send_fishing_gear_info(PS) ->
    #player_status{sid = Sid, fishing = Fishing} = PS,
    #fishing{fish_rod_usage_count = FishRodUsageCount} = Fishing,
    SendInfo = [{FishingRodId, do_usage_count(FishingRodId, UsageCountSum)} || {FishingRodId, UsageCountSum} <- FishRodUsageCount],
    {ok, BinData} = pt_177:write(17701, [SendInfo]),
    lib_server_send:send_to_sid(Sid, BinData),
    {ok, PS}.

do_usage_count(FishingRodId, UsageCountSum) ->
    case data_fishing:get_fishing_gear_property(FishingRodId) of
      [] ->
        0;
      BaseFishRod ->
        SingleCount = BaseFishRod#base_fishing_gear.usage_count,
        Rem = UsageCountSum rem SingleCount,
          if
            Rem == 0 andalso UsageCountSum > 0 ->
              UsageCount = SingleCount;
            true ->
              UsageCount = Rem
          end,
        UsageCount
    end.

%% 选择渔具
select_fishing_gear(PS, [FishingRodId, BaitId]) ->
    CheckList = [{check_fishing_rod, FishingRodId}
                 ,{check_bait, BaitId}],
    case check_args(CheckList) of
      true ->
        #player_status{fishing = Fishing} = PS,
        NewFishingGearSelect = #fishing_gear_select{fishing_rod_id = FishingRodId, bait_id = BaitId},
        NewFishing = Fishing#fishing{fishing_gear_select = NewFishingGearSelect},
        NewPS = PS#player_status{fishing = NewFishing},
        Recode = ?SUCCESS;
      {false, ErrCode} ->
        Recode = ErrCode,
        NewPS = PS
    end,
    return_error_code(PS, Recode),
    {ok, NewPS}.

%% 购买渔具
buy_fishing_gear(PS, [FishingGearId, Num]) ->
    CheckList = [{check_buy_goods, FishingGearId}
                ,{check_buy_number, Num}],
    case check_args(CheckList) of
      true ->
        do_buy_fishing_gear(PS, FishingGearId, Num);
      {false, ErrCode} ->
        return_error_code(PS, ErrCode),
        {ok, PS}
    end.

do_buy_fishing_gear(PS, FishingGearId, Num) ->
    BaseFishingGear = data_fishing:get_fishing_gear_property(FishingGearId),
    Price = BaseFishingGear#base_fishing_gear.price,
    PurchaseList = [{?TYPE_GOODS, FishingGearId, Num}],
    CheckList = [{money, Price, Num, PS}
                ,{space_enougth, PS, PurchaseList}],
    case check_buy_purchase(CheckList) of
      true->
        [{BuyType, MoneyId, UnitPrice}] = Price,
        CostPrice = UnitPrice * Num,
        FishingMoneyList = [{BuyType, MoneyId, CostPrice}],
        case lib_goods_api:cost_object_list(PS, FishingMoneyList, buy_fishing_gear, "buy fish") of
          {true, NewPS} ->
            Produce = #produce{type = fishing, reward = [{?TYPE_GOODS, FishingGearId, Num}], show_tips = 1},
            LastPS = lib_goods_api:send_reward(NewPS, Produce),
            %% 写入日志
            add_log_buy_fishing_gear(LastPS, PurchaseList, FishingMoneyList),
            Recode = ?SUCCESS;
          {false, ErrCode, LastPS} ->
            Recode = ErrCode
        end;
      {false,ErrCode} ->
        Recode = ErrCode,
        LastPS = PS
    end,
    return_error_code(LastPS, Recode),
    {ok, LastPS}.

%% 道具检查判断
goods_check(PS, MaunalOrAuto) ->
    CheckList = [{check_fishing_type, MaunalOrAuto}
                 ,{check_key_value, ?ENERGY_COST}
                 ,{check_key_value, ?FREE_RODS}
                 ,{check_key_value, ?FREE_BAIT}],
    case check_args(CheckList) of
      true ->
        {NewPS, Recode} = do_goods_check(PS, MaunalOrAuto);
      {false, ErrCode} ->
        Recode = ErrCode,
        NewPS = PS
    end,
    #player_status{sid = Sid} = NewPS,
    {ok, BinData} = pt_177:write(17710, [Recode]),
    lib_server_send:send_to_sid(Sid, BinData),
    {ok, NewPS}.

do_goods_check(PS, MaunalOrAuto) ->
    #player_status{fishing = Fishing} = PS,
    [FishingRodId, BaitId] = lib_fishing_util:get_fishing_gear_selected(Fishing),
    #base_fishing_key_value{value = FreeRodsId} = data_fishing:get_key_value(?FREE_RODS),
    #base_fishing_key_value{value = FreeBaitId} = data_fishing:get_key_value(?FREE_BAIT),
    CheckList = [{energy_value}
                ,{fishing_rod, FishingRodId, FreeRodsId, MaunalOrAuto}
                ,{fishing_bait, BaitId, FreeBaitId, MaunalOrAuto}],
    case check_fishing(CheckList, PS) of
      {true, NewPS} ->
        {NewPS, ?SUCCESS};
      {false, ErrCode, NewPS} ->
        {NewPS, ErrCode}
    end.

%% 甩杆
throw_fishing_gear(PS, Args) ->
    [SceneId, SceneType, HomeFishPondLv, WaterArea, MaunalOrAuto] = Args,
    BaseScene = data_fishing:get_distri_scene_property(SceneId, SceneType, HomeFishPondLv),
    CheckList = [{check_recode, BaseScene}
                ,{check_water_area_type, {WaterArea, MaunalOrAuto}}
                ,{check_key_value, ?SUCCESS_PROB}
                ,{check_key_value, ?ENERGY_COST}
                ,{check_key_value, ?FREE_RODS}
                ,{check_key_value, ?FREE_BAIT}],
    case check_args(CheckList) of
      true ->
        case check_fish_rod(PS) of
          {true, NewPs1} ->
            {NewPS, Recode, FishId} = do_throw_fishing_gear(NewPs1, Args),
            #player_status{fishing = Fishing, sid = Sid, id = RoleId} = NewPS,
            [FishingRodId, BaitId] = lib_fishing_util:get_fishing_gear_selected(Fishing),
            UsageCount = lib_fishing_util:return_usage_count(NewPS, FishingRodId),
            RangeOfBalance = lib_fishing_util:get_fish_balance(FishId),
            {ok, BinData} = pt_177:write(17704, [RangeOfBalance, Recode, FishingRodId, UsageCount, BaitId, MaunalOrAuto]),
            lib_server_send:send_to_sid(Sid, BinData),
            [FishLv, Mastery] = get_fishing_lv_mastery(NewPS),
            EnergyValue = lib_counter:get_count(RoleId, ?MOD_ACTIVITY, ?POWER_LIVE_DAILY),
            lib_server_send:send_to_sid(Sid, pt_177, 17700, [EnergyValue, FishLv, Mastery]),
            {ok, NewPS};
          {false, ErrCode} ->
            lib_server_send:send_to_sid(PS#player_status.sid, pt_177, 17713, [ErrCode]),
            {ok, PS}
        end;
      {false, ErrCode} ->
        lib_server_send:send_to_sid(PS#player_status.sid, pt_177, 17713, [ErrCode]),
        {ok, PS}
    end.


do_throw_fishing_gear(PS, Args) ->
    [SceneId, SceneType, HomeFishPondLv, WaterArea, MaunalOrAuto] = Args,
    {Result, NewPS, Recode, {CacheRodsCost, CostEnergyValue}} = cost_goods(PS),
    case Result of
      true  ->
        case MaunalOrAuto of
          ?MAUNAL ->   %% 手动钓鱼
            {FishId, SelectedArea} = get_fish_id(SceneId, SceneType, HomeFishPondLv, WaterArea, MaunalOrAuto, NewPS);
          ?AUTO ->   %% 自动钓鱼
            BaseKeyValue = data_fishing:get_key_value(?SUCCESS_PROB),
            SuccessProb = BaseKeyValue#base_fishing_key_value.value,
            AutoWeightList = [{SuccessProb, 1},{?PERCENTAGE - SuccessProb, 0}],
            case urand:rand_with_weight(AutoWeightList) of
              1 ->
                {FishId, SelectedArea} = get_fish_id(SceneId, SceneType, HomeFishPondLv, WaterArea, MaunalOrAuto, NewPS);
              _ ->
                {FishId, SelectedArea} = {0, 0}
            end
        end,
  %      {FishId, SelectedArea} = get_fish_id(SceneId, SceneType, HomeFishPondLv, WaterArea, MaunalOrAuto, NewPS),
        {ok, LastPS} = lib_fishing_util:cache(FishId, SceneId, SelectedArea, MaunalOrAuto, CacheRodsCost, CostEnergyValue, NewPS),
        {LastPS, ?SUCCESS, FishId};
      false ->
        ErrCode = Recode,
        {NewPS, ErrCode, 0}
    end.

%% 钓鱼  1:近水域  2:中水域   3:远水域
get_fish_id(SceneId, SceneType, HomeFishPondLv, WaterArea, MaunalOrAuto, PS) ->
    SelectedArea = lib_fishing_util:select_water_area(WaterArea, MaunalOrAuto),
    FishList = do_get_fish_list(SelectedArea, SceneId, SceneType, HomeFishPondLv, []),
    case FishList of
    [] ->
      {0, SelectedArea};
    _ ->
      ProbList = lib_fishing_util:get_probability(PS#player_status.fishing),
      WeightList = lib_fishing_util:get_weight_list(FishList, ProbList),
      case urand:rand_with_weight(WeightList) of
        false ->
          {0, SelectedArea};
        Elem ->
          FishId = Elem,
          {FishId, SelectedArea}
      end
    end.

do_get_fish_list(_SelectedArea, _SceneId, SceneType, 0, FishList) when SceneType == ?HOME_SCENE -> FishList;
do_get_fish_list(SelectedArea, SceneId, SceneType, HomeFishPondLv, FishList) ->
    case data_fishing:get_distri_scene_property(SceneId, SceneType, HomeFishPondLv) of
      #base_distri_scene{} = BaseScene ->
        case SelectedArea of
          ?NEAR ->
            TmpFishList = BaseScene#base_distri_scene.near_water_area;
          ?MIDDLE ->
            TmpFishList = BaseScene#base_distri_scene.middle_water_area;
          ?FAR ->
            TmpFishList = BaseScene#base_distri_scene.far_water_area
        end;
      _ ->
        TmpFishList = []
    end,
    case SceneType of
      ?HOME_SCENE when HomeFishPondLv > 0 ->
        do_get_fish_list(SelectedArea, SceneId, SceneType, HomeFishPondLv - 1, FishList ++ TmpFishList);
      _ ->
        TmpFishList ++ FishList
    end.

%% 消耗道具
cost_goods(PS) ->
    BaseKeyValue = data_fishing:get_key_value(?ENERGY_COST),
    EnergyCost = BaseKeyValue#base_fishing_key_value.value,
    #player_status{fishing = Fishing} = PS,
    [FishingRodId, BaitId] = lib_fishing_util:get_fishing_gear_selected(Fishing),
    #base_fishing_key_value{value = FreeRodsId} = data_fishing:get_key_value(?FREE_RODS),
    #base_fishing_key_value{value = FreeBaitId} = data_fishing:get_key_value(?FREE_BAIT),
    [NoComsumeEnergyFlag, TmpNoComsumeRodUsageFlag] = lib_fishing_util:get_fishing_lv_prob(Fishing#fishing.lv),%% 不消耗为1  消耗为0
    if
      FreeRodsId == FishingRodId andalso FreeBaitId == BaitId ->
        {NoComsumeRodUsageFlag, NoComsumeBaitFlag} = {1, 1};
      FreeRodsId =/= FishingRodId andalso FreeBaitId == BaitId ->
        {NoComsumeRodUsageFlag, NoComsumeBaitFlag} = {TmpNoComsumeRodUsageFlag, 1};
      FreeRodsId == FishingRodId andalso FreeBaitId =/= BaitId ->
        {NoComsumeRodUsageFlag, NoComsumeBaitFlag} = {1, 0};
      true ->
        {NoComsumeRodUsageFlag, NoComsumeBaitFlag} = {TmpNoComsumeRodUsageFlag, 0}
    end,
    {ok, NewPS} = case NoComsumeRodUsageFlag of
      1 ->  %%不消耗鱼竿使用次数
        NoComsumeRodFlag = 1,
        {ok, PS};
      0 ->  %%消耗鱼竿使用次数
        {ok, TmpNewPS} = lib_fishing_util:update_fish_rod_usage_count(PS, FishingRodId, ?MINUSNUM, ?MINUS),
        db_player_fishing_replace(TmpNewPS),
        NoComsumeRodFlag = ?IF(lib_fishing_util:get_usage_count(TmpNewPS, FishingRodId) > 0, 1, 0),
        {ok, TmpNewPS}
    end,
    CostEnergyValue = case NoComsumeEnergyFlag of
      0 ->
        [EnergyCost];
      1 ->
        []
    end,
    Flag = NoComsumeBaitFlag * 2 + NoComsumeRodFlag * 1, %% 把两个标志作为两位的二进制,转为十进制，3(11) 2(10) 1(01) 0(00)
    UpdateList = case Flag of
      0 ->  %% (00)都消耗
        [{?TYPE_GOODS, BaitId, 1}, {?TYPE_GOODS, FishingRodId, 1}];
      1 ->  %% (01)不消耗鱼竿
        [{?TYPE_GOODS, BaitId, 1}];
      2 ->  %% (10)不消耗鱼饵
        [{?TYPE_GOODS, FishingRodId, 1}];
      3 ->  %% (11)不消耗鱼饵、鱼竿
        []
    end,
    CacheRodsCost = ?IF(NoComsumeRodUsageFlag == 1, 0, 1),
    CacheEnergyCost = ?IF(NoComsumeEnergyFlag == 1, 0, EnergyCost),
    if
      CostEnergyValue == [] andalso UpdateList == [] ->
        {true, NewPS, ?SUCCESS, {CacheRodsCost, CacheEnergyCost}};
      UpdateList =/= [] ->
        case do_cost_goods(CostEnergyValue, UpdateList, NewPS) of
          {ok, LastPS} ->
            {true, LastPS, ?SUCCESS, {CacheRodsCost, CacheEnergyCost}};
          {false, ErrCode, LastPS} ->
            {false, LastPS, ErrCode, {CacheRodsCost, CacheEnergyCost}}
        end;
      true ->
        case do_cost_energy(NewPS, CostEnergyValue) of
          {ok, LastPS} ->
            {true, LastPS, ?SUCCESS, {CacheRodsCost, CacheEnergyCost}};
          {false, ErrCode, LastPS} ->
            {false, LastPS, ErrCode, {CacheRodsCost, CacheEnergyCost}}
        end
    end.

do_cost_goods(CostEnergyValue, UpdateList, PS) ->
    case lib_goods_api:cost_object_list(PS, UpdateList, fishing_consume, "fishing") of
      {true, NewPS} ->
        if
          CostEnergyValue =/= [] ->
            do_cost_energy(NewPS, CostEnergyValue);
          true ->
            {ok, NewPS}
        end;
      {false, ErrCode, NewPS} ->
        {false, ErrCode, NewPS}
    end.

do_cost_energy(PS, CostEnergyValue) ->
    [Live] =  CostEnergyValue,
    case lib_activitycalen_api:cost_power_live(PS, Live, ?MOD_FISHING, 0 ) of
      {false, ErrCode} ->
        {false, ErrCode, PS};
      _ ->
        {ok, PS}
    end.

%% 收杆
take_back_fishing_rod(PS, [Code]) ->
    CheckList = [{check_take_back_code, Code}],
    case check_args(CheckList) of
      true ->
        do_send_fish(PS, Code);
      {false, ErrCode} ->
        return_error_code(PS, ErrCode)
    end.

do_send_fish(PS, Code) ->
    #player_status{sid = Sid, fishing = Fishing} = PS,
    [FishingRodId, _] = lib_fishing_util:get_fishing_gear_selected(Fishing),
    {FishId, NewPs} = lib_fishing_util:get_hooked_fish(PS),
    if
      Code == 1 andalso FishId =/= 0 ->
        {ok, NewPs1} = lib_fishing_util:update_unlock_lv_unlock(NewPs, FishId),
        {ok, NewPs2} = lib_fishing_util:update_fish_basket(NewPs1, FishId),
        db_player_fishing_replace(NewPs2),
        Produce = #produce{type = fishing, reward = [{?TYPE_GOODS, FishId, ?FISHNUM}], show_tips = 1,
                           title = utext:get(1770001), content = utext:get(1770002)},
        {ok, LastPS} = lib_goods_api:send_reward_with_mail(NewPs2, Produce, ?MOD_FISHING),
        {ok, BinData} = pt_177:write(17705, [FishId, FishingRodId]),
        lib_server_send:send_to_sid(Sid, BinData);
      true ->
        {ok, BinData} = pt_177:write(17705, [0, FishingRodId]),
        lib_server_send:send_to_sid(Sid, BinData),
        LastPS = NewPs
    end,
    if
      FishId =/= 0 ->
        add_log_fishing_harvest(LastPS, FishId, Code);
      true ->
        ok
    end,
    {ok, LastPS}.

% %% 鱼类图鉴
% send_fish_map(PS) ->
%     #player_status{sid = Sid, fishing = Fishing} = PS,
%     #fishing{unlock_number = UnlockNum, unlock_fish = UnlockFish} = Fishing,
%     {ok, BinData} = pt_177:write(17706, [UnlockNum, UnlockFish]),
%     lib_server_send:send_to_sid(Sid, BinData).

%% 鱼鉴信息
send_fish_illu_map(PS) ->
    #player_status{sid = Sid, fishing = Fishing} = PS,
    #fishing{fish_illu_map = FishIlluMap, attr = AttrList} = Fishing,
    SumCombat = lib_player_attr:calc_attr_power(AttrList),
    AllFishIllu = data_fishing:get_all_fish_illu(),
    F = fun({IlluId, Type}, {FishIlluList, Length}) ->
      #illu_info{illu_lv = IlluLv} = maps:get(IlluId, FishIlluMap, #illu_info{}),
      case data_fishing:get_fish_illu_lv_cfg(IlluId, IlluLv) of
        #base_fish_illustrated_lv{attr = Attr} ->
          Combat = lib_player_attr:calc_attr_power(Attr),
          {[{IlluId, Type, IlluLv, Combat} | FishIlluList], Length + 1};
        _ ->
          {[{IlluId, Type, IlluLv, 0} | FishIlluList], Length}
      end
    end,
    {FishIlluList, Length} = lists:foldl(F, {[], 0}, AllFishIllu),
    {ok, BinData} = pt_177:write(17706, [Length, SumCombat, FishIlluList]),
    lib_server_send:send_to_sid(Sid, BinData).

%% 激活升级鱼鉴
upgrade_fish_illu_map(PS, FishIlluId) ->
    #player_status{fishing = Fishing} = PS,
    #fishing{fish_illu_map = FishIlluMap} = Fishing,
    #illu_info{illu_id = IlluId, illu_lv = IlluLv} = maps:get(FishIlluId, FishIlluMap, #illu_info{}),
    CheckList = [{check_fish_illu_info, {FishIlluId}}
                ,{check_fish_illu_lv, {FishIlluId, IlluLv}}],
    case check_args(CheckList) of
      true ->
        case IlluId of
          0 ->
            ObjectList = [{?TYPE_GOODS, FishIlluId, 1}],
            case lib_goods_api:check_object_list(PS, ObjectList) of
              true ->
                do_upgrade_fish_illu(PS, FishIlluId, [], IlluId + 1);
              {false, ErrCode} ->
                {false, ErrCode, PS}
            end;
          _ ->
            #base_fish_illustrated_lv{attr = OldAttr} = data_fishing:get_fish_illu_lv_cfg(FishIlluId, IlluLv),
            do_upgrade_fish_illu(PS, FishIlluId, OldAttr, IlluLv + 1)
        end;
      {false, ErrCode} ->
        {false, ErrCode, PS}
    end.

%% 激活鱼鉴
do_upgrade_fish_illu(PS, FishIlluId, OldAttr, IlluLv) ->
    #player_status{sid = Sid, id = RoleId, fishing = Fishing} = PS,
    #fishing{fish_illu_map = FishIlluMap, attr = AttrList} = Fishing,
    #base_fish_illustrated_lv{cost = Cost, attr = Attr} = data_fishing:get_fish_illu_lv_cfg(FishIlluId, IlluLv),
    case lib_goods_api:cost_object_list_with_check(PS, Cost, fish_illustration, upgrade_illustration) of
      {true, NewPS,_} ->
        db_fish_illustrated_replace(RoleId, FishIlluId, IlluLv),
        NewFishIlluMap = maps:put(FishIlluId, #illu_info{illu_id = FishIlluId, illu_lv = IlluLv}, FishIlluMap),
        NewAttrList = util:combine_list(Attr ++ util:minus_list(AttrList, OldAttr)),
        NewFishing = Fishing#fishing{fish_illu_map = NewFishIlluMap, attr = NewAttrList},
        NewPS2 = NewPS#player_status{fishing = NewFishing},
        CountAttrPS = lib_player:count_player_attribute(NewPS2, ?MOD_FISHING, fishing_illu_up),
        lib_player:send_attribute_change_notify(CountAttrPS, ?NOTIFY_ATTR),
        %% 钓鱼图鉴升级日志
        lib_log_api:log_fish_illustrated_lv(RoleId, FishIlluId, IlluLv - 1, IlluLv, Cost),
        NewCombat = lib_player_attr:calc_attr_power(NewAttrList),
        Length = maps:size(NewFishIlluMap),
        lib_server_send:send_to_sid(Sid, pt_177, 17714, [FishIlluId, IlluLv, Length, NewCombat]),
        {ok, CountAttrPS};
      {false, ErrCode, NewPS} ->
        lib_server_send:send_to_sid(Sid, pt_177, 17713, [ErrCode]),
        {false, ErrCode, NewPS}
    end.

%% 切换钓鱼区
transfer_to_other_scene(PS, [TargetSceneId]) ->
    CheckList = [{check_lv, {PS, TargetSceneId}}],
    case check_args(CheckList) of
      true ->
        {Type, Area} = data_fishing:get_transfer_coordinate(TargetSceneId),
        case data_fishing:get_transfer_coordinate(TargetSceneId) of
          {Type, Area} ->
            skip;
          _ ->
            {Type, Area} = {0, []}
        end,
        case Area of
          [{_AreaId, [{_X, _Y} | _T2]} | _T1] ->
            case do_transfer_coordinate(Area) of
              {0, 0} ->
                return_error_code(PS, ?ERRCODE(err_177_scene_change_failure));
              {TargetX, TargetY} ->
                do_transfer(PS, TargetSceneId, [TargetX, TargetY], Type)
            end;
          _ ->
            return_error_code(PS, ?ERRCODE(err_177_scene_change_failure))
        end;
      {false, ErrCode} ->
        return_error_code(PS, ErrCode)
    end.

do_transfer(PS, TargetSceneId, [TargetX, TargetY], _Type) ->
    #player_status{sid = Sid, id = RoleId, scene = SceneId, scene_pool_id = PoolId, copy_id = CopyId, x = X, y = Y} = PS,
    % mod_scene_agent:update(PS, [{pk_protect_time,0}]),
    % lib_scene:broadcast_player_attr(PS, [{8, 0}]),

    LastPS = lib_scene:change_scene(PS, TargetSceneId, 0, 0, TargetX, TargetY, []),
    %% 钓鱼状态
    lib_fishing_util:update_fishing_act([RoleId, SceneId, PoolId, CopyId, X, Y], ?ACT_LABEL_FISHING, ?NO_ACT_FISHING),
    Recode = ?SUCCESS,

    {ok, BinData} = pt_177:write(17713, [Recode]),
    lib_server_send:send_to_sid(Sid, BinData),
    LastPS2 = lib_fishing_util:initialize_fish_basket(LastPS), %%初始化鱼筐记录
    db_player_fishing_replace(LastPS2),
    {ok, LastPS2}.

do_transfer_coordinate(Area) ->
    AreaIdList = [{?AVERAGE, AreaId} || {AreaId, _} <- Area],
    case urand:rand_with_weight(AreaIdList) of
      false ->
        {0, 0};
      RandAreaId ->
        case lists:keyfind(RandAreaId, 1, Area) of
          false ->
            {0, 0};
          {_, Coordinate} ->
            WeightList = [{?AVERAGE,{TmpX, TmpY}} || {TmpX, TmpY} <- Coordinate],
            case urand:rand_with_weight(WeightList) of
              false ->
                {0, 0};
              {X, Y} ->
                {X, Y}
            end
        end
    end.

%% 鱼筐信息
send_fish_basket_info(PS) ->
    #player_status{sid = Sid, fishing = Fishing} = PS,
    #fishing{fish_basket = FishBasket} = Fishing,
    {ok, BinData} = pt_177:write(17708, [FishBasket]),
    lib_server_send:send_to_sid(Sid, BinData).

%% 返回错误码
return_error_code(PS, ErrCode) ->
    #player_status{sid = Sid} =PS,
    {ok, BinData} = pt_177:write(17713,[ErrCode]),
    lib_server_send:send_to_sid(Sid, BinData),
    {ok, PS}.

%% 获取玩家钓鱼等级、熟练度
get_fishing_lv_mastery(PS) ->
    Fishing = PS#player_status.fishing,
    #fishing{lv = Lv, mastery = Mastery} = Fishing,
    [Lv, Mastery].

%% 秘籍清理背包 更新鱼竿使用次数
update_fishing_rod_usage_count(PS, List) ->
    AllFishingRod = data_fishing:get_all_fishing_rod(?RODTYPE),
    FishGearList = [GoodsInfo#goods.goods_id || GoodsInfo<-List, (GoodsInfo#goods.type == ?GOODS_TYPE_FISHING andalso
                    GoodsInfo#goods.subtype == ?GOODS_SUBTYPE_FISHING_GEAR andalso lists:member(GoodsInfo#goods.goods_id, AllFishingRod) == true)],
    case FishGearList of
      [] ->
        PS;
      _ ->
        #player_status{fishing = Fishing} = PS,
        NewFishing = Fishing#fishing{fish_rod_usage_count = []},
        NewPS = PS#player_status{fishing = NewFishing},
        db_player_fishing_update(NewPS),
        NewPS
    end.

%% 判断出售列表是否含有鱼竿
sell_fishing_rod(PS, TypeIdList) ->
    FishingRodList = data_fishing:get_all_fishing_rod(?RODTYPE),
    #player_status{fishing = Fishing} = PS,
    #fishing{fish_rod_usage_count = OldFRUC} = Fishing,
    F = fun(Goods, FRUC) ->
      {GoodsId, Num} = Goods,
      case lists:member(GoodsId, FishingRodList) of
        true ->
          BaseFishRod = data_fishing:get_fishing_gear_property(GoodsId),
          UsageCount = BaseFishRod#base_fishing_gear.usage_count,
          case lists:keyfind(GoodsId, 1, FRUC) of
            false ->
              FRUC;
            {_Id, UsageCountSum} ->
              case UsageCountSum rem UsageCount of
                0 ->
                  MinusNum = UsageCount * Num;
                Rem ->
                  MinusNum = UsageCount * (Num - 1) + Rem
              end,
              lists:keystore(GoodsId, 1, FRUC, {GoodsId, UsageCountSum - MinusNum})
          end;
        false ->
          FRUC
      end
    end,
    NewFRUC = lists:foldl(F, OldFRUC, TypeIdList),
    NewFishing = Fishing#fishing{fish_rod_usage_count = NewFRUC},
    NewPS = PS#player_status{fishing = NewFishing},
    db_player_fishing_replace(NewPS),
    NewPS.

%% 增加鱼竿
handle_event(PS, #event_callback{type_id = ?EVENT_GIVE_GOODS, data = #callback_give_goods_data{type = 3,goods = Goods}}) ->
    #goods{goods_id = GoodsTypeId, location = Location} = Goods,
    case data_fishing:get_fishing_gear_property(GoodsTypeId) of
      [] ->
        {ok, PS};
      #base_fishing_gear{fishing_gear_type = Type, usage_count = UsageCount} ->
        case Type of
          ?RODTYPE ->
            #player_status{id = RoleId, fishing = Fishing, figure = #figure{lv = Lv}} = PS,
            #fishing{fish_rod_usage_count = FRUC} = Fishing,
            GoodsStatus = lib_goods_do:get_goods_status(),
            #goods_status{dict = Dict} = GoodsStatus,
            GoodsList = lib_goods_util:get_type_goods_list(RoleId, GoodsTypeId, Location, Dict, Lv),
            F = fun(GoodsTmp, NumTmp) ->
              NumTmp + GoodsTmp#goods.num
            end,
            Num = lists:foldl(F, 0, GoodsList),
            case lists:keyfind(GoodsTypeId, 1, FRUC) of
              false->
                Rem = UsageCount;
              {_, OldUsageCountSum} ->
                RemTmp = OldUsageCountSum rem UsageCount,
                if
                  RemTmp =< 0 ->
                    Rem = UsageCount - RemTmp;
                  true ->
                    Rem = RemTmp
                end
            end,
            NewUsageCount = (Num - 1) * UsageCount + Rem,
            NewFRUC = lists:keystore(GoodsTypeId, 1, FRUC, {GoodsTypeId, NewUsageCount}),
            NewFishing = Fishing#fishing{fish_rod_usage_count = NewFRUC},
            NewPS = PS#player_status{fishing = NewFishing},
            send_fishing_gear_info(NewPS),
            db_player_fishing_replace(NewPS),
            {ok, NewPS};
          _ ->
            {ok, PS}
        end
    end;

handle_event(PS, #event_callback{type_id = ?EVENT_PREPARE_CHANGE_SCENE}) when is_record(PS, player_status) ->
    #player_status{id = RoleId, sid = Sid, scene = SceneId, scene_pool_id = PoolId, copy_id = CopyId, x = X, y = Y, act_label = ActLabel} = PS,
    case lists:keyfind(?ACT_LABEL_FISHING, 1, ActLabel) of
      {_, 1} ->
        lib_fishing_util:update_fishing_act([RoleId, SceneId, PoolId, CopyId, X, Y], ?ACT_LABEL_FISHING, ?NO_ACT_FISHING),
        NewPS = lib_fishing_util:initialize_fish_basket(PS), %%初始化鱼筐记录
        {ok, BinData} = pt_177:write(17709, [?SUCCESS]),
        lib_server_send:send_to_sid(Sid, BinData),
        {ok, NewPS};
      _ ->
        {ok, PS}
    end;

handle_event(PS, #event_callback{}) -> {ok, PS}.

%% 写入购买渔具日志
add_log_buy_fishing_gear(PS, PurchaseList, FishingMoneyList) ->
  #player_status{id = RoleId, figure = Figure} = PS,
  #figure{lv = RoleLv, vip = UsefulVipLv} = Figure,
  lib_log_api:log_buy_fishing_gear(RoleId, RoleLv, UsefulVipLv, PurchaseList, FishingMoneyList).

%% 写入钓鱼收获日志
add_log_fishing_harvest(PS, FishId, ReCode) ->
  #player_status{id = RoleId, figure = Figure, fishing = Fishing} = PS,
  #figure{lv = RoleLv} = Figure,
  [FishingLv, _] = get_fishing_lv_mastery(PS),
  {SceneId, WaterArea, MaunalOrAuto, RodsCost, EnergyValue} = lib_fishing_util:get_log_cache(Fishing),
  FishType = lib_fishing_util:get_fish_type(FishId),
  lib_log_api:log_fishing_harvest(RoleId, RoleLv, SceneId, FishingLv, WaterArea, FishId, FishType, MaunalOrAuto, RodsCost, EnergyValue, ReCode).

add_fishing_mastery(PS, IncrMastery) ->
  #player_status{fishing = Fishing} = PS,
  #fishing{lv = FishingLv, mastery = OldMastery} = Fishing,
  {NewLv, NewMastery} = lib_fishing_util:update_lv(FishingLv, OldMastery + IncrMastery),
  NewFishing = Fishing#fishing{lv = NewLv, mastery = NewMastery},
  NewStatus = PS#player_status{fishing = NewFishing},
  LastPS = lib_live_skill:notify_skill_change(NewStatus, ?TYPE_FISH_SKILL, NewLv, NewMastery),
  db_player_fishing_replace(LastPS),
  LastPS.

%% ---------------------------------------- check function ---------------------------------------------
%% 检查参数
check_args([]) -> true;
check_args([H | T]) ->
    case do_check_args(H) of
        true -> check_args(T);
        {false, ErrCode} -> {false, ErrCode}
    end.

do_check_args({check_key_value, Key}) ->
    case data_fishing:get_key_value(Key) of
      [] ->
        {false, ?ERRCODE(err177_data_no_exist)};
      _ ->
        true
    end;
do_check_args({check_take_back_code, Code}) ->
    if
      Code =/= ?SUCCEED andalso Code =/= ?FAILURE ->
        {false, ?ERRCODE(err177_args_type_error)};
      true ->
        true
    end;
do_check_args({check_buy_goods, FishingGearId}) ->
    BaseFishGear = data_fishing:get_fishing_gear_property(FishingGearId),
    if
      is_record(BaseFishGear, base_fishing_gear) ->
        true;
      true ->
        {false, ?ERRCODE(err177_data_no_exist)}
    end;
do_check_args({check_buy_number, Num}) ->
    if
      is_integer(Num) andalso Num > 0 ->
        true;
      true ->
        {false, ?ERRCODE(err_177_buy_num_error)}
    end;
do_check_args({check_fishing_rod, FishingRodId}) ->
    AllFishingRod = data_fishing:get_all_fishing_rod(?RODTYPE),
    case lists:member(FishingRodId, AllFishingRod) of
      true ->
        true;
      false ->
        {false, ?ERRCODE(err177_rod_data_no_exist)}
    end;
do_check_args({check_bait, BaitId}) ->
    AllBait = data_fishing:get_all_fishing_rod(?BAITTYPE),
    case lists:member(BaitId, AllBait) of
      true ->
        true;
      false ->
        {false, ?ERRCODE(err177_bait_data_no_exist)}
    end;
do_check_args({check_fishing_type, MaunalOrAuto}) ->
    if
      MaunalOrAuto =/= ?MAUNAL andalso MaunalOrAuto =/= ?AUTO ->
        {false, err177_args_type_error};
      true ->
        true
    end;
do_check_args({check_recode, Record}) ->
    case Record of
      [] ->
        {false, ?ERRCODE(err177_fish_ponds_no_open)};
      _ ->
        true
    end;
do_check_args({check_water_area_type, {WaterArea, MaunalOrAuto}}) ->
    case MaunalOrAuto of
      ?MAUNAL ->
        case lists:member(WaterArea, ?MAUNAL_WATER_AREA) of
          true ->
            true;
          false ->
            {false, ?ERRCODE(err177_args_type_error)}
        end;
      ?AUTO ->
        case lists:member(WaterArea, ?AUTO_WATER_AREA) of
          true ->
            true;
          false ->
            {false, ?ERRCODE(err177_args_type_error)}
        end;
      _ ->
        {false, ?ERRCODE(err177_args_type_error)}
    end;
do_check_args({check_lv, {PS, TargetSceneId}}) ->
    #player_status{figure = Figure, fishing = Fishing} = PS,
    case data_fishing:get_open_lv(TargetSceneId) of
        [] ->
            {false, ?ERRCODE(err177_data_no_exist)};
        {RoleLv, FishingLv} ->
            if
                Figure#figure.lv >= RoleLv andalso Fishing#fishing.lv >= FishingLv ->
                    true;
                Figure#figure.lv < RoleLv ->
                    {false, ?ERRCODE(err177_lv_not_reach_the_open_level)};
                Fishing#fishing.lv < FishingLv ->
                    {false, ?ERRCODE(err177_fishing_lv_not_reach_the_open_level)}
            end
    end;
do_check_args({check_fish_illu_info, {IlluId}}) ->
    case data_fishing:get_fish_illu_cfg(IlluId) of
      #base_fish_illustrated{} ->
        true;
      _ ->
        {false, ?ERRCODE(err177_data_no_exist)}
    end;
do_check_args({check_fish_illu_lv, {IlluId, IlluLv}}) ->
    case data_fishing:get_fish_illu_lv_cfg(IlluId, IlluLv + 1) of
      #base_fish_illustrated_lv{} ->
        true;
      _ ->
        {false, ?ERRCODE(err433_already_max_lv)}
    end;
do_check_args({check_all, {PS, _SceneId}}) ->
    List =  [alive, safe_scene],
    lib_player_check:check_list(PS, List).

%% 检查渔具购买条件
check_buy_purchase([]) -> true;
check_buy_purchase([H | T]) ->
    case do_check_buy_purchase(H) of
        true -> check_buy_purchase(T);
        {false, Res} -> {false, Res}
    end.

do_check_buy_purchase({money, P, Num, PS}) ->
    [{BuyType, MoneyId, UnitPrice}] = P,
    CostPrice = UnitPrice * Num,
    FishingMoneyList = [{BuyType, MoneyId, CostPrice}],
    case lib_goods_api:check_object_list(PS, FishingMoneyList) of
      true ->
        true;
      _ ->
        {false,?ERRCODE(money_not_enough)}
    end;
do_check_buy_purchase({space_enougth, PS, PurchaseList}) ->
    case lib_goods_api:can_give_goods(PS, PurchaseList) of
      true -> true;
      _ -> {false, ?ERRCODE(err150_no_cell)}
    end;
do_check_buy_purchase(_Arg) ->
    {false, ?ERRCODE(err177_bad_check)}.

%% 检查钓鱼条件  活力值 鱼竿 鱼饵
check_fishing([], NewPS) -> {true, NewPS};
check_fishing([H | T] ,PS) ->
   case do_check_fishing(H, PS) of
     {true, NewPS} -> check_fishing(T, NewPS);
     {false, Res, NewPS} -> {false, Res, NewPS}
   end.

do_check_fishing({energy_value}, PS) ->
   RoleId = PS#player_status.id,
   BaseKeyValue = data_fishing:get_key_value(?ENERGY_COST),
   EnergyCost = BaseKeyValue#base_fishing_key_value.value,
   EnergyValue = lib_counter:get_count(RoleId, ?MOD_ACTIVITY, ?POWER_LIVE_DAILY),
   if
     EnergyValue >= EnergyCost ->
       {true, PS};
     EnergyValue < EnergyCost ->
       {false,?ERRCODE(err177_short_of_energy_value), PS}
   end;
do_check_fishing({fishing_rod, FishingRodId, FreeRodsId, MaunalOrAuto}, PS) ->
    FishingRodList = case FishingRodId == FreeRodsId of
      true ->
        [];
      false ->
        [{?TYPE_GOODS, FishingRodId, 1}]
    end,
    case lib_goods_api:check_object_list(PS, FishingRodList) of
        true ->
          {true, PS};
        _ ->
          case MaunalOrAuto of
            2 ->
              NewPS = lib_fishing_util:update_selected_fishing_gear(PS, FreeRodsId, ?RODTYPE),
              {true, NewPS};
            _ ->
              {false,?ERRCODE(err177_short_of_fishing_rod), PS}
          end
    end;
do_check_fishing({fishing_bait, BaitId, FreeBaitId, MaunalOrAuto}, PS) ->
    BaitList = case BaitId == FreeBaitId of
      true ->
        [];
      false ->
        [{?TYPE_GOODS, BaitId, 1}]
    end,
    case lib_goods_api:check_object_list(PS, BaitList) of
      true ->
        {true, PS};
      _ ->
          case MaunalOrAuto of
            2 ->
              NewPS = lib_fishing_util:update_selected_fishing_gear(PS, FreeBaitId, ?BAITTYPE),
              {true, NewPS};
            _ ->
              {false, ?ERRCODE(err177_short_of_bait), PS}
          end
    end.

%% 检查鱼竿使用次数
check_fish_rod(PS) ->
    #player_status{fishing = Fishing} = PS,
    #base_fishing_key_value{value = FreeRodsId} = data_fishing:get_key_value(?FREE_RODS),
    case lib_fishing_util:get_fishing_gear_selected(Fishing) of
      [0, _] ->
        {false, ?ERRCODE(err177_please_select_fishing_gear)};
      [_, 0] ->
        {false, ?ERRCODE(err177_please_select_fishing_gear)};
      [FishingRodId, _] when FreeRodsId =/= FishingRodId ->
        #fishing{fish_rod_usage_count = FRUC} = Fishing,
        case lists:keyfind(FishingRodId, 1, FRUC) of
          {_, UsageCount} ->
            if
              UsageCount =< 0 ->
                {ok, NewPS} = lib_fishing_util:sync_fish_rod(PS);
              true ->
                NewPS = PS
            end;
          false ->
            {ok, NewPS} = lib_fishing_util:sync_fish_rod(PS)
        end,
        {true, NewPS};
      _ ->
        {true, PS}
    end.

%% ---------------------------------------------- db   -------------------------------------------------
db_player_fishing_select(RoleId) ->
    Sql = io_lib:format(?sql_player_fishing_select, [RoleId]),
    case db:get_row(Sql) of
        [] ->
            [1, 0, []];
        [Lv, Mastery, FishRodUsageCount] ->
            [Lv, Mastery, util:bitstring_to_term(FishRodUsageCount)]
    end.

db_player_fishing_replace(PS) ->
    Fishing = PS#player_status.fishing,
    RoleId = PS#player_status.id,
    Lv = Fishing#fishing.lv,
    Mastery = Fishing#fishing.mastery,
    FishRodUsageCount = util:term_to_bitstring(Fishing#fishing.fish_rod_usage_count),
    SQL = io_lib:format(?sql_player_fishing_replace, [RoleId, Lv, Mastery, FishRodUsageCount]),
    db:execute(SQL).

db_player_fishing_update(PS) ->
    #player_status{id = RoleId, fishing = Fishing} = PS,
    FishRodUsageCount = util:term_to_bitstring(Fishing#fishing.fish_rod_usage_count),
    SQL = io_lib:format(?sql_player_update_fish_rod, [FishRodUsageCount, RoleId]),
    db:execute(SQL).

db_fish_illustrated_replace(RoleId, FishIlluId, FishIlluLv) ->
    SQL = io_lib:format(?sql_fish_illustrated_insert, [RoleId, FishIlluId, FishIlluLv]),
    db:execute(SQL).

db_fish_illustrated_select(RoleId) ->
    SQL = io_lib:format(?sql_fish_illustrated_select, [RoleId]),
    db:get_all(SQL).
