local MakePlayerCharacter = require "prefabs/player_common"
local FatBadge = require "widgets/fatbadge"
local easing = require "easing"


local assets = {
        Asset( "SOUND", "sound/woodie.fsb" ),

        Asset( "ANIM", "anim/warmbear.zip" ),
        Asset( "ANIM", "anim/ghost_warmbear_build.zip" ),

        Asset( "ANIM", "anim/butter_meter.zip" ),
}

local prefabs = {} --附带的prefab
local start_inv = { --出生自带物品
    "honeycomb",
    "bugnet",
    "honeyspear",
    -- Custom starting items
}

local MaxSpeed = 1.25 -- 最高速度
local AbsSpeed = 0.4 -- 速度上下限的差值
local AbsorbAmount = 0.3 -- 伤害吸收值
local ColdDec = {value = 0, set = -.3, reset = 0} -- 冬季脂肪掉落率
local MoistureDec = {value = 0, [0] = 0, [1] = 0, [2] = -.2, [3] = -.4, [4] = -.6, reset = 0}  -- 潮湿的过程中脂肪消耗
local FatToHealth = {value = 0, low = -1.5, med = -.6, high = 0} -- 脂肪转化为血量参数
local HUNGERRATE = {LoseFat = .1, RaisingFatMed = 1.5, RaisingFatFast = 2.5} -- 脂肪转化过程中对应的饥饿消耗率
local FATRAISEVAL = {LoseFat = -.5, RaisingFatMed = .8, RaisingFatFast = 1.4} -- 饥饿转化脂肪的速率
local HUNGERTOFATVAL = {LoseFat = 50, RaisingFatMed = 100, RaisingFatFast = 130} -- 饥饿转化脂肪的上下限
local FATVALUE = {
    eel = 2, fish = 1, honey = 1, taffy = 2, waffles = 8, butter = 15,
    honeyham = 5, meatballs = 2, fishtacos = 3, bonestew = 10, cookedmeat = 2,
    eel_cooked = 2, fishsticks = 5, corn_cooked = 1, fish_cooked = 1, honeynuggets = 3,
    trunk_winter = 5, trunk_summer = 5, trunk_cooked = 5, turkeydinner = 8,
    mandrakesoup = 20, frogglebunwich = 2, cookedsmallmeat = 1, drumstick_cooked = 1,
    deerclops_eyeball = 10,
    corn = -1, carrot = -1, kabobs = -2, petals = -3, berries = -1, foliage = -3,
    powcake = -5, red_cap = -5, wetgoop = -6, durian = -10, eggplant = -1, blue_cap = -5,
    wormlight = -3, green_cap = -5, rottenegg = -10, cave_banana = -2, pomegranate = -5,
    fruitmedley = -5, flowersalad = -6, cactus_meat = -10, petals_evil = -15,
    carrot_cooked = -1, cactus_flower = -5, durian_cooked = -8, spoiled_food = -10,
    berries_cooked = -1, red_cap_cooked = -1, jammypreserves = -4, monsterlasagna = -15,
    eggplant_cooked = -1, blue_cap_cooked = -1, green_cap_cooked = -1, cave_banana_cooked = -1,
    pomegranate_cooked = -2, cactus_meat_cooked = -5,
}

local function GetFat(inst)
    if inst.components.fat ~= nil then
        return inst.components.fat:GetCurrent()
    elseif inst.player_classified ~= nil then
        return inst.player_classified.currentfat:value()
    else
        return 0
    end
end

local function GetFatMax(inst)
    if inst.components.fat ~= nil then
        return inst.components.fat:GetMax()
    elseif inst.player_classified ~= nil then
        return inst.player_classified.maxfat:value()
    else
        return 0
    end
end

local function GetFatRateScale(inst)
    if inst.components.fat ~= nil then
        return inst.components.fat:GetRateScale()
    elseif inst.player_classified ~= nil then
        return inst.player_classified.fatratescale:value()
    else
        return RATE_SCALE.NEUTRAL
    end
end

local function oneat(inst, food)--吃东西的时候的函数
    --吃东西的时候的函数
    if food and food.components.edible and FATVALUE[food.prefab] ~= nil then
        inst.components.fat:DoDelta(FATVALUE[food.prefab], false)
        if food.prefab == "honey" then inst.components.sanity:DoDelta(2, false) end --honey give extra 2 sanity
    end
end

local function CalcSanityAura(inst, observer)
    --print(tostring(inst.components.friends ~= nil)..tostring(inst.components.friends.friends[observer.name])..tostring(observer.name))
    return (inst.components.friends ~= nil and inst.components.friends.friends[observer.name] 
        and inst.components.friends.friends[observer.name] > 200 and (TUNING.SANITYAURA_MED * (inst.components.friends.friends[observer.name] - 200) / 1000))
        or 0
end

local function HungerAndFat(inst)
    if inst.components.hunger.current < HUNGERTOFATVAL.LoseFat then
        if inst.components.fat.current > 0 then
            return FATRAISEVAL.LoseFat, 0
        else
            return 0, TUNING.WILSON_HUNGER_RATE
        end
    elseif inst.components.hunger.current > HUNGERTOFATVAL.RaisingFatFast then
        if inst.components.fat.current < inst.components.fat.max  then
            return FATRAISEVAL.RaisingFatFast, TUNING.WILSON_HUNGER_RATE * HUNGERRATE.RaisingFatFast
        else
            return 0, TUNING.WILSON_HUNGER_RATE
        end
    elseif inst.components.hunger.current > HUNGERTOFATVAL.RaisingFatMed then
        if inst.components.fat.current < inst.components.fat.max  then
            return FATRAISEVAL.RaisingFatMed, TUNING.WILSON_HUNGER_RATE * HUNGERRATE.RaisingFatMed
        else
            return 0, TUNING.WILSON_HUNGER_RATE
        end
    else
        return 0, TUNING.WILSON_HUNGER_RATE
    end
    --return inst.components.fat:GetRate(), inst.components.hunger.hungerrate
end

local function FatCtrl(inst)
    if compatible.temperature() <= 25 then 
        ColdDec.value = ColdDec.set
    else
        ColdDec.value = ColdDec.reset
    end
    if inst.components.moisture then
        local seg = inst.components.moisture:GetSegs()
        MoistureDec.value = MoistureDec[seg]
        if MoistureDec.value == nil then MoistureDec.value = 0 end
    end
    local rate_f, rate_h = HungerAndFat(inst)
    -- Debug("HungerToFat:"..rate_f.." ColdDec:"..ColdDec.value.." MoistureDec:"..MoistureDec.value.." FatToHealth:"..FatToHealth.value)
    rate_f = rate_f + ColdDec.value + FatToHealth.value + MoistureDec.value -- + SanityDec
    -- Debug({"Fat Rate: ", rate_f, "Hunger Rate: ", rate_h})
    if inst.components.fat:GetRate() ~= rate_f then 
        inst.components.fat:SetRate(rate_f)
        -- if rate_f ~= 0 then inst.components.fat:Start() end 
    end
    if inst.components.hunger.hungerrate ~= rate_h then 
        inst.components.hunger:SetRate(rate_h) 
        if rate_h == 0 then -- 不消耗饱食度，暂停饥饿
            inst.components.hunger:Pause()
        else
            inst.components.hunger:Resume()
        end
    end
end

local function OnHealthDelta(inst, data)
    -- Debug("OnHealthDelta")
    if inst.components.fat.current > 0 then
        if data.newpercent < 0.33 then
            if FatToHealth.value ~= FatToHealth.low then 
                FatToHealth.value = FatToHealth.low
                inst.components.health:StartRegen(.6, 3, true)
                FatCtrl(inst)
                -- print("StartRegen 1")
                if data.newpercent < data.oldpercent then inst.components.talker:Say("I try my best to alive.") end
            end
            if data.newpercent < 0.1 and data.newpercent < data.oldpercent then inst.components.talker:Say("I'm dying!!! I gonna die!!!") end
        elseif data.newpercent < 0.55 then
            if FatToHealth.value ~= FatToHealth.med then 
                FatToHealth.value = FatToHealth.med
                inst.components.health:StartRegen(.5, 6, true)
                FatCtrl(inst)
                -- print("StartRegen 0.5")
                if data.newpercent > data.oldpercent then inst.components.talker:Say("I feel better now.") end
                if data.newpercent < data.oldpercent then inst.components.talker:Say("I can handle it.") end
            end
        else
            if FatToHealth.value ~= FatToHealth.high then 
                FatToHealth.value = FatToHealth.high
                inst.components.health:StopRegen()
                FatCtrl(inst)
                -- print("StopRegen")
                if data.newpercent > data.oldpercent then inst.components.talker:Say("Wound already cured~") end
            end
        end
    else
        if FatToHealth.value ~= FatToHealth.high then 
            FatToHealth.value = FatToHealth.high
            inst.components.health:StopRegen()
            FatCtrl(inst)
            -- print("StopRegen")
            if data.newpercent < .5 then inst.components.talker:Say("I'm too weak to heal myself...") end
        end
    end
end

local function OnFatDelta(inst, data)
    -- Debug("OnFatDelta")
    if compatible.ismastersim() then
        -- 主机更新伤害吸收，向外辐射的温度，防寒，防暑
        if(inst.components.health.SetAbsorbAmount ~= nil) then inst.components.health:SetAbsorbAmount(data.newpercent * AbsorbAmount)--设置新的伤害吸收值,DLC函数不一样要检测一下
        elseif(inst.components.health.SetAbsorptionAmount ~= nil) then inst.components.health:SetAbsorptionAmount(data.newpercent * AbsorbAmount)
        end
        if compatible.isdst then
            inst.components.heater.heat = compatible.temperature() <= 25 and 60 * data.newpercent or 0
        end
        if data.newpercent >= .75 then--有0.65的脂肪值就可以保证不会冻上
            if compatible.temperature() <= 25 then --环境温度小于25
                if inst.components.temperature.settemp == nil then inst.components.temperature:SetTemp(25) end--未设置设置
            else
                if inst.components.temperature.settemp ~= nil then inst.components.temperature:SetTemp(nil) end--大于15且设置了就取消
            end
        else--脂肪少于0.75
            if inst.components.temperature.settemp ~= nil then inst.components.temperature:SetTemp(nil) end--取消不冻
            inst.components.temperature.inherentinsulation = 200 * data.newpercent --获得延时
        end
        -- 防暑
        if inst.components.temperature.inherentsummerinsulation ~= nil  and compatible.temperature() >= 45 then 
            inst.components.temperature.inherentsummerinsulation = 100*(.35-data.newpercent) 
        end
        -- 防潮湿掉脑残
        if compatible.isROG or compatible.isSW then
            if data.newpercent < .75 then
                local p = data.newpercent * 1.33
                p = (1 - p) * 1.2
                inst.components.sanity.moisture_mult = (p>0) and p or 0
            else
                inst.components.sanity.moisture_mult = 0
            end
        end
        -- 更新脂肪值增减速率
        FatCtrl(inst)
    end

    if not compatible.isdst or inst == ThePlayer then -- 作为人物所在端更新人物大小，碰跑速度
        -- Debug("Change Size")
        local percent = inst:GetFat()/inst:GetFatMax()
        local scale = easing.linear(1, 1, percent * .25, 1)--设置大小
        inst.Transform:SetScale(scale,scale,scale)
        if inst.components.locomotor then inst.components.locomotor.runspeed = TUNING.WILSON_RUN_SPEED * (percent * (-AbsSpeed) + MaxSpeed) end--设置速度
    end
end

local function common_postinit(inst)

    inst.MiniMapEntity:SetIcon( "warmbear.tex" )

    inst:AddTag("ghostwithhat")
    -- 添加脂肪值的状态仪表，有status的重载实现，不在这里实现
    inst:AddTag("fat")
    inst:AddTag("warmbear")
    -- 由于没有使用fatreplica所以直接挂在人物句柄下面，用于获取脂肪值关键的几个数据
    inst.GetFat = GetFat
    inst.GetFatMax = GetFatMax
    inst.GetFatRateScale = GetFatRateScale

    inst:ListenForEvent("fatdelta", OnFatDelta, inst) -- 监听脂肪值的变化，包括了主机和从机两个部分
    inst.components.talker.colour = Vector3(255/255, 136/255, 17/255)

    -- inst:ListenForEvent("playeractivated", OnPlayerActivated)
    -- inst:ListenForEvent("playerdeactivated", OnPlayerDeactivated)
end

local function master_postinit(inst)
    -- 添加脂肪值并开始计算
    inst:AddComponent("fat")
    inst.soundsname = "woodie"

    if compatible.isdst then  -- 不是多人不需要散发热量和脑残恢复光环
        inst:AddComponent("heater")
        inst.components.heater.heat = 50
        inst:AddComponent("friends")
        inst:AddComponent("sanityaura")
        inst.components.sanityaura.aurafn = CalcSanityAura
    end
    
    -- if inst.components.moisture ~= nil then--防水设置
    --     inst:AddComponent("waterproofer")
    --     inst.components.waterproofer:SetEffectiveness(TUNING.WATERPROOFNESS_SMALLMED)
    -- end

    -- Stats    
    inst.components.fat:SetMax(200)
    inst.components.sanity:SetMax(90)
    inst.components.hunger:SetMax(150)
    inst.components.health:SetMaxHealth(180)

    inst.components.combat:SetAttackPeriod(.5)--set attack period
    inst.components.combat:SetDefaultDamage(40)--set the hand damage
    --inst.components.sanity.night_drain_mult = 0.95
    inst.components.sanity.neg_aura_mult = 0.85
    -- 绑定吃东西时脂肪值的计算函数
    inst.components.eater:SetOnEatFn(oneat)

    inst:ListenForEvent("healthdelta", OnHealthDelta, inst) -- 监听血量以用脂肪进行恢复

    inst.components.fat:Start()
    -- inst:ListenForEvent("ms_respawnedfromghost", onrespawnedfromghost)
    -- inst:ListenForEvent("ms_becameghost", onbecameghost)

    -- onrespawnedfromghost(inst)
end

local function fn(inst)  -- 单机两个都需要初始化
    common_postinit(inst)
    master_postinit(inst)
end

if compatible.isdst then
    return MakePlayerCharacter("warmbear", prefabs, assets, common_postinit, master_postinit, start_inv)
else
    return MakePlayerCharacter("warmbear", prefabs, assets, fn, start_inv)
end
