--***********************************************************
--**                    THE INDIE STONE                    **
--***********************************************************

require "TimedActions/ISBaseTimedAction"

ISWashVehicle = ISBaseTimedAction:derive("ISWashVehicle")

-----

local function predicateHasWater(item)
    return instanceof(item, "DrainableComboItem") and item:isWaterSource() and item:getDrainableUsesInt() > 0
end

local ITEMS = ArrayList.new()

local function getWaterItems(character)
	local inventory = character:getInventory()
	ITEMS:clear()
	return inventory:getAllEvalRecurse(predicateHasWater, ITEMS)
end

local AREAS = {
	{ id = "Front", area = "Engine" },
	{ id = "Rear", area = "TruckBed" },
	{ id = "Left", area = "SeatLeft" },
	{ id = "Left", area = "SeatFrontLeft" },
	{ id = "Right", area = "SeatRight" },
	{ id = "Right", area = "SeatFrontRight" }
}

local function getWaterAmountForItems(items)
	local total = 0
	for i=1,items:size() do
		local item = items:get(i-1)
		total = total + item:getDrainableUsesInt()
	end
	return total
end

function ISWashVehicle.getWaterAmountForPlayer(character)
	local items = getWaterItems(character)
	return getWaterAmountForItems(items)
end

ISWashVehicle.BLOOD_PER_WATER = 5

function ISWashVehicle.getWaterAmountForArea(vehicle, id)
	local intensity = vehicle:getBloodIntensity(id)
	return round(intensity * 100 / ISWashVehicle.BLOOD_PER_WATER)
end

function ISWashVehicle.chooseArea(character, vehicle)
	local waterTotal = ISWashVehicle.getWaterAmountForPlayer(character)
	for _,area in ipairs(AREAS) do
		if vehicle:getScript():getAreaById(area.area) and vehicle:getBloodIntensity(area.id) > 0 then
			if waterTotal >= 1 then
				return area
			end
		end
	end
	return nil
end

function ISWashVehicle.hasBlood(vehicle)
	for _,area in ipairs(AREAS) do
		if vehicle:getScript():getAreaById(area.area) and (vehicle:getBloodIntensity(area.id) > 0) then
			return true
		end
	end
	return false
end

-----

function ISWashVehicle:isValid()
	return self.vehicle:isInArea(self.area, self.character)
end

function ISWashVehicle:waitToStart()
	self.character:faceThisObject(self.vehicle)
	return self.character:shouldBeTurning()
end

function ISWashVehicle:update()
	self:setCurrentTime(1000 - (1000 * self.vehicle:getBloodIntensity(self.id)));
	if self.vehicle:getBloodIntensity(self.id) <= 0 then
		self:forceComplete()
		return
	end

	if self.character:isTimedActionInstant() then
		local waterAvailable = ISWashVehicle.getWaterAmountForPlayer(self.character)
		if waterAvailable < 1 then
			self:forceStop()
			return
		end
		local waterUsed = math.ceil(self.vehicle:getBloodIntensity(self.id) * 100 / ISWashVehicle.BLOOD_PER_WATER)
		waterUsed = math.min(waterUsed, waterAvailable)
		local bloodRemoved = waterUsed * ISWashVehicle.BLOOD_PER_WATER
		self:removeBlood(bloodRemoved, waterUsed)
		-- Don't forceComplete(); there is a delay before the client command is processed (even in singleplayer).
		return
	end

	self.character:faceThisObject(self.vehicle)
	self.character:setMetabolicTarget(Metabolics.HeavyDomestic)

	self.accumulator = self.accumulator + getGameTime():getMultiplier()
	local TICKS = 15
	if self.accumulator < TICKS then return end
	local bloodRemoved = math.floor(self.accumulator / (TICKS/3))

	self.waterAccumulator = self.waterAccumulator + bloodRemoved
	local waterUsed = math.floor(self.waterAccumulator / ISWashVehicle.BLOOD_PER_WATER)
	if waterUsed < 1 then
		return
	end

	local waterAvailable = ISWashVehicle.getWaterAmountForPlayer(self.character)
	if waterAvailable < 1 then
		self:forceStop()
		return
	end

	waterUsed = math.min(waterUsed, waterAvailable)
	self.waterAccumulator = math.max(self.waterAccumulator - waterUsed * ISWashVehicle.BLOOD_PER_WATER, 0.0)

	bloodRemoved = waterUsed * ISWashVehicle.BLOOD_PER_WATER
	self.accumulator = math.max(self.accumulator - bloodRemoved * TICKS, 0.0)

	self:removeBlood(bloodRemoved, waterUsed)
end

function ISWashVehicle:removeBlood(bloodRemoved, waterUsed)
	local intensity = self.vehicle:getBloodIntensity(self.id) - bloodRemoved * 0.01
	local intensity2 = round(intensity * 100)
	if intensity2 == self.amountSent then
		return
	end

	self:useWater(waterUsed)

	local args = { vehicle = self.vehicle:getId(), id = self.id, intensity = intensity }
	sendClientCommand(self.character, 'vehicle', 'setBloodIntensity', args)
	self.amountSent = intensity2
end

function ISWashVehicle:useWater(units)
	local waterItems = getWaterItems(self.character)
	for i=1,waterItems:size() do
		local item = waterItems:get(i-1)
		local used = math.min(units, item:getDrainableUsesInt())
		item:setUsedDelta(item:getUsedDelta() - item:getUseDelta() * used)
		if item:getDrainableUsesInt() <= 0.0 then
			item:setUsedDelta(0.0)
			item:Use()
		end
		units = units - used
	end
end

function ISWashVehicle:start()
	self:setActionAnim("VehicleWash")
	self:setOverrideHandModels(nil, nil)
	self.accumulator = 0.0
	self.waterAccumulator = 0.0
	self.sound = self.character:playSound("CleanBloodVehicle")
end

function ISWashVehicle:stop()
	self.character:stopOrTriggerSound(self.sound)
	ISBaseTimedAction.stop(self)
end

function ISWashVehicle:perform()
	self.character:stopOrTriggerSound(self.sound)
	local area = ISWashVehicle.chooseArea(self.character, self.vehicle)
	if area then
		-- Add in reverse order
		ISTimedActionQueue.addAfter(self, ISWashVehicle:new(self.character, self.vehicle, area.id, area.area))
		ISTimedActionQueue.addAfter(self, ISPathFindAction:pathToVehicleArea(self.character, self.vehicle, area.area))
	end
	-- needed to remove from queue / start next.
	ISBaseTimedAction.perform(self)
end

function ISWashVehicle:new(character, vehicle, id, area)
	local o = {}
	setmetatable(o, self)
	self.__index = self
	o.character = character
	o.vehicle = vehicle
	o.id = id
	o.area = area
	o.maxTime = 1000
	return o
end

