-- Author: StrauntMaunt, Ola
-- Name: Generate Fence on Spline
-- Description:
-- Icon:iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAR5JREFUOI3NkrFKA0EQhr85civxPXwCS7GIRcD2sEprIcehWAlCStMLIXAKadNYpLOIhcc14hv4CoEEwSaR0x2LeGHvcsHYiH+zM//+O/Pv7MK/wlP/TH7SPN9GhdzLgzQONcuyT3czjUObxqF1uXdr1eVqeZBlHwAFB5twAjC6Pi502RTN875XA5hMXvNiWrGuuKjEsNOyw07L/pYTgMfeiV3TuaBzoACN6GZxhfF4KhVCt2AZq9ygHeigHdgSZwftQCt0S275jHe7c+Z1KwBHD01rhBcSSBpvcLXQXKQHygimO7Oilf37QzUCRlR9UTGeqhHEF9UtUfFFIyP0fE9PjWjXiOILs8u9ZDv/ieWhudNSoPeddh19fd2Zv8UX1Lt5pCkorycAAAAASUVORK5CYIIAFYxSCQAHAIB4ATog+X8AAGCKVovWAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQh1eL1gEAAN7AzQEAAAAAAF52hNYBAAASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYIpWi9YBAAAAAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjEsJAAgAkOD8HP33fwAAkNivhdYBAAB8AAAAAAAAAI8PAAABAAAAkEZvhtYBAACw2K+F1gEAAMDYr4XWAQAAwNivhdYBAAAJBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGDmKYbWAQAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/6gV+v+bgILT8Bf49AAAAAEoFDrT+/38/5uAgNAAAAAD6Bf69SgUONOoFfr8AAAAAAACwQQAAAAAAAIBBAACAPwAAAAAAAAAAAAAAAAAAAADQ3q+F1gEAAAAAAAAAAAAAAABIQwAAAAD/AP8AAABIQw==
-- Hide: no
-- AlwaysLoaded: no
-- Version 25.06.01

-- Max amount of panels/poles places
local amount = 200
-- Update the yOffset for the vertex offset shader with the angle
-- 0 or nil to disable
local offsetMultiplier = 2

-- Helper to lowercase safely
local function lower(str)
    return string.lower(str or "")
end

-- Helper to get all children of a node
local function getAllChildren(parent)
    local children = {}
    local numChildren = getNumOfChildren(parent)
    for i = 0, numChildren - 1 do
        local child = getChildAt(parent, i)
        table.insert(children, child)
    end
    return children
end

-- Random helper
local function randomFromTable(t)
    return t[math.random(1, #t)]
end

-- Make sure at least 3 objects selected
if getNumSelected() < 3 then
    print("Select at least 1 spline, 1 panel group or object, and 1 pole group or object")
    return
end

-- Sort selections into spline, panels, and poles
local spline = nil
local panels = {}
local poles = {}

for i = 0, getNumSelected() - 1 do
    local node = getSelection(i)
    local nodeName = lower(getName(node))
    local numChildren = getNumOfChildren(node)

    if spline == nil then
        spline = node
        print("Detected spline: " .. getName(node))
    else
        if nodeName:find("panel") then
            if numChildren > 0 then
                local panelChildren = getAllChildren(node)
                for _, child in ipairs(panelChildren) do
                    table.insert(panels, child)
                    print("Detected panel child: " .. getName(child))
                end
            else
                table.insert(panels, node)
                print("Detected panel: " .. getName(node))
            end
        elseif nodeName:find("pole") then
            if numChildren > 0 then
                local poleChildren = getAllChildren(node)
                for _, child in ipairs(poleChildren) do
                    table.insert(poles, child)
                    print("Detected pole child: " .. getName(child))
                end
            else
                table.insert(poles, node)
                print("Detected pole: " .. getName(node))
            end
        end
    end
end

if spline == nil or #panels == 0 or #poles == 0 then
    print("Error: Could not detect spline, panel(s), or pole(s). Make sure names contain 'panel' and 'pole'.")
    return
end

-- 🆕 Fence Length Detection from spline name
local fenceLength = 2 -- default fallback
local splineName = lower(getName(spline))
local foundLength = string.match(splineName, "_([%d%.]+)m")
if foundLength then
    fenceLength = tonumber(foundLength)
    print("Fence length override detected from name: " .. fenceLength)
else
    print("No fence length override found, using default: " .. fenceLength)
end

math.randomseed(getTime()) -- safe random seed for Giants Editor

function connectPoints(template, startX, startY, startZ, endX, endY, endZ)
    local splinePoints = {endX, endY, endZ, startX, endY, startZ}
    local newSpline = createSplineFromEditPoints(getRootNode(), splinePoints, true, false)
    local rX, rY, rZ = getSplineOrientation(newSpline, 0, 0, -1, 0)
    delete(newSpline)

    local newFence = clone(template, false)
    link(getRootNode(), newFence)
    setWorldRotation(newFence, rX, rY, rZ)
    setWorldTranslation(newFence, startX, startY, startZ)
    return newFence
end

local spawnedObject = {}

local mSplineLength = getSplineLength(spline)
print("Spline length: " .. mSplineLength)
local fenceLengthTime = 1 / (mSplineLength / fenceLength)
print("Fence length time: " .. fenceLengthTime)
local splinePos = 0.0

local lastEndX, lastEndY, lastEndZ = nil, nil, nil -- will store the last segment's end position

-- Spawn all Objects
for i = 0, amount do
    if splinePos <= 1.0 and ((splinePos + fenceLengthTime) <= 1.0) then
        local x, y, z = getSplinePosition(spline, splinePos)
        local endX, endY, endZ = getSplinePosition(spline, (splinePos + fenceLengthTime))

        -- Randomly pick panel and pole
        local panel = randomFromTable(panels)
        local pole = randomFromTable(poles)

        local newPanel = connectPoints(panel, x, y, z, endX, endY, endZ)
        if (offsetMultiplier ~= nil and offsetMultiplier ~= 0) then
            local heightDifference = (endY - y) * offsetMultiplier
            local shaderParameterName = "yOffset"
            local xOffset, yOffset, zOffset, wOffset = 0, 0, 0, 0
            xOffset = math.atan(heightDifference / fenceLength)
            setShaderParameterRecursive(newPanel, shaderParameterName, xOffset, yOffset, zOffset, wOffset, false)
        end
        table.insert(spawnedObject, newPanel)

        local newPole = connectPoints(pole, x, y, z, endX, endY, endZ)
        table.insert(spawnedObject, newPole)

        -- store the last end point
        lastEndX, lastEndY, lastEndZ = endX, endY, endZ

        splinePos = splinePos + fenceLengthTime
    else
        break
    end
end

-- Add a final pole at the END of the last panel segment
if lastEndX ~= nil and lastEndY ~= nil and lastEndZ ~= nil then
    local finalPoleTemplate = randomFromTable(poles)
    local finalPole = clone(finalPoleTemplate, false)
    link(getRootNode(), finalPole)
    setWorldTranslation(finalPole, lastEndX, lastEndY, lastEndZ)
    table.insert(spawnedObject, finalPole)
end

-- Move Objects to parent Group
local parentGroup = createTransformGroup("fence")
link(getRootNode(), parentGroup)
setWorldTranslation(parentGroup, getSplinePosition(spline, 0))

for i = 1, #spawnedObject do
    local x, y, z = getWorldTranslation(spawnedObject[i])
    link(parentGroup, spawnedObject[i])
    setWorldTranslation(spawnedObject[i], x, y, z)
end

print("Fence generated successfully with " .. tostring(#panels) .. " panel types and " .. tostring(#poles) .. " pole types.")