if not TowTruckMod then TowTruckMod = {} end
if not TowTruckMod.Hook then TowTruckMod.Hook = {} end

local towSpeedDiff = 6

TowTruckMod.isDebug = getDebug() or false

-- If using the AVCS mod.
local useAVCS = false
if getActivatedMods():contains("AnotherVehicleClaimSystemB41") then
	useAVCS = true
end

---------------------------------------------------------------------------
------ TowTruck functions -------------------------------------------------
--- Attach vehicle to TowTruck --------------------------------------------
---------------------------------------------------------------------------

-- ISBaseTimedAction

function TowTruckMod.Hook.attachTowTruckAction(playerObj, towingVehicle, towedVehicle, towingPoint, towedPoint)
    if playerObj == nil or towingVehicle == nil or towedVehicle == nil then return end
	
    -- check vehicle available
    if #(TowTruckMod.Utils.getHookTypeVariants(towingVehicle, towedVehicle, false)) == 0 then return end	
	
	-- Another Vehicle Claim System Hook
	if useAVCS then
		local checkResultA = AVCS.checkPermission(playerObj, towingVehicle)
		local checkResultB = AVCS.checkPermission(playerObj, towedVehicle)
		
		checkResultA = AVCS.getSimpleBooleanPermission(checkResultA)
		checkResultB = AVCS.getSimpleBooleanPermission(checkResultB)
		
		if not checkResultA or not checkResultB then
			playerObj:setHaloNote(getText("IGUI_AVCS_Vehicle_No_Permission"), 250, 250, 250, 300)
			return
		end
	end		
	
    --- Unequip item
    local storePrim = playerObj:getPrimaryHandItem()
    if storePrim ~= nil then
       ISTimedActionQueue.add(ISUnequipAction:new(playerObj, storePrim, 12))
    end

	-- Are we driving? If so skip the walking around and just hook the vehicle
	local playerDriving = towingVehicle:isDriver(playerObj)
	local hookTime = 250
	if TowTruckMod.isDebug then
		hookTime = 50
	end
	
	if not playerDriving then
		local hookPos = towingVehicle:getAttachmentWorldPos("hook", Vector3f.new())
		ISTimedActionQueue.add(ISPathFindAction:pathToLocationF(playerObj, hookPos:x(), hookPos:y(), hookPos:z()))		
		hookTime = 100
	end	
	
    -- Schedule attach vehicle
	playerObj:setVariable("bSettingHook", "true")
    ISTimedActionQueue.add(ISTowtruckAttachVehicle:new(playerObj, hookTime, TowTruckMod.Config.lowLevelAnimation, TowTruckMod.Hook.performAttachTowTruck, towingVehicle, towedVehicle, "hook", towedPoint))
end

function TowTruckMod.Hook.performAttachTowTruck(playerObj, towTruck, towedVehicle, attachmentA, attachmentB)
    if #(TowTruckMod.Utils.getHookTypeVariants(towTruck, towedVehicle)) == 0 then return end

	local modData = towTruck:getModData()

	-- Save default hook offset before modifying
	if modData.hookOffsetY == nil then
		TowTruckMod.Hook.setDefaultHookOffset(towTruck)
	end

	-- Raise the hook to a good starting height (2 increments up from default)
	local script = towTruck:getScript()
	local hook = script:getAttachmentById("hook")
	local defaultY = modData.hookOffsetY or hook:getOffset():y()
	local initialRaise = 0.30  -- Raise by 0.30 units (2 x 0.15 increment)
	hook:getOffset():setComponent(1, defaultY + initialRaise)

	modData.towHeight = 2  -- Start at height level 2
	towTruck:transmitModData()
		
	-- Check alarm
	local alarmSet = towedVehicle:isAlarmed()	
	if alarmSet == true then
		towedVehicle:triggerAlarm()
	end
		
	--ModData stuff
	local towedModData = towedVehicle:getModData()
	towedModData.isTowed = true
	towedModData.scriptName = towedVehicle:getScriptName()
	towedModData.mass = towedVehicle:getMass()
	towedModData.brakingForce = towedVehicle:getBrakingForce()
	towedVehicle:transmitModData()
		
	-- We have to fake being a 'Trailer' just to be able to tow from a pointConstraint.
	towedVehicle:setScriptName("notTowingA_Trailer")
			
	-- Hoist the vehicle up
    local args = { vehicleA = towTruck:getId(), vehicleB = towedVehicle:getId(), attachmentA = attachmentA, attachmentB = attachmentB }
	sendClientCommand(playerObj, 'vehicle', 'attachTrailer', args)
	
	-- Reset the towedVehicle back to its original script and set a lower mass for towing.
	ISTimedActionQueue.add(ISTowtruckSchedule:new(playerObj, 10, TowTruckMod.Hook.setVehiclePostAttach, towedVehicle))
		
	playerObj:clearVariable("bSettingHook")
end

function TowTruckMod.Hook.OnSpawnVehicle(vehicle)
	if not vehicle then
		return
	end

	if vehicle:getModData()["isTowed"] then
		vehicle:setScriptName("notTowingA_Trailer")
		local playerObj = getPlayer()
		ISTimedActionQueue.add(ISTowtruckSchedule:new(playerObj, 10, TowTruckMod.Hook.setVehiclePostAttach, vehicle))

		local towingVehicle = vehicle:getVehicleTowedBy()
		if towingVehicle then
			local modData = towingVehicle:getModData()
			-- Restore the hook height from saved data, or set default raised position
			local savedHeight = modData.towHeight or 2
			if savedHeight < 2 then savedHeight = 2 end

			-- Apply the height to the hook attachment
			local script = towingVehicle:getScript()
			local hook = script:getAttachmentById("hook")
			if hook then
				local defaultY = modData.hookOffsetY or hook:getOffset():y()
				local raiseAmount = savedHeight * 0.15
				hook:getOffset():setComponent(1, defaultY + raiseAmount)
			end

			modData.towHeight = savedHeight
			towingVehicle:transmitModData()
		end
	end
end

-- Change the things back to normal for the vehicle we are towing.
function TowTruckMod.Hook.setVehiclePostAttach(playerObj, towedVehicle)
	if not towedVehicle then return end
	
	local towedModData = towedVehicle:getModData()	
	towedVehicle:setScriptName(towedModData.scriptName)
	towedVehicle:setMass(200)
	towedVehicle:setBrakingForce(0)
	towedVehicle:constraintChanged()
end

---------------------------------------
-- Detach vehicle from TowTruck
---------------------------------------

function TowTruckMod.Hook.detachTowTruckAction(playerObj, vehicle)
    local towedVehicle = vehicle:getVehicleTowing()
	if towedVehicle == nil then
		return
	end

	local playerDriving = vehicle:isDriver(playerObj)
	-- Are we driving? If so just skip the walking around and just hook the vehicle
	if not playerDriving then
		--- Go to attachment point of towed vehicle
		local hookPos = vehicle:getAttachmentWorldPos("hook", Vector3f.new())
		ISTimedActionQueue.add(ISPathFindAction:pathToLocationF(playerObj, hookPos:x(), hookPos:y(), hookPos:z()))

		-- Unequip item
		local storePrim = playerObj:getPrimaryHandItem()
		if storePrim ~= nil then
			ISTimedActionQueue.add(ISUnequipAction:new(playerObj, storePrim, 12));
		end
	end
	-- Schedule the detach
    ISTimedActionQueue.add(ISTowtruckDetachVehicle:new(playerObj, 50, TowTruckMod.Config.highLevelAnimation, TowTruckMod.Hook.performDetachTowTruck, vehicle, towedVehicle))
end

function TowTruckMod.Hook.performDetachTowTruck(playerObj, towingVehicle, towedVehicle)
	TowTruckMod.Hook.resetHook(towingVehicle)
	local args = { vehicle = towedVehicle:getId() }
	sendClientCommand(playerObj, 'towing', 'detachTowTruck', args)

	local modData = towedVehicle:getModData()
	if modData.mass then
		towedVehicle:setMass(modData.mass)
		towedVehicle:setBrakingForce(modData.brakingForce)
	end
	modData.isTowed = false
	modData.mass = false
	towedVehicle:transmitModData()
end

-------------------------------------------------------------------------------------
----- Utility Functions -----
-------------------------------------------------------------------------------------

function TowTruckMod.Hook.adjustTowHeight(playerObj, towingVehicle, towedVehicle, offsetAmount)
	-- Safety checks
	if not towingVehicle or not towedVehicle then return end
	if not towingVehicle:getVehicleTowing() then return end

	local script = towingVehicle:getScript()
	local attachmentA = towingVehicle:getTowAttachmentSelf()
	local attachmentB = towingVehicle:getTowAttachmentOther()

	if not attachmentA or not attachmentB then return end

	local hook = script:getAttachmentById(attachmentA)
	if not hook then return end

	local hookPos = hook:getOffset()
	hook:getOffset():setComponent(1, hookPos:y() + offsetAmount)

	local modData = towingVehicle:getModData()
	local result = (offsetAmount > 0) and 1 or -1
	modData.towHeight = modData.towHeight + result
	towingVehicle:transmitModData()

	towedVehicle:setScriptName("notTowingA_Trailer")

	local args = { vehicleA = towingVehicle:getId(), vehicleB = towedVehicle:getId(), attachmentA = attachmentA, attachmentB = attachmentB }
	sendClientCommand(playerObj, 'vehicle', 'attachTrailer', args)

	ISTimedActionQueue.add(ISTowtruckSchedule:new(playerObj, 10, TowTruckMod.Hook.setVehiclePostAttach, towedVehicle))
end

function TowTruckMod.Hook.setDefaultHookOffset(towTruck)
	if not towTruck then return end

	local modData = towTruck:getModData()
	local script = towTruck:getScript()
	local hookAttachment = script:getAttachmentById("hook")
	local offSet = hookAttachment:getOffset()

	modData.hookOffsetY = offSet:get(1)
	towTruck:transmitModData()
end

function TowTruckMod.Hook.resetHook(towTruck)
	if not towTruck then return end

	local script = towTruck:getScript()
	local modData = towTruck:getModData()
	local hookAttachment = script:getAttachmentById("hook")
	local hookOffsetY = modData.hookOffsetY or 0
	modData.towHeight = -1
	towTruck:transmitModData()

	hookAttachment:getOffset():setComponent(1, hookOffsetY)
end

-------------------------------------------------------------------------------------
----- Flip Vehicle Over -----
-------------------------------------------------------------------------------------

function TowTruckMod.Hook.flipVehicleAction(playerObj, towingVehicle, towedVehicle, towingPoint, towedPoint)
    if playerObj == nil or towingVehicle == nil or towedVehicle == nil then return end

    -- Attach vehicle
    ISTimedActionQueue.add(ISTowtruckAttachVehicle:new(playerObj, 300, TowTruckMod.Config.lowLevelAnimation, TowTruckMod.Hook.performFlipVehicle, towingVehicle, towedVehicle, "hook", "flipNode"))
end

function TowTruckMod.Hook.performFlipVehicle(playerObj, towingVehicle, towedVehicle, attachmentA, attachmentB)
    if #(TowTruckMod.Utils.getCenterHook(towingVehicle, towedVehicle)) == 0 then return end

	local modData = towingVehicle:getModData()
	modData.towHeight = 0
	towingVehicle:transmitModData()

	-- Check alarm
	local alarmSet = towedVehicle:isAlarmed()
	if alarmSet == true then
		towedVehicle:triggerAlarm()
	end

	TowTruckMod.Hook.setDefaultHookOffset(towingVehicle)

	-- Use rope to pull vehicle over *fingers crossed*
    local args = { vehicleA = towingVehicle:getId(), vehicleB = towedVehicle:getId(), attachmentA = attachmentA, attachmentB = attachmentB }
	sendClientCommand(playerObj, 'vehicle', 'attachTrailer', args)
end

function TowTruckMod.Hook.fakeFunc()
	return
end

Events.OnSpawnVehicleEnd.Add(TowTruckMod.Hook.OnSpawnVehicle)

