--- scripts/BaleConvoyer.lua
--- Author: K1utzer

BaleConvoyer = {}

local DEBUG = true

function Log(formatString, ...)
    if DEBUG then
        print(string.format(formatString, ...))
    end
end

local function getBaleObject(nodeId)
    local current = nodeId
    while current ~= 0 do
        local obj = g_currentMission.nodeToObject[current]
        if obj and obj:isa(Bale) then
            return obj
        end
        current = getParent(current)
    end
    return nil
end


local function getBaleLength(baleObj)
    return baleObj.length or 3
end

function TurnOnVehicle:getIsTurnedOn()
    local spec = self.spec_turnOnVehicle
    return spec.isAlwaysTurnedOn or spec.isTurnedOn
end

local function getBaleDimensions(baleObj)
    return baleObj.width or 0, baleObj.height or 0, baleObj.length or 3
end


Log("[BaleConvoyer] Module loaded")

function BaleConvoyer.prerequisitesPresent(specializations)
    return true
end

function BaleConvoyer.initSpecialization()
	local schema = Vehicle.xmlSchema
	schema:register(XMLValueType.FLOAT,      "vehicle.baleConvoyer#maxSize",       "Maximum bale size",      1.0)
    schema:register(XMLValueType.NODE_INDEX, "vehicle.baleConvoyer#triggerNode",   "Trigger node",           nil)
    schema:register(XMLValueType.NODE_INDEX, "vehicle.baleConvoyer#balePlaceNode", "Bale place node",        nil)
end



function BaleConvoyer.registerFunctions(vehicleType)
	SpecializationUtil.registerFunction(vehicleType, "baleTriggerCallback", BaleConvoyer.baleTriggerCallback)
end

--- Register load and update event listeners
function BaleConvoyer.registerEventListeners(vehicleType)
    SpecializationUtil.registerEventListener(vehicleType, "onLoad", BaleConvoyer)
	SpecializationUtil.registerEventListener(vehicleType, "onUpdate", BaleConvoyer)
	SpecializationUtil.registerEventListener(vehicleType, "onDelete", BaleConvoyer)
	Log("[BaleConvoyer] Registered onLoad & onUpdate listeners")
end


function BaleConvoyer:onDelete()
	if self.spec_baleConvoyer.triggerNode ~= nil then
		removeTrigger(self.spec_baleConvoyer.triggerNode)
	end
end

function BaleConvoyer:onLoad(savegame)

	local maxSize       = self.xmlFile:getValue("vehicle.baleConvoyer#maxSize",       1.0)
    local triggerNode   = self.xmlFile:getValue("vehicle.baleConvoyer#triggerNode",   nil, self.components, self.i3dMappings)
    local balePlaceNode = self.xmlFile:getValue("vehicle.baleConvoyer#balePlaceNode", nil, self.components, self.i3dMappings)
	self.spec_baleConvoyer = {
		isActive = false,
		balesInTrigger = {},
		processedBales = nil,
        maxSize       = maxSize,
        triggerNode   = triggerNode,
        balePlaceNode = balePlaceNode
    }
	if triggerNode ~= nil then
        addTrigger(triggerNode, "baleTriggerCallback", self)
    else
        Log("[BaleConvoyer] Bale convoyer needs a valid trigger!")
    end
	self.cleanupMovedBales = BaleConvoyer.cleanupMovedBales
	Log(
		"[BaleConvoyer] Loaded baleConvoyer: maxSize=%.2f, triggerNode=%s, balePlaceNode=%s",
		maxSize,
		tostring(triggerNode),
		tostring(balePlaceNode)
	)
end

function BaleConvoyer:baleTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId)
	Log("[BaleConvoyer] Object %d entered trigger", otherId)		
	if otherId ~= 0 then
        local rigidBodyType = getRigidBodyType(otherId)
        if (self.isServer and rigidBodyType == RigidBodyType.DYNAMIC)
        or (not self.isServer and rigidBodyType == RigidBodyType.KINEMATIC) then -- on client side bales are kinematic
            local object = g_currentMission:getNodeObject(otherId)
			if object ~= nil then
                if object:isa(Bale) then
					Log("[BaleConvoyer] Object is Bale")
					if object.mountObject == nil then
						Log("[BaleConvoyer] Can Interact")
						local w,h,l = getBaleDimensions(object)
						local spec = self.spec_baleConvoyer
						Log("[BaleConvoyer] width= %.2f, height = %.2f, length= %.2f.",w,h,l)
						if (l <= spec.maxSize) then
							if onEnter then
								local list = spec.balesInTrigger
								table.insert(list, object)
							elseif onLeave then
								for i, v in ipairs(spec.balesInTrigger) do
									if v == object then
										table.remove(spec.balesInTrigger, i)
										break
									end
								end
							end
						end
					end
				end
			end
		end
	end
end

function BaleConvoyer:cleanupMovedBales()

    local spec = self.spec_baleConvoyer
	local baleObj = spec.processedBales
	if baleObj == nil then
		return
	end
	local nodeId = baleObj.nodeId
	local bx,by,bz = getWorldTranslation(nodeId)
	local px,py,pz = getWorldTranslation(spec.balePlaceNode)
	local dist     = MathUtil.vector3Length(bx-px, by-py, bz-pz)
	local _,_,threshold = getBaleDimensions(baleObj)
	threshold = threshold * 1.2
	if dist > threshold then
		spec.processedBales = nil
		Log(
			"[BaleConvoyer] Unlinked bale %s after %.2f (th=%.2f)",
			nodeId, dist, threshold
		)
	end
end

function BaleConvoyer:onUpdate(dt)
    local spec = self.spec_baleConvoyer

    if not self:getIsTurnedOn() or spec.triggerNode == nil or spec.balePlaceNode == nil then
        return
    end
	self:cleanupMovedBales()
	for i = #spec.balesInTrigger, 1, -1 do
		local baleObj = spec.balesInTrigger[i]
        local w,h,l = getBaleDimensions(baleObj)
        if (l <= spec.maxSize)
           and spec.processedBales == nil then
			local nodeId = baleObj.nodeId
			baleObj:removeFromPhysics()
			local px,py,pz = getWorldTranslation(spec.balePlaceNode)
			local rx,ry,rz = getWorldRotation(spec.balePlaceNode)
            setTranslation(nodeId, px, py, pz, true)
            setRotation(nodeId, rx, ry, rz, true)
			spec.processedBales = baleObj
			table.remove(spec.balesInTrigger, i)
			baleObj:addToPhysics()
            Log(
                "[BaleConvoyer] Moved bale %s (%.2f×%.2f) to placeNode",
                baleObj.id, w, h
            )
        end
    end
end



