%%%-----------------------------------
%%% module  : pt
%%% @author  : xyao
%%% email   : jiexiaowen@gmail.com
%%% Created : 2011.06.30
%%% description: 协议公共函数
%%%-----------------------------------
-module(pt).
-include("attr.hrl").
-include("figure.hrl").
-include("common.hrl").

-export([
        read_string/1,                  %% 返回的是string()
        read_string_binary/1,           %% 返回的是binary()
        write_string/1,
        read_voice_bin/1,
        write_voice_bin/1,
        read_float/2,
        write_float/2,
        write_attr/1,
        write_attr_list/1,
        write_figure/1,
        write_picture/1,
        write_battle_attr/1,
        write_object_list/1,
        read_array/2,
        write_array/2,
        pack/2,
        pack/3,
        encode/3,
        encode/4
    ]).


%%读取字符串
read_string(Bin) ->
    case Bin of
        <<Len:16, Bin1/binary>> ->
            case Bin1 of
                <<Str:Len/binary-unit:8, Rest/binary>> ->
                    {unicode:characters_to_list(Str, utf8), Rest};
                _R1 ->
                    {[],<<>>}
            end;
        _R1 ->
            {[],<<>>}
    end.

%% 读取字符串 返回的是binary()
read_string_binary(Bin) ->
    case Bin of
        <<Len:16, Bin1/binary>> ->
            case Bin1 of
                <<Str:Len/binary-unit:8, Rest/binary>> ->
                    {Str, Rest};
                _R1 ->
                    {<<>>,<<>>}
            end;
        _R1 ->
            {<<>>,<<>>}
    end.

%% 读取语音信息
read_voice_bin(Bin) ->
    case Bin of
        <<Len:32, Bin1/binary>> ->
            case Bin1 of
                <<VoiceBin:Len/binary, Rest/binary>> ->
                    {VoiceBin, Rest};
                _R1 ->
                    {<<>>,<<>>}
            end;
        _R1 ->
            {<<>>,<<>>}
    end.

%% 读取浮点数,3位精度
read_float(Bin, Bit) ->
    <<Float:Bit, Rest/binary>> = Bin,
    {Float/1000, Rest}.

%% 读取数组
read_array(Fun, Bin) ->
    <<Len:16, RestBin/binary>> = Bin,
    read_array(0, Len, Fun, RestBin, []).
read_array(Max, Max, _, RestBin, Result) ->
    {lists:reverse(Result), RestBin};
read_array(Min, Max, Fun, Bin, Result) ->
    {One, RestBin} = Fun(Bin),
    read_array(Min+1, Max, Fun, RestBin, [One|Result]).

%% 打包数组
write_array(Fun, List) when is_list(List) ->
    Len = length(List),
    <<Len:16, (list_to_binary([Fun(E)||E<-List]))/binary>>.

%% 打包字符串
write_string(S) when is_list(S)->
    SB = unicode:characters_to_binary(S, utf8),
    L = byte_size(SB),
    <<L:16, SB/binary>>;

write_string(S) when is_binary(S)->
    L = byte_size(S),
    <<L:16, S/binary>>;

write_string(S) when is_integer(S)->
    SS = integer_to_list(S),
    SB = list_to_binary(SS),
    L = byte_size(SB),
    <<L:16, SB/binary>>;

write_string(_S) ->
    <<0:16, <<>>/binary>>.

%% 打包语音二进制
write_voice_bin(VoiceBin) when is_binary(VoiceBin) ->
    Len = byte_size(VoiceBin),
    <<Len:32, VoiceBin/binary>>.

%% 打包小数(无论是整数还是小数,都乘以1000)
write_float(Float, Bit) when is_float(Float) ->
    Int = round(Float * 1000),
    <<Int:Bit>>;
write_float(Float, Bit) when is_integer(Float) ->
    Int = round(Float * 1000),
    <<Int:Bit>>.

%% 打包属性
write_attr(Attr) ->
    write_attr_list(Attr).

%% 打包属性列表
write_attr_list(AttrList) when is_list(AttrList) ->
    List = [<<AttributeId:8, Value:32>> || {AttributeId, Value} <- AttrList, Value > 0],
    Len = length(List),
    AttrList_b = list_to_binary(List),
    <<Len:16, AttrList_b/binary>>;
write_attr_list(AttrMap) when is_map(AttrMap) ->
    Length = maps:size(AttrMap),
    AttrB = maps:fold(fun(Key,Value,Bin) -> <<Key:8, Value:32, Bin/binary>> end, <<>>, AttrMap),
    <<Length:16, AttrB/binary>>;
write_attr_list(Attr) when is_record(Attr, attr) ->
    #attr{
        att = Att, def = Def, hit = Hit, dodge = Dodge, crit = Crit, ten = Ten,
        hurt_add_ratio = HurtAddRatio, hurt_del_ratio = HurtDelRatio,
        hit_ratio = HitRatio, dodge_ratio = DodgeRatio,
        crit_ratio = CritRatio, uncrit_ratio = UnCritRatio,
        final_hurt_add = FinalHurtAdd, final_hurt_del = FinalHurtDel,
        skill_hurt_add_ratio = SkillHurtAddRatio, skill_hurt_del_ratio = SkillHurtDelRatio,
        final_hurt_add_ratio = FinalHurtAddRatio, final_hurt_del_ratio = FinalHurtDelRatio,
        pvp_hurt_add_ratio = PvpHurtAddRatio,
        crit_hurt_add_ratio = CritHurtAddRatio,
        crit_hurt_del_ratio = CritHurtDelRatio,
        boss_hurt_add_ratio = BossHurtAddRatio,

        att_add_ratio = AttAddRatio, hp_add_ratio = HpAddRatio,
        def_add_ratio = DefAddRatio, hit_add_ratio = HitAddRatio,
        dodge_add_ratio = DodgeAddRatio, crit_add_ratio = CritAddRatio,
        ten_add_ratio = TenAddRatio,

        heart_ratio = HeartRatio,
        heart_resist_ratio = HeartResistRatio,
        speed_add_ratio = SpeedAddRatio,
        hp_resume = HpResume, exp_add_ratio = ExpAddRatio,
        ice_hurt = IceHurt, ice_def = IceDef,
        thunder_hurt = ThunderHurt, thunder_def = ThunderDef,
        fire_hurt = FireHurt, fire_def = FireDef,
        wind_hurt = WindHurt, wind_def = WindDef,
        parry_ratio = ParryRatio, stab_ratio = StabRatio,
        pvp_hurt_del_ratio = PvpHurtDelRatio,
        heart_add_hurt = HeartAddHurt, heart_del_hurt = HeartDelHurt
        ,suck_blood = SuckBlood, suck_blood_add = SuckBloodAdd
        ,suck_blood_minus = SuckBloodMinus
        ,boss_hurt_del_ratio = BossHurtDelRatio
        ,mon_hurt_add_ratio = MonHurtAddRatio, mon_hurt_del_ratio = MonHurtDelRatio
    } = Attr,
    AttSpeedAddRatio = 0,
    <<
    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
    >>.

%% 打包形象（请修改相应的read_figure，测试专用）
write_figure(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,
        baby_vehicle = BabyVehicleId,
        husong_angel_id = AngelId,
        profile_photo_id = ProfilePhotoId,
        light_weapon_model = LightWeaponModel,
        power = Power,
        is_ride = IsRide,
        role_foster = Fosters,
        foster_skin = FSkins,
        sup_vip = SupVipType,
        follow_pet = FollowPet,
        follow_guard = FollowGuard,
        visus = Visus,
        nucleon_lv = NucleonLv,
        king = King,
        post = Post
        ,foster_sub_skin = FosterSubSkins
    } = Figure,
    StarSoulId = 0, % TODO 待删除
    DeityId = 0,    % TODO 待删除
    GodId = 0,      % TODO 待删除
    NameBin = write_string(Name),
    Fun = fun({Part, ModelId}) -> <<Part:8, ModelId:32>> end,
    LvModelBin = write_array(Fun, LvModel),
    Fun1 = fun({Part, FashionId, Color}) -> <<Part:8, FashionId:32, Color:32>> end,
    FashionModelBin = write_array(Fun1, FashionModel),
    GodWeaponModelBin = write_array(Fun, [{0,0}]), %% 神兵
    PictureBin = write_string(Picture),
    GuildNameBin = write_string(GuildName),
    PositionNameBin = write_string(PositionName),
    DressFun = fun({DressType, DressId}) -> <<DressType:8, DressId:32>> end,
    DressBin = write_array(DressFun, DressList),
    LoverNameBin = write_string(""),
    Fun2 = fun({Type, ModelId, PicId}) -> <<Type:8, ModelId:32, PicId:32>> end,
    LightWeaponModelBin = write_array(Fun2, LightWeaponModel),
    Fun3 = fun({FashionType, FashionId}) -> <<FashionType:8, FashionId:32>> end,
    BabyModelBin = write_array(Fun3, BabyModel),
    Fun4 = fun({FosterType,Grade}) -> <<FosterType:8, Grade:16>> end,
    FosterBin = write_array(Fun4, Fosters),
    Fun5 = fun({FosterType,{SkinId,Star}}) -> <<FosterType:8, SkinId:16, Star:16>> end,
    FSkinBin  = write_array(Fun5,FSkins),
    Fun6 = fun({NatureType, Tip, ShowPos}) -> <<NatureType:8, Tip:8, ShowPos:8>> end,
    NatureTipsBin  = write_array(Fun6, []),
    Fun7 = fun({FosterType,{SkinId,Star}}) ->
        <<FosterType:8, SkinId:16, Star:16>>;
        ({FosterType,_}) ->
        <<FosterType:8, 0:16, 0:16>>
    end,
    FosterSubSkinBin  = write_array(Fun7, FosterSubSkins),
    case FollowPet of [FollowPetId, FollowPetStage|_]-> skip; _ -> FollowPetId = 0, FollowPetStage = 0 end,
    case BabyIfShow of [ChildFollowStatus, ChildSex, AgeMonth] -> HeartLinkLevel = 0; _ -> ChildFollowStatus = 0, ChildSex = 0, AgeMonth = 0, HeartLinkLevel = 0 end,
    <<NameBin/binary, Sex:8, Realm:8, Career:8, Lv:16, Gm:8, Vip:8, Title:8, LvModelBin/binary,
    FashionModelBin/binary, PictureBin/binary, PictureVer:32,  GuildId:64, GuildNameBin/binary,
    Position:8, PositionNameBin/binary, Dsgt:32, WeaponEffect:32, WingFigure:32, DressBin/binary,
    Liveness:32, FairyFigure:32, Turn:8, TurnStage:8, JueWeiLv:8, GodWeaponModelBin/binary, 0:8,
    0:64, LoverNameBin/binary, ChildFollowStatus:8, ChildSex:8, AgeMonth:8, HeartLinkLevel:16 , BabyVehicleId:32, BabyModelBin/binary, AngelId:32, GodId:32, StarSoulId:16, ProfilePhotoId:32,
    DeityId:16, VipType:8, LightWeaponModelBin/binary, RoleId:64, Power:64,FosterBin/binary,IsRide:8,FSkinBin/binary,SupVipType:8,
    FollowPetId:32, FollowPetStage:8, FollowGuard:32, NatureTipsBin/binary,Visus:16,NucleonLv:8,King:8,Post:8,FosterSubSkinBin/binary>>.

%% 打包头像信息
write_picture(Info) ->
    case Info of
        #figure{
            id = RoleID,
            name = Name,
            sex = Sex,
            career = Career,
            turn = Turn,
            vip_type = VType,
            vip = Vip,
            sup_vip = SupVipType,
            lv = Lv,
            power = Power,
            picture = Picture,
            picture_ver = PictureVer,
            profile_photo_id = ProfilePhotoId,
            dress_list = DressList
        } ->
            PictureFrame =
            case lists:keyfind(2, 1, DressList) of
                {_DType, DId}  -> DId;
                _ -> 0
            end;
        #rec_picture{
            id = RoleID,
            name = Name,
            sex = Sex,
            career = Career,
            turn = Turn,
            vip_type = VType,
            vip = Vip,
            sup_vip = SupVipType,
            lv = Lv,
            power = Power,
            picture = Picture,
            picture_ver = PictureVer,
            profile_photo_id = ProfilePhotoId,
            picture_frame = PictureFrame
        } ->
            ok
    end,
    NameBin = write_string(Name),
    PictureBin = write_string(Picture),
    <<RoleID:64, NameBin/binary, Sex:8, Career:8, Turn:8, VType:8, Vip:8, Lv:16,
    Power:64, PictureBin/binary, PictureVer:32, ProfilePhotoId:32,
    PictureFrame:32, SupVipType:8>>.

%% 打包战斗属性
write_battle_attr(#battle_attr{hp=Hp, hp_lim=HpLim, speed=Speed, attr=Attr}) ->
    AttrBin = write_attr_list(Attr),
    <<Hp:64, HpLim:64, Speed:16, AttrBin/binary>>.

%% 打包(奖励/消耗)列表
write_object_list(ObjectList) when is_list(ObjectList) ->
    List    = [<<Type:8, ObjectTypeId:32, Num:32>> || {Type, ObjectTypeId, Num} <- ObjectList],
    Len     = length(List),
    ObjectList_b = list_to_binary(List),
    <<Len:16, ObjectList_b/binary>>.

%% 打包信息，添加消息头
%% Zip:1压缩0不压缩 默认不压缩, 当数据大于31个字节的时候，默认压缩
pack(Cmd, Data) ->
    pack(Cmd, Data, 0).
pack(Cmd, Data, _Zip) ->
    % case Zip == 1 of %orelse byte_size(Data) > 31 of
    %     true ->
    %         Data1 = zlib:compress(Data),
    %         L = byte_size(Data1) + 7,
    %         <<L:32, Cmd:16, 1:8, Data1/binary>>;
    %     false ->
    %         L = byte_size(Data) + 7,
    %         <<L:32, Cmd:16, 0:8, Data/binary>>
    % end.
    L = byte_size(Data) + 7,
    <<L:32, Cmd:16, 0:8, Data/binary>>.

%% 打包信息，添加消息头
%% Zip:1压缩0不压缩 默认不压缩, 当数据大于31个字节的时候，默认压缩
%% 曾总vip包间接口 你们别用
encode(Cmd, Data, _SrcData) ->
    encode(Cmd, Data, _SrcData, 0).
encode(Cmd, Data, _SrcData, Zip) ->
    %lib_misc:print_out(Cmd, Data, _SrcData),
    pack(Cmd, Data, Zip).
