PushHandToolExtension = {}

source(g_currentModDirectory .. "scripts/specializations/events/PushHandToolExtensionDriveModeEvent.lua")

PushHandToolExtension.MOD_NAME = g_currentModName
PushHandToolExtension.SPEC_NAME = g_currentModName .. ".pushHandToolExtension"
PushHandToolExtension.SPEC_TABLE_NAME = "spec_" .. PushHandToolExtension.SPEC_NAME

function PushHandToolExtension.prerequisitesPresent(specializations)
	return SpecializationUtil.hasSpecialization(PushHandTool, specializations)
end

function PushHandToolExtension.initSpecialization()
	local schema = Vehicle.xmlSchema

	schema:setXMLSpecializationType("PushHandToolExtension")

	local key = "vehicle.pushHandTool"

	schema:register(XMLValueType.STRING, key .. ".driveMode#animationName", "Name of toggle mode animation", 0)
	schema:register(XMLValueType.FLOAT, key .. ".driveMode#animationSpeed", "Animation speed scale", 1)
	schema:register(XMLValueType.FLOAT, key .. ".driveMode#maxSpeed", "Max. vehicle speed while drive mode is enabled")
	schema:register(XMLValueType.FLOAT, key .. ".driveMode#gearRatio", "Min. gear ratio while drive mode is enabled")
	VehicleCharacter.registerCharacterXMLPaths(schema, key .. ".driveMode.characterNode")
	schema:setXMLSpecializationType()

	local schemaSavegame = Vehicle.xmlSchemaSavegame

	schemaSavegame:register(XMLValueType.BOOL, "vehicles.vehicle(?)." .. PushHandToolExtension.SPEC_NAME .. "#driveModeIsActive", "DriveMode is active")
end

function PushHandToolExtension.registerFunctions(vehicleType)
	SpecializationUtil.registerFunction(vehicleType, "setPushHandToolDriveMode", PushHandToolExtension.setPushHandToolDriveMode)
end

function PushHandToolExtension.registerOverwrittenFunctions(vehicleType)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "setVehicleCharacter", PushHandToolExtension.setVehicleCharacter)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "deleteVehicleCharacter", PushHandToolExtension.deleteVehicleCharacter)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsFoldAllowed", PushHandToolExtension.getIsFoldAllowed)
end

function PushHandToolExtension.registerEventListeners(vehicleType)
	SpecializationUtil.registerEventListener(vehicleType, "onLoad", PushHandToolExtension)
	SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", PushHandToolExtension)
	SpecializationUtil.registerEventListener(vehicleType, "onReadStream", PushHandToolExtension)
	SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", PushHandToolExtension)
	SpecializationUtil.registerEventListener(vehicleType, "onUpdate", PushHandToolExtension)
	SpecializationUtil.registerEventListener(vehicleType, "onCameraChanged", PushHandToolExtension)
	SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", PushHandToolExtension)
end

function PushHandToolExtension:onLoad(savegame)
	local spec = self.spec_pushHandTool
	local baseKey = "vehicle.pushHandTool"
	spec.driveMode = {
		animationName = self.xmlFile:getValue(baseKey .. ".driveMode#animationName"),
		animationSpeed = self.xmlFile:getValue(baseKey .. ".driveMode#animationSpeed", 1),
		baseSpeed = self.spec_motorized.motor.maxForwardSpeed,
		baseGearRatio = self.spec_motorized.motor.minForwardGearRatio,
		maxSpeed = self.xmlFile:getValue(baseKey .. ".driveMode#maxSpeed")
	}

	if spec.driveMode.maxSpeed ~= nil then
		spec.driveMode.maxSpeed = spec.driveMode.maxSpeed / 3.6
	end

	spec.driveMode.gearRatio = self.xmlFile:getValue(baseKey .. ".driveMode#gearRatio")
	spec.driveMode.vehicleCharacter = VehicleCharacter.new(self)

	if spec.driveMode.vehicleCharacter ~= nil and not spec.driveMode.vehicleCharacter:load(self.xmlFile, baseKey .. ".driveMode.characterNode") then
		spec.driveMode.vehicleCharacter = nil
	end

	spec.driveMode.isActive = false
end

function PushHandToolExtension:onPostLoad(savegame)
	local spec = self.spec_pushHandTool

	if savegame ~= nil then
		local key = savegame.key .. "." .. PushHandToolExtension.SPEC_NAME

		if savegame.xmlFile:getValue(key .. "#driveModeIsActive", false) then
			self:setPushHandToolDriveMode(true, true)
			AnimatedVehicle.updateAnimationByName(self, spec.driveMode.animationName, 99999, true)
		end
	end
end

function PushHandToolExtension:saveToXMLFile(xmlFile, key, usedModNames)
	local spec = self.spec_pushHandTool

	if spec.driveMode.animationName ~= nil then
		xmlFile:setValue(key .. "#driveModeIsActive", spec.driveMode.isActive)
	end
end

function PushHandToolExtension:onReadStream(streamId, connection)
	local spec = self.spec_pushHandTool

	self:setPushHandToolDriveMode(streamReadBool(streamId), true)
	AnimatedVehicle.updateAnimationByName(self, spec.driveMode.animationName, 99999, true)
end

function PushHandToolExtension:onWriteStream(streamId, connection)
	local spec = self.spec_pushHandTool

	streamWriteBool(streamId, spec.driveMode.isActive)
end

function PushHandToolExtension:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
	if self:getIsControlled() and self.isClient then
		local spec = self.spec_pushHandTool

		if spec.driveMode.vehicleCharacter ~= nil and spec.driveMode.isActive and not self:getIsAnimationPlaying(spec.driveMode.animationName) then
			spec.driveMode.vehicleCharacter:update(dt)
		end
	end
end

function PushHandToolExtension:onCameraChanged(activeCamera, camIndex)
	if self:getIsEntered() then
		local spec = self.spec_pushHandTool

		if spec.driveMode.vehicleCharacter ~= nil then
			spec.driveMode.vehicleCharacter:setCharacterVisibility(not activeCamera.isInside)
		end
	end
end

function PushHandToolExtension:setVehicleCharacter(superFunc, playerStyle)
	superFunc(self, playerStyle)

	local spec = self.spec_pushHandTool

	if spec.driveMode.vehicleCharacter ~= nil and playerStyle ~= nil then
		spec.driveMode.vehicleCharacter:loadCharacter(playerStyle, self, PushHandToolExtension.vehicleCharacterLoaded, {})
	end
end

function PushHandToolExtension:vehicleCharacterLoaded(success, arguments)
	if success then
		local spec = self.spec_pushHandTool

		if spec.driveMode.vehicleCharacter ~= nil then
			local activeCamera = self:getActiveCamera()

			if activeCamera ~= nil then
				spec.driveMode.vehicleCharacter:setCharacterVisibility(not activeCamera.isInside)
			end

			spec.driveMode.vehicleCharacter:updateIKChains()
		end
	end
end

function PushHandToolExtension:deleteVehicleCharacter(superFunc)
	superFunc(self)

	local spec = self.spec_pushHandTool

	if spec.driveMode.vehicleCharacter ~= nil then
		spec.driveMode.vehicleCharacter:unloadCharacter()
	end
end

function PushHandToolExtension:getIsFoldAllowed(superFunc, direction, onAiTurnOn)
	local spec = self.spec_pushHandTool

	if spec.driveMode.isActive then
		return false
	end

	return superFunc(self, direction, onAiTurnOn)
end

function PushHandToolExtension:setPushHandToolDriveMode(driveModeState, noEventSend)
	local spec = self.spec_pushHandTool

	if driveModeState == nil then
		driveModeState = not spec.driveMode.isActive
	end

	if spec.driveMode.isActive ~= driveModeState then
		spec.driveMode.isActive = driveModeState
		self.spec_motorized.motor.maxForwardSpeed = driveModeState and spec.driveMode.maxSpeed or spec.driveMode.baseSpeed
		self.spec_motorized.motor.minForwardGearRatio = driveModeState and spec.driveMode.gearRatio or spec.driveMode.baseGearRatio
		self.spec_drivable.cruiseControl.maxSpeed = self.spec_motorized.motor.maxForwardSpeed * 3.6

		self:setCruiseControlMaxSpeed(math.min(self.spec_drivable.cruiseControl.speed, self.spec_drivable.cruiseControl.maxSpeed), nil)
		self:playAnimation(spec.driveMode.animationName, driveModeState and spec.driveMode.animationSpeed or -spec.driveMode.animationSpeed, self:getAnimationTime(spec.driveMode.animationName), true)
		self:setFoldState(self.spec_foldable.turnOnFoldDirection, false, true)
	end

	PushHandToolExtensionDriveModeEvent.sendEvent(self, driveModeState, noEventSend)
end

function PushHandToolExtension:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection)
	if self.isClient then
		local spec = self.spec_pushHandTool

		if spec.driveMode.vehicleCharacter ~= nil then
			self:clearActionEventsTable(spec.actionEvents)

			if isActiveForInputIgnoreSelection then
				local _, actionEventId = self:addPoweredActionEvent(spec.actionEvents, InputAction.IMPLEMENT_EXTRA4, self, PushHandToolExtension.actionEventToggleDriveMode, false, true, false, true, 1)

				g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_NORMAL)
				g_inputBinding:setActionEventText(actionEventId, g_i18n:getText("action_changeDriveMode"))
			end
		end
	end
end

function PushHandToolExtension:actionEventToggleDriveMode(actionName, inputValue, callbackState, isAnalog)
	self:setPushHandToolDriveMode()
end