--this is deepseek's as a foundation:

--# Main
require(asset.documents.Craft.Touches)
require(asset.documents.Craft.Cameras)
require(asset.documents.immediateUI_codea)

function setup()
    setupGlobals()
    setupCraftScene()
    LifeUI.init()
    -- Create the cubemap sphere
    cubeMapSphere = CubeMapSphere(scene, PLANET_RADIUS, 128)
    -- after: cubeMapSphere = CubeMapSphere(scene, PLANET_RADIUS, 256)
    
    -- === Life model & renderer ===
    local CELLS = 64            -- choose N so textureSize % N == 0 (e.g., 256, 128, 64, 32…)
    life  = ConwaysCubeLifeModel(CELLS)
    life:setStepInterval(0.08)
    life:setPlaying(false)
    
    LifeUI.attachModel(life)
    
    lifeRenderer = CubeLifeRenderer(life, cubeMapSphere, color(255), color(0))
    
    setConway()
    life:seedRandomCells(0.51)
    lifeRenderer:applyDirty()
    life:setPlaying(true)
    
    setupUI(life, cubeMapSphere, lifeRenderer)
end

function setupGlobals()
    viewer.mode = FULLSCREEN
    PLANET_RADIUS = 200
    parameter.boolean("ShowCubeMap", true)
    parameter.boolean("DrawingMode", false)
end

function setupCraftScene()
    scene = craft.scene()
    scene.ambientColor = color(180, 180, 200)
    
    -- Camera with orbit viewer
    scene.camera.position = vec3(0, 0, 800)
    local cam = scene.camera:get(craft.camera)
    cam.nearPlane, cam.farPlane = 10, 4000
    vwr = scene.camera:add(OrbitViewer, vec3(0, -130, 0), 1400, 300, 2400)
    
    -- Simple sky
    local sky = scene.sky.material
    sky.sky = color(37, 56, 100)
    sky.horizon = color(0)
    sky.ground = color(52, 37, 71)
end

function setupUI(life, cms, renderer)
    parameter.boolean("Life_Play", true, function(v) life:setPlaying(v) end)
    parameter.action("Life_Step", function()
        life:step()
        lifeRenderer:applyDirty()
    end)
    parameter.action("Life_Clear", function()
        life:clearAll()
        lifeRenderer:fullRedraw()
        lifeRenderer:applyDirty()
    end)
    parameter.action("Life_Randomize", function()
        life:clearAll()
        lifeRenderer:fullRedraw()
        life:seedRandomCells()
        lifeRenderer:applyDirty()
    end)
    
    parameter.action("Conway (B3/S23)",   function() 
            setConway()
            lifeRenderer:fullRedraw()
            life:seedRandomCells()
        end)
parameter.action("HighLife (B36/S23)", function() 
    setHighLife()
        lifeRenderer:fullRedraw()
        life:seedRandomCells()
end)
parameter.action("Seeds (B2/S)",       function() 
    setSeeds()
        lifeRenderer:fullRedraw()
        life:seedRandomCells()
end)
parameter.action("Day&Night (B3678/S34678)", function() 
    setDayNight()
        lifeRenderer:fullRedraw()
        life:seedRandomCells()
end)
parameter.action("Morley (B368/S245)", function() 
    setMorley()
        lifeRenderer:fullRedraw()
        life:seedRandomCells()
end)
parameter.action("Random Rule", function() 
    setRandomRule()
        lifeRenderer:fullRedraw()
        life:seedRandomCells()
end)
    
    -- optional: a watch so you always see the canonical rule in effect
    parameter.watch(function() return "Active Rule: "..currentRule end)
    
    parameter.boolean("EditCells", true)  -- tap sphere to toggle cells

        -- default palette
        PALETTE = {
            age1 = color(255, 245, 245),  -- new
            age2 = color(234, 164,   72),  -- 1 gen
            age3 = color(255, 125,   0),  -- 3 gens
            age4 = color(255, 86, 0),  -- 4 gens
            age5 = color( 243, 41, 33),  -- 5+ gens
            dead = color(0, 0, 0)         -- background
        }
        
        local function applyPalette()
            -- push background/dead color
            if renderer.deadC then renderer.deadC = PALETTE.dead end
            -- runtime palette function
            renderer:setPalette(function(age)
                if age <= 0 then return PALETTE.dead
                elseif age == 1 then return PALETTE.age1
                elseif age == 2 then return PALETTE.age2
                elseif age == 3 then return PALETTE.age3
                elseif age == 4 then return PALETTE.age4
                else return PALETTE.age5
                end
            end)
            -- do a targeted refresh of what’s visible next time dirty arrives
            -- (or uncomment for a guaranteed full recolor)
            -- renderer:fullRedraw(); cms.needsTextureUpdate = true
        end
        
        -- color pickers
        parameter.color("Life_New_Age1", PALETTE.age1, function(c) PALETTE.age1 = c; applyPalette() end)
        parameter.color("Life_Age2",     PALETTE.age2, function(c) PALETTE.age2 = c; applyPalette() end)
        parameter.color("Life_Age3",     PALETTE.age3, function(c) PALETTE.age3 = c; applyPalette() end)
        parameter.color("Life_Age4",     PALETTE.age4, function(c) PALETTE.age4 = c; applyPalette() end)
        parameter.color("Life_Age5Plus", PALETTE.age5, function(c) PALETTE.age5 = c; applyPalette() end)
        
        -- spawn a glider at a random face / safe location (no wrapping during stamp)
        local function spawnRandomGlider()
            local N = life.N
            local face = math.random(1, 6)
            local rot  = math.random(0, 3)
            -- keep 3x3 inside the face bounds; pick a center that leaves margin
            local margin = 2           -- glider is 3x3, so 2 is safe for center
            local x = math.random(1 + margin, N - margin)
            local y = math.random(1 + margin, N - margin)
            life:stampGlider(face, x, y, rot)
            renderer:applyDirty()
            cms.needsTextureUpdate = true
        end
        
        parameter.action("Spawn Random Glider", spawnRandomGlider)
        
        -- expose play/pause & speed (handy while testing colors)
        parameter.boolean("Life_Play", true, function(p)
            life:setPlaying(p)
        end)
        parameter.number("Life_StepInterval", 0.02, 0.5, 0.08, function(sec)
            life:setStepInterval(sec)
        end)
        
        -- prime palette once
        applyPalette()
    
    
    
    parameter.action("Toggle Drawing", function()
        DrawingMode = not DrawingMode
        print("Drawing mode: " .. (DrawingMode and "ON" or "OFF"))
                end)
                
                
    parameter.number("Brush Size", 5, 50, 20, function(value)
        if cubeMapSphere then cubeMapSphere:setBrushSize(value) end
    end)
    
    parameter.action("Clear All", function()
        if cubeMapSphere then cubeMapSphere:clearAllFaces() end
    end)
    
    -- Color picker buttons
    local colors = {
        {"Red", color(255, 0, 0)},
        {"Green", color(0, 255, 0)},
        {"Blue", color(0, 0, 255)},
        {"White", color(255, 255, 255)},
        {"Black", color(0, 0, 0)}
    }
    
    for _, colorInfo in ipairs(colors) do
        parameter.action(colorInfo[1], function()
            if cubeMapSphere then cubeMapSphere:setDrawingColor(colorInfo[2]) end
        end)
    end
end

function draw()
    scene:update(DeltaTime)
    
    -- run Life on a timer
    if life:update(DeltaTime) then
        lifeRenderer:applyDirty()
    end
    if cubeMapSphere.needsTextureUpdate then
        cubeMapSphere:updateCubeTexture()
        cubeMapSphere.needsTextureUpdate = false
    end
    
    scene:draw()
    
    -- existing preview logic stays the same…
    if ShowCubeMap and cubeMapSphere then
        cubeMapSphere:drawPreview()
    end
    
    if cubeMapSphere and cubeMapSphere.needsTextureUpdate then
        cubeMapSphere:updateCubeTexture()
        cubeMapSphere.needsTextureUpdate = false
    end
    
    LifeUI.draw()
end


function touched(t)
    immediateUI(t)
    if not DrawingMode then
        if vwr then vwr:touched(t) end
        return
    end
    if t.state == BEGAN or t.state == MOVING then
        if EditCells then
            -- cast ray and toggle cell under finger
            local cam = scene.camera:get(craft.camera)
            local ro, rd = cam:screenToRay(vec2(t.x, t.y))
            local hit = cubeMapSphere:intersectRaySphere(ro, rd, cubeMapSphere.entity.position, PLANET_RADIUS)
            if hit then
                if lifeRenderer:toggleAtWorldPoint(hit) then return end
            end
        end
    end
end