-- **************************************************
-- ██████  ██████   █████  ██    ██ ███████ ███    ██ 
-- ██   ██ ██   ██ ██   ██ ██    ██ ██      ████   ██ 
-- ██████  ██████  ███████ ██    ██ █████   ██ ██  ██ 
-- ██   ██ ██   ██ ██   ██  ██  ██  ██      ██  ██ ██ 
-- ██████  ██   ██ ██   ██   ████   ███████ ██   ████
-- **************************************************

require "TimedActions/ISBaseTimedAction"

local OnApplyBandage = ISApplyBandage.new
local OnComfreyCataplasm = ISComfreyCataplasm.new
local OnGarlicCataplasm = ISGarlicCataplasm.new
local OnPlantainCataplasm = ISPlantainCataplasm.new
local OnCleanBurn = ISCleanBurn.new
local OnDisinfect = ISDisinfect.new
local OnRemoveBullet = ISRemoveBullet.new
local OnRemoveGlass = ISRemoveGlass.new
local OnSplint = ISSplint.new
local OnStitch = ISStitch.new

local function overrideTime(doctorLevel, lowBuff, highBuff, baseTime)
    local offset = 0
    if doctorLevel <= 3 then offset = doctorLevel * lowBuff
    else offset = (90 + doctorLevel * highBuff) end
    return (baseTime - offset) * SandboxVars.FirstAidOverhaul.SpeedMultiplier
end

function ISApplyBandage:new(doctor, otherPlayer, item, bodyPart, doIt)
	local o = OnApplyBandage(self, doctor, otherPlayer, item, bodyPart, doIt)

    if not doctor:isTimedActionInstant() then
        o.maxTime = overrideTime(o.doctorLevel, 30, 9, 210)
    end

    return o
end

function ISCleanBurn:new(doctor, otherPlayer, bandage, bodyPart)
	local o = OnCleanBurn(self, doctor, otherPlayer, bandage, bodyPart)

    if not doctor:isTimedActionInstant() then
        o.maxTime = overrideTime(o.doctorLevel, 40, 12, 430)
    end

    return o
end

function ISComfreyCataplasm:new(doctor, otherPlayer, item, bodyPart)
	local o = OnComfreyCataplasm(self, doctor, otherPlayer, item, bodyPart)

    if not doctor:isTimedActionInstant() then
        o.maxTime = overrideTime(o.doctorLevel, 30, 9, 210)
    end

    return o
end

function ISDisinfect:new(doctor, otherPlayer, alcohol, bodyPart)
	local o = OnDisinfect(self, doctor, otherPlayer, alcohol, bodyPart)

    if not doctor:isTimedActionInstant() then
        o.maxTime = overrideTime(o.doctorLevel, 30, 9, 210)
    end

    return o
end

function ISGarlicCataplasm:new(doctor, otherPlayer, item, bodyPart)
	local o = OnGarlicCataplasm(self, doctor, otherPlayer, item, bodyPart)

    if not doctor:isTimedActionInstant() then
        o.maxTime = overrideTime(o.doctorLevel, 30, 9, 210)
    end

    return o
end

function ISPlantainCataplasm:new(doctor, otherPlayer, item, bodyPart)
	local o = OnPlantainCataplasm(self, doctor, otherPlayer, item, bodyPart)

    if not doctor:isTimedActionInstant() then
        o.maxTime = overrideTime(o.doctorLevel, 30, 9, 210)
    end

    return o
end

function ISRemoveBullet:new(doctor, otherPlayer, bodyPart)
	local o = OnRemoveBullet(self, doctor, otherPlayer, bodyPart)

    if not doctor:isTimedActionInstant() then
        o.maxTime = overrideTime(o.doctorLevel, 40, 12, 430)
    end

    return o
end

function ISRemoveGlass:new(doctor, otherPlayer, bodyPart, hands)
    local o = OnRemoveGlass(self, doctor, otherPlayer, bodyPart, hands)

    if not doctor:isTimedActionInstant() then
        if not hands then
            o.maxTime = overrideTime(o.doctorLevel, 40, 9, 270)
        else
            o.maxTime = overrideTime(o.doctorLevel, 40, 9, 420)
        end
    end

    return o
end

function ISSplint:new(doctor, otherPlayer, rippedSheet, plank, bodyPart, doIt)
	local o = OnSplint(self, doctor, otherPlayer, rippedSheet, plank, bodyPart, doIt)

    if not doctor:isTimedActionInstant() then
        o.maxTime = overrideTime(o.doctorLevel, 40, 9, 260)
    end

    local doctorLevel = doctor:getPerkLevel(Perks.Doctor)
    if doctorLevel < SandboxVars.FirstAidOverhaul.FractureTreatmentLevel then
        doctor:Say(getText("IGUI_LowLevel"))
        o.ignoreAction = true
    end

    return o
end

function ISStitch:new(doctor, otherPlayer, item, bodyPart, doIt)
	local o = OnStitch(self, doctor, otherPlayer, item, bodyPart, doIt)

    local baseTime = 200
    if doctor:getInventory():contains("SutureNeedleHolder") or (item and item:getType() == "SutureNeedle") then
        baseTime = 150
    end

    if not doctor:isTimedActionInstant() then
        o.maxTime = overrideTime(o.doctorLevel, 30, 9, baseTime + 90)
    end

    local doctorLevel = doctor:getPerkLevel(Perks.Doctor)
    if doctorLevel < SandboxVars.FirstAidOverhaul.FractureTreatmentLevel then
        doctor:Say(getText("IGUI_LowLevel"))
        o.ignoreAction = true
    end

    return o
end

local OnPerformBandage = ISApplyBandage.perform
local OnPerformComfreyCataplasm = ISComfreyCataplasm.perform
local OnPerformGarlicCataplasm = ISGarlicCataplasm.perform
local OnPerformPlantainCataplasm = ISPlantainCataplasm.perform
local OnPerformCleanBurn = ISCleanBurn.perform
local OnPerformDisinfect = ISDisinfect.perform
local OnPerformRemoveBullet = ISRemoveBullet.perform
local OnPerformSplint = ISSplint.perform
local OnPerformStitch = ISStitch.perform

local function rollDice()
    local playerObj = getPlayer(); if not playerObj then return true end
    if playerObj:HasTrait("MedicalPractitioner") then return true end
    local doctorLevel = playerObj:getPerkLevel(Perks.Doctor)
    if doctorLevel >= SandboxVars.FirstAidOverhaul.FailureLevelThreshold then return true end
    if ZombRand(0, 100) < SandboxVars.FirstAidOverhaul.FailureChance then return false end
    return true
end

local function hasFailureChance(bodyPart)
    if bodyPart:isCut() == true then return true end
    if bodyPart:getBiteTime() > 0 then return true end
    if bodyPart:isBurnt() == true then return true end
    return false
end

local function tryDoubleXP(playerObj, otherPlayer, amount)
    if not getWorld():getGameMode() == "Multiplayer" then return end
    if playerObj:getUsername() == otherPlayer:getUsername() then return end
    playerObj:getXp():AddXP(Perks.Doctor, amount)
end

function ISApplyBandage:perform()
    if hasFailureChance(self.bodyPart) == false or (hasFailureChance(self.bodyPart) == true and rollDice() == true) then
        OnPerformBandage(self)
        if not string.contains(self.bodyPart:getType():getBandageModel(), "Foot") then
            tryDoubleXP(self.character, self.otherPlayer, 15)
        end
    else
        self.character:Say(getText("IGUI_FailedAttempt"))
        ISHealthPanel.setBodyPartActionForPlayer(self.otherPlayer, self.bodyPart, nil, nil, nil)
        ISBaseTimedAction.perform(self)
    end
end

function ISCleanBurn:perform()
    if rollDice() == true then
        OnPerformCleanBurn(self)
        tryDoubleXP(self.character, self.otherPlayer, 15)
    else
        self.character:Say(getText("IGUI_FailedAttempt"))
        ISHealthPanel.setBodyPartActionForPlayer(self.otherPlayer, self.bodyPart, nil, nil, nil)
        ISBaseTimedAction.perform(self)
    end
end

function ISComfreyCataplasm:perform()
    OnPerformComfreyCataplasm(self)
    tryDoubleXP(self.character, self.otherPlayer, 15)
end

function ISDisinfect:perform()
    OnPerformDisinfect(self)
    tryDoubleXP(self.character, self.otherPlayer, 15)
end

function ISGarlicCataplasm:perform()
    OnPerformGarlicCataplasm(self)
    tryDoubleXP(self.character, self.otherPlayer, 15)
end

function ISPlantainCataplasm:perform()
    OnPerformPlantainCataplasm(self)
    tryDoubleXP(self.character, self.otherPlayer, 15)
end

function ISRemoveBullet:perform()
    if rollDice() == true then
        OnPerformRemoveBullet(self)
        tryDoubleXP(self.character, self.otherPlayer, 15)
    else
        self.character:Say(getText("IGUI_FailedAttempt"))
        ISHealthPanel.setBodyPartActionForPlayer(self.otherPlayer, self.bodyPart, nil, nil, nil)
        ISBaseTimedAction.perform(self)
    end
end

function ISRemoveGlass:perform() -- FULL OVERRIDE, GAME DOESN'T ALLOW TO REDUCE XP.

    ISBaseTimedAction.perform(self)

    if self.character:HasTrait("Hemophobic") then
        self.character:getStats():setPanic(self.character:getStats():getPanic() + 50)
    end

    local addPain = (30 - (self.doctorLevel * 1))
    if self.doctor:getAccessLevel() ~= "None" then
        self.bodyPart:setAdditionalPain(self.bodyPart:getAdditionalPain() + addPain)
    end

    if self.handPain then
        self.bodyPart:setAdditionalPain(self.bodyPart:getAdditionalPain() + 30)
    end
    self.bodyPart:setHaveGlass(false)

    if isClient() then
        sendRemoveGlass(self.character, self.otherPlayer, self.bodyPart, self.handPain)
    end

    ISHealthPanel.setBodyPartActionForPlayer(self.otherPlayer, self.bodyPart, nil, nil, nil)
end

function ISSplint:perform()
    OnPerformSplint(self)
    tryDoubleXP(self.character, self.otherPlayer, 15)
end

function ISStitch:perform()
    if rollDice() == true then
        OnPerformStitch(self)
        tryDoubleXP(self.character, self.otherPlayer, 15)
    else
        self.character:Say(getText("IGUI_FailedAttempt"))
        ISHealthPanel.setBodyPartActionForPlayer(self.otherPlayer, self.bodyPart, nil, nil, nil)
        ISBaseTimedAction.perform(self)
    end
end