--
-- Author: E.T.A La Marchoise
-- Date: 19-08-22
-- Version: 1.0.0.0
-- Copyright (C) E.T.A La Marchoise, All Rights Reserved
-- GrapevineBaseDetector.lua
--

GrapevineBaseDetector = {};
local SPEC_NAME = "spec_" .. g_currentModName .. ".grapevineBaseDetector";

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

    schema:setXMLSpecializationType("GrapevineBaseDetector")
    schema:register(XMLValueType.NODE_INDEX, "vehicle.vineDetector.grapevineBaseDetector#node", "Raycast node")
    schema:register(XMLValueType.FLOAT, "vehicle.vineDetector.grapevineBaseDetector#maxDistance", "Max raycast distance", 1)
    schema:setXMLSpecializationType()
end

function GrapevineBaseDetector.prerequisitesPresent(specializations)
    return true;
end

function GrapevineBaseDetector.registerFunctions(vehicleType)
    SpecializationUtil.registerFunction(vehicleType, "getSpec", GrapevineBaseDetector.getSpec)
    SpecializationUtil.registerFunction(vehicleType, "raycastCallbackGrapevineBaseDetection", GrapevineBaseDetector.raycastCallbackGrapevineBaseDetection)
    SpecializationUtil.registerFunction(vehicleType, "finishedGrapevineBaseDetection", GrapevineBaseDetector.finishedGrapevineBaseDetection)
    SpecializationUtil.registerFunction(vehicleType, "clearCurrentGrapevineBasePlaceable", GrapevineBaseDetector.clearCurrentGrapevineBasePlaceable)
    SpecializationUtil.registerFunction(vehicleType, "cancelGrapevineBaseDetection", GrapevineBaseDetector.cancelGrapevineBaseDetection)
    SpecializationUtil.registerFunction(vehicleType, "getFirstGrapevineBaseHitPosition", GrapevineBaseDetector.getFirstGrapevineBaseHitPosition)
    SpecializationUtil.registerFunction(vehicleType, "getCurrentGrapevineBaseHitPosition", GrapevineBaseDetector.getCurrentGrapevineBaseHitPosition)
    SpecializationUtil.registerFunction(vehicleType, "getCurrentGrapevineBaseHitDistance", GrapevineBaseDetector.getCurrentGrapevineBaseHitDistance)
    SpecializationUtil.registerFunction(vehicleType, "handleGrapevineBasePlaceable", GrapevineBaseDetector.handleGrapevineBasePlaceable)
    SpecializationUtil.registerFunction(vehicleType, "getCanStartGrapevineBaseDetection", GrapevineBaseDetector.getCanStartGrapevineBaseDetection)
    SpecializationUtil.registerFunction(vehicleType, "getIsValidGrapevineBasePlaceable", GrapevineBaseDetector.getIsValidGrapevineBasePlaceable)
end

function GrapevineBaseDetector.registerEventListeners(vehicleType)
    SpecializationUtil.registerEventListener(vehicleType, "onLoad", GrapevineBaseDetector)
    SpecializationUtil.registerEventListener(vehicleType, "onUpdate", GrapevineBaseDetector)
end

function GrapevineBaseDetector:getSpec()
    return self[SPEC_NAME];
end

function GrapevineBaseDetector:onLoad(savegame)
    local spec = self:getSpec()
    spec.raycast = {
        node = self.xmlFile:getValue("vehicle.vineDetector.grapevineBaseDetector#node", nil, self.components, self.i3dMappings)
    }

    if spec.raycast.node == nil then
        Logging.xmlWarning(self.xmlFile, "Missing grapevine base detector node")
    end

    spec.raycast.maxDistance = self.xmlFile:getValue("vehicle.vineDetector.grapevineBaseDetector#maxDistance", 1)
    spec.raycast.vineNode = nil
    spec.raycast.isRaycasting = false
    spec.raycast.firstHitPosition = {
        0,
        0,
        0
    }
    spec.raycast.currentHitPosition = {
        0,
        0,
        0
    }
    spec.raycast.currentHitDistance = 0
    spec.raycast.currentNode = nil
    spec.isVineDetectionActive = false
end

function GrapevineBaseDetector:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
    local spec = self:getSpec()

    if self.isServer and spec.raycast.node ~= nil then
        if self:getCanStartGrapevineBaseDetection() then
            spec.isVineDetectionActive = true

            if not spec.raycast.isRaycasting then
                spec.raycast.isRaycasting = true
                local x, y, z = getWorldTranslation(spec.raycast.node)
                local dx, dy, dz = localDirectionToWorld(spec.raycast.node, 0, -1, 0)

                raycastAll(x, y, z, dx, dy, dz, "raycastCallbackGrapevineBaseDetection", spec.raycast.maxDistance, self, nil, false, true)
            end
        elseif spec.isVineDetectionActive then
            self:clearCurrentGrapevineBasePlaceable()
            self:finishedGrapevineBaseDetection()

            spec.isVineDetectionActive = false
        end
    end
end

function GrapevineBaseDetector:raycastCallbackGrapevineBaseDetection(hitActorId, x, y, z, distance, nx, ny, nz, subShapeIndex, hitShapeId, isLast)
    if hitActorId ~= 0 then
        if VehicleDebug.state == VehicleDebug.DEBUG then
            DebugUtil.drawDebugGizmoAtWorldPos(x, y, z, 0, 0, 1, 0, 1, 0, string.format("Grapevine base - hitActorId %s (%s); hitShape %s (%s)", getName(hitActorId), hitActorId, getName(hitShapeId), hitShapeId))
        end

        local spec = self:getSpec()

        if not spec.raycast.isRaycasting then
            self:cancelGrapevineBaseDetection()

            return false
        end

        local placeable = g_currentMission.vineSystem:getPlaceable(hitActorId)

        if not self:getIsValidGrapevineBasePlaceable(placeable) or g_currentMission.nodeToObject[hitActorId] == self then
            if isLast then
                self:clearCurrentGrapevineBasePlaceable()
                self:finishedGrapevineBaseDetection()

                return false
            end

            return true
        end

        if self:handleGrapevineBasePlaceable(hitActorId, placeable, x, y, z, distance) then
            self:finishedGrapevineBaseDetection()

            return false
        end

        if isLast then
            self:finishedGrapevineBaseDetection()

            return false
        end

        return true
    else
        self:clearCurrentGrapevineBasePlaceable()
        self:finishedGrapevineBaseDetection()
    end
end

function GrapevineBaseDetector:finishedGrapevineBaseDetection()
    self:getSpec().raycast.isRaycasting = false
end

function GrapevineBaseDetector:getFirstGrapevineBaseHitPosition()
    local raycast = self:getSpec().raycast

    return raycast.firstHitPosition[1], raycast.firstHitPosition[2], raycast.firstHitPosition[3]
end

function GrapevineBaseDetector:getCurrentGrapevineBaseHitPosition()
    local raycast = self:getSpec().raycast

    return raycast.currentHitPosition[1], raycast.currentHitPosition[2], raycast.currentHitPosition[3]
end

function GrapevineBaseDetector:getCurrentGrapevineBaseHitDistance()
    return self:getSpec().raycast.currentHitDistance
end

function GrapevineBaseDetector:clearCurrentGrapevineBasePlaceable()
    local spec = self:getSpec()
    local raycast = spec.raycast
    raycast.currentNode = nil
    raycast.placeable = nil
end

function GrapevineBaseDetector:cancelGrapevineBaseDetection()
    self:clearCurrentGrapevineBasePlaceable()
    self:finishedGrapevineBaseDetection()
end

function GrapevineBaseDetector:getCanStartGrapevineBaseDetection()
    return true
end

function GrapevineBaseDetector:getIsValidGrapevineBasePlaceable(vinePlaceable)
    return vinePlaceable ~= nil
end

function GrapevineBaseDetector:handleGrapevineBasePlaceable(node, placeable, x, y, z, distance)
    local spec = self:getSpec()
    local raycast = spec.raycast

    if raycast.currentNode ~= node then
        raycast.firstHitPosition[3] = z
        raycast.firstHitPosition[2] = y
        raycast.firstHitPosition[1] = x
    end

    raycast.currentNode = node
    raycast.currentHitPosition[3] = z
    raycast.currentHitPosition[2] = y
    raycast.currentHitPosition[1] = x
    raycast.currentHitDistance = distance
    raycast.placeable = placeable

    return true
end