%%% -------------------------------------------------------
%%% @author huangyongxing@yeah.net
%%% @doc
%%% 协议打包解包
%%% @end
%%% -------------------------------------------------------
-module(cli_simu_proto).
-export([
        proto_handler/1
        , pack_send/2
        , pack_send/3
        , unpack/3
        , pack/2

        , read_figure/1
        , read_battle_attr/1
    ]).

-include("cli_simu.hrl").

%% 协议解包后处理数据入口模块映射
map(100) -> cli_login;
map(120) -> cli_scene;
map(130) -> cli_player;
map(200) -> cli_battle;
map(210) -> cli_skill;
map(590) -> cli_player;
map(_)   -> false.

proto_handler(Cmd) ->
    Div = Cmd div 100,
    case map(Div) of
        false ->
            {error, list_to_binary([<<"Invalid Cmd:">>, integer_to_binary(Cmd)])};
        _ ->
            Mod = map(Div),
            {ok, Mod}
    end.

%% 打包并发送数据，角色进程专用，需要进程字典socket信息
pack_send(Cmd, Data) ->
    case get(socket) of
        undefined ->
            {error, socket};
        Socket ->
            pack_send(Socket, Cmd, Data)
    end.

%% 打包并发送数据
pack_send(Socket, Cmd, Data) ->
    case pack(Cmd, Data) of
        {ok, BinData} when is_binary(BinData) ->
            % case Cmd =:= 10006 of
            %     true  -> ignore;
            %     false -> ?INFO_TTY("send data: ~w", [BinData])
            % end,
            gen_tcp:send(Socket, BinData);
        {error, Error} ->
            ?ERR("error encode[~w]:~w", [Cmd, Error])
    end.

%% 数据解包
%% unpack(Cmd, IsZip, Bin) -> {ok, Data} | {error, Error}.
unpack(Cmd, _IsZip = 0, Bin) ->
    decode(Cmd, Bin);
unpack(Cmd, _IsZip = 1, Bin) ->
    NewBin = zlib:uncompress(Bin),
    decode(Cmd, NewBin);
unpack(_Cmd, _IsZip, _Bin) ->
    {error, unknown_package}.

%% 数据打包 -> {ok, BinData} | {error, Error}
pack(Cmd, Data) ->
    case encode(Cmd, Data) of
        {ok, EncodeResult} when is_binary(EncodeResult) ->
            BinData = add_header(Cmd, EncodeResult),
            {ok, BinData};
        Error ->
            Error
    end.

%% 协议号映射
proto_mod_map(Cmd) ->
    Div = Cmd div 100,
    case Div >= 100 andalso Div =< 655 of
        true  ->
            list_to_atom(lists:concat(["cli_pt_", Div]));
        false ->
            ignore
    end.

%% 数据最终打包，添加头部
add_header(Cmd, DataBin) ->
    BinSize = byte_size(DataBin),
    LenAll  = 2 + 2 + BinSize,
    <<LenAll:16, Cmd:16, DataBin/binary>>.

%% 调用ProtoMod:decode，并对结果做基本检测，
%% ProtoMod:decode应返回: ignore | {error, Error} | {ok, DecodeResult}
decode(Cmd, BinData) ->
    case map(Cmd div 100) of
        false -> ignore;
        _ ->
            ProtoMod = proto_mod_map(Cmd),
            ReadRet = ProtoMod:decode(Cmd, BinData),
            case ReadRet of
                {error, _Error} ->
                    ReadRet;
                {ok, _Data} ->
                    ReadRet;
                ignore ->
                    ignore
            end
    end.

%% 调用ProtoMod:encode，并对结果做基本检测。
%% ProtoMod:encode应返回: {error, Error} | {ok, EncodeResult :: binary()}
encode(Cmd, Data) ->
    ProtoMod = proto_mod_map(Cmd),
    case ProtoMod of
        ignore -> ignore;
        _ ->
            WriteRet = ProtoMod:encode(Cmd, Data),
            case WriteRet of
                {ok, _EncodeResult} -> WriteRet;
                {error, _Error} -> WriteRet;
                ignore -> ignore
            end
    end.

read_figure(Bin) ->
    FunReadLvModel = fun(<<Part:8, ModelId:32, RestBinX/binary>>) ->
            {{Part, ModelId}, RestBinX}
    end,
    FunReadFashionModel = fun(<<Part:8, FashionId:32, Color:32, RestBinX/binary>>) ->
            {{Part, FashionId, Color}, RestBinX}
    end,
    FunReadDress = fun(<<DressType:8, DressId:32, RestBinX/binary>>) ->
            {{DressType, DressId}, RestBinX}
    end,
    FunReadLightWeapon = fun(<<Type:8, ModelId:32, PicId:32, RestBinX/binary>>) ->
            {{Type, ModelId, PicId}, RestBinX}
    end,
    FunReadFoster = fun(<<FosterType:8, Grade:16, RestBinX/binary>>) ->
            {{FosterType, Grade}, RestBinX}
    end,
    FunReadFosterSkin = fun(<<FosterType:8, SkinId:16, Star:16, RestBinX/binary>>) ->
            {{FosterType, SkinId, Star}, RestBinX}
    end,
    FunReadNatureTips = fun(<<NatureType:8, Tip:8, ShowPos:8, RestBinX/binary>>) ->
            {{NatureType, Tip, ShowPos}, RestBinX}
    end,

    {Name, RestBin1} = pt:read_string(Bin),
    <<Sex:8, Realm:8, Career:8, Lv:16, Gm:8, Vip:8, Title:8, RestBin2/binary>> = RestBin1,
    {LvModel, RestBin3} = pt:read_array(FunReadLvModel, RestBin2),
    {FashionModel, RestBin4} = pt:read_array(FunReadFashionModel, RestBin3),
    {Picture, RestBin5} = pt:read_string(RestBin4),
    <<PictureVer:32, GuildId:64, RestBin6/binary>> = RestBin5,
    {GuildName, RestBin7} = pt:read_string(RestBin6),
    <<Position:8, RestBin8/binary>> = RestBin7,
    {PositionName, RestBin9} = pt:read_string(RestBin8),
    <<Dsgt:32, WeaponEffect:32, WingFigure:32, RestBin10/binary>> = RestBin9,
    {DressList, RestBin11} = pt:read_array(FunReadDress, RestBin10),
    <<Liveness:32, FairyFigure:32, Turn:8, TurnStage:8, JueWeiLv:8, RestBin12/binary>> = RestBin11,
    {_GodWeaponModel, RestBin13} = pt:read_array(FunReadLvModel, RestBin12),
    <<_MarriageType:8, _LoverRoleId:64, RestBin14/binary>> = RestBin13,
    {_LoverName, RestBin15} = pt:read_string(RestBin14),
    <<ChildFollowStatus:8, ChildSex:8, AgeMonth:8, HeartLinkLevel:16, _BabyVehicleId:32,
    RestBin16/binary>> = RestBin15,
    {BabyModel, RestBin17} = pt:read_array(FunReadLvModel, RestBin16),
    <<AngelId:32, _GodId:32, _StarSoulId:16, ProfilePhotoId:32, _DeityId:16,
    VipType:8, RestBin18/binary>> = RestBin17,
    {LightWeaponModel, RestBin19} = pt:read_array(FunReadLightWeapon, RestBin18),
    <<RoleId:64, Power:64, RestBin20/binary>> = RestBin19,
    {Foster, RestBin21} = pt:read_array(FunReadFoster, RestBin20),
    <<IsRide:8, RestBin22/binary>> = RestBin21,
    {FosterSkins, RestBin23} = pt:read_array(FunReadFosterSkin, RestBin22),
    <<SupVipType:8, FollowPetId:32, _FollowPetStage:8, _FollowGuard:32, RestBin24/binary>> = RestBin23,
    {_NatureTips, RestBin25} = pt:read_array(FunReadNatureTips, RestBin24),
    <<_Visus:16, _NucleonLv:8, _King:8, _Post:8, RestBinEnd/binary>> = RestBin25,
    BabyIfShow = [ChildFollowStatus, ChildSex, AgeMonth, HeartLinkLevel],
    Figure = #figure{
        id=RoleId,
        name=Name,
        sex=Sex,
        realm=Realm,
        career=Career,
        lv=Lv,
        gm=Gm,
        vip_type=VipType,
        vip=Vip,
        title = Title,
        lv_model = LvModel,
        fashion_model = FashionModel,
        picture = Picture,
        picture_ver = PictureVer,
        guild_id = GuildId,
        guild_name = GuildName,
        position = Position,
        position_name = PositionName,
        designation = Dsgt,
        weapon_effect = WeaponEffect,
        wing_figure = WingFigure,
        dress_list = DressList,
        liveness = Liveness,
        fairy_figure = FairyFigure,
        turn = Turn,
        turn_stage = TurnStage,
        juewei_lv = JueWeiLv,
        baby_if_show = BabyIfShow,
        baby_model = BabyModel,
        husong_angel_id = AngelId,
        profile_photo_id = ProfilePhotoId,
        light_weapon_model = LightWeaponModel,
        power = Power,
        is_ride = IsRide,
        role_foster = Foster,
        foster_skin = FosterSkins,
        sup_vip = SupVipType,
        follow_pet = FollowPetId
    },
    {Figure, RestBinEnd}.

%% 与write_battle_attr/1相反操作
read_battle_attr(Bin) ->
    <<Hp:64, HpLim:64, Speed:16, RestBin1/binary>> = Bin,
    {Attr, RestBin2} = read_attr(RestBin1),
    {Hp, HpLim, Speed, Attr, RestBin2}.

read_attr(Bin) ->
    <<Att:32, Def:32, Hit:32, Dodge:32, Crit:32, Ten:32,
    HurtAddRatio:32, HurtDelRatio:32,
    HitRatio:32, DodgeRatio:32, CritRatio:32, UnCritRatio:32,
    FinalHurtAdd:32, FinalHurtDel:32,
    SkillHurtAddRatio:32, SkillHurtDelRatio:32,
    FinalHurtAddRatio:32, FinalHurtDelRatio:32,
    _PvpHurtAddRatio:32, CritHurtAddRatio:32, CritHurtDelRatio:32,
    _BossHurtAddRatio:32,
    _AttAddRatio:32, _HpAddRatio:32,
    HeartRatio:32, _HeartResistRatio:32,
    _AttSpeedAddRatio:32, _SpeedAddRatio:32,
    _HpResume:32,
    _IceHurt:32, _IceDef:32,
    _ThunderHurt:32, _ThunderDef:32,
    _FireHurt:32, _FireDef:32,
    _WindHurt:32, _WindDef:32,
    _ExpAddRatio:32,
    ParryRatio:32, StabRatio:32,
    _PvpHurtDelRatio:32,
    HeartAddHurt:32, HeartDelHurt:32
    , _SuckBlood:32, _SuckBloodAdd:32, _SuckBloodMinus:32
    , _DefAddRatio:32, _HitAddRatio:32, _DodgeAddRatio:32
    , _CritAddRatio:32, _TenAddRatio:32,
    _BossHurtDelRatio:32, _MonHurtAddRatio:32, _MonHurtDelRatio:32,
    RestBin/binary>> = Bin,
    Attr = #attr{
        att = Att
        ,hit = Hit
        ,dodge = Dodge
        ,crit = Crit
        ,def = Def
        ,ten = Ten
        ,final_hurt_add = FinalHurtAdd
        ,final_hurt_del = FinalHurtDel
        ,hurt_add_ratio = HurtAddRatio
        ,hurt_del_ratio = HurtDelRatio
        ,hit_ratio = HitRatio
        ,dodge_ratio = DodgeRatio
        ,crit_ratio = CritRatio
        ,uncrit_ratio = UnCritRatio
        ,heart_ratio = HeartRatio
        ,heart_add_hurt = HeartAddHurt
        ,heart_del_hurt = HeartDelHurt
        ,stab_ratio = StabRatio
        ,parry_ratio = ParryRatio
        ,crit_hurt_add_ratio = CritHurtAddRatio
        ,crit_hurt_del_ratio = CritHurtDelRatio
        ,final_hurt_add_ratio = FinalHurtAddRatio
        ,final_hurt_del_ratio = FinalHurtDelRatio
        ,skill_hurt_add_ratio = SkillHurtAddRatio
        ,skill_hurt_del_ratio = SkillHurtDelRatio
    },
    {Attr, RestBin}.

