
local monitor = peripheral.find("monitor")

if not monitor then
    print("Connect a monitor first!")
    return
end

monitor.setTextScale(0.5)
monitor.setBackgroundColor(colors.black)
monitor.setCursorBlink(false)
monitor.clear()

local width, height = monitor.getSize()
local blackCode = colors.toBlit(colors.black)

local screen, foreground

local function clearFrame()
    screen, foreground = {}, {}

    for y = 1, height do
        screen[y], foreground[y] = {}, {}

        for x = 1, width do
            screen[y][x] = " "
            foreground[y][x] = blackCode
        end
    end
end

local function pixel(x, y, char, color)
    if x >= 1 and x <= width and y >= 1 and y <= height then
        screen[y][x] = char:sub(1, 1)
        foreground[y][x] = colors.toBlit(color or colors.green)
    end
end

local function drawText(x, y, str, color)
    for i = 1, #str do
        pixel(x + i - 1, y, str:sub(i, i), color)
    end
end

local function centerText(y, str, color)
    drawText(math.floor((width - #str) / 2) + 1, y, str, color)
end

local function render()
    local bg = string.rep(blackCode, width)

    for y = 1, height do
        monitor.setCursorPos(1, y)
        monitor.blit(
            table.concat(screen[y]),
            table.concat(foreground[y]),
            bg
        )
    end
end

local function progressBar(x, y, w, percent, color)
    local filled = math.floor(w * percent / 100)

    for i = 0, w - 1 do
        if i < filled then
            pixel(x + i, y, "#", color)
        else
            pixel(x + i, y, "-", colors.gray)
        end
    end
end

-- 1. SYSTEM STARTUP
local function startup()
    local modules = {
        "MEMORY CHECK",
        "MONITOR LINK",
        "STORAGE SCAN",
        "NETWORK LINK",
        "CORE INTERFACE"
    }

    local barW = math.max(1, width - 10)
    local barX = math.floor((width - barW) / 2) + 1
    local barY = math.max(1, height - 3)

    for p = 0, 100, 5 do
        clearFrame()

        centerText(2, "NEXUS SYSTEM", colors.lime)
        centerText(3, "INITIAL BOOT SEQUENCE", colors.green)

        for i, name in ipairs(modules) do
            local y = 5 + i

            if y < barY - 2 then
                local status = "[WAIT]"
                local col = colors.gray

                if p >= i * 20 then
                    status = "[OK]"
                    col = colors.lime
                elseif p >= (i - 1) * 20 then
                    status = "[....]"
                    col = colors.white
                end

                drawText(2, y, name, colors.green)
                drawText(width - #status + 1, y, status, col)
            end
        end

        drawText(2, barY - 2, "LOADING SYSTEM", colors.green)
        progressBar(barX, barY, barW, p, colors.lime)
        centerText(math.min(height, barY + 1),
            tostring(p) .. "% COMPLETE", colors.white)

        render()
        sleep(0.06)
    end

    sleep(0.5)
end

-- 2. MATRIX RAIN
local function matrixRain(seconds)
    local symbols = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ#$%&*+-<>?"
    local drops = {}

    for x = 1, width do
        drops[x] = {
            head = math.random(-height, 0),
            length = math.random(3, math.max(3, math.floor(height / 2))),
            speed = math.random(1, 2)
        }
    end

    for frame = 1, math.floor(seconds / 0.12) do
        clearFrame()

        for x = 1, width do
            local d = drops[x]

            for tail = 0, d.length do
                local y = math.floor(d.head) - tail

                if y >= 1 and y <= height then
                    local i = math.random(1, #symbols)
                    local ch = symbols:sub(i, i)
                    local col = colors.green

                    if tail == 0 then
                        col = colors.white
                    elseif tail <= 2 then
                        col = colors.lime
                    end

                    pixel(x, y, ch, col)
                end
            end

            d.head = d.head + d.speed

            if d.head - d.length > height then
                d.head = math.random(-height, 0)
                d.length = math.random(
                    3, math.max(3, math.floor(height / 2))
                )
                d.speed = math.random(1, 2)
            end
        end

        drawText(2, 1, "MATRIX // DATA STREAM", colors.lime)
        render()
        sleep(0.12)
    end
end

-- 3. RADAR
local function radar(seconds)
    local cx = math.floor((width + 1) / 2)
    local cy = math.floor((height + 1) / 2)

    local maxR = math.max(1, math.floor(math.min(
        (width - 4) / 4,
        (height - 4) / 2
    )))

    local targets = {}

    for i = 1, 6 do
        local a = math.random() * math.pi * 2
        local r = math.random() * maxR

        targets[i] = {
            x = math.floor(cx + math.cos(a) * r * 2 + 0.5),
            y = math.floor(cy + math.sin(a) * r + 0.5)
        }
    end

    local angle = 0

    for frame = 1, math.floor(seconds / 0.12) do
        clearFrame()
        drawText(2, 1, "RADAR // ACTIVE", colors.lime)

        for y = 2, height - 1 do
            for x = 1, width do
                local dx = (x - cx) / 2
                local dy = y - cy
                local dist = math.sqrt(dx * dx + dy * dy)

                if dist <= maxR and dist > 0.1 then
                    local a = math.atan(dy, dx)
                    local diff = math.atan(
                        math.sin(a - angle),
                        math.cos(a - angle)
                    )

                    if diff <= 0.05 and diff >= -0.5 then
                        pixel(x, y, ".", colors.lime)
                    elseif math.abs(dist - math.floor(dist + 0.5)) < 0.25 then
                        pixel(x, y, ".", colors.green)
                    end
                end
            end
        end

        for _, target in ipairs(targets) do
            local dx = (target.x - cx) / 2
            local dy = target.y - cy
            local a = math.atan(dy, dx)

            local diff = math.atan(
                math.sin(a - angle),
                math.cos(a - angle)
            )

            if math.abs(diff) < 0.4 then
                pixel(target.x, target.y, "*", colors.white)
            else
                pixel(target.x, target.y, "+", colors.lime)
            end
        end

        pixel(cx, cy, "+", colors.white)
        drawText(2, height, "TRACKS: 06", colors.green)

        render()
        angle = angle + 0.13
        sleep(0.12)
    end
end

-- 4. ENERGY CORE PULSE
local function energyCore(seconds)
    local cx = math.floor((width + 1) / 2)
    local cy = math.floor((height + 1) / 2)

    local maxR = math.max(1, math.floor(math.min(
        (width - 4) / 4,
        (height - 8) / 2
    )))

    local barW = math.max(1, width - 10)
    local barX = math.floor((width - barW) / 2) + 1

    for frame = 1, math.floor(seconds / 0.12) do
        clearFrame()

        centerText(1, "ENERGY CORE // SIM", colors.lime)

        local pulse = 1 + (
            (math.sin(frame * 0.18) + 1) / 2
        ) * (maxR - 1)

        for y = 2, height - 3 do
            for x = 1, width do
                local dx = (x - cx) / 2
                local dy = y - cy
                local dist = math.sqrt(dx * dx + dy * dy)

                if math.abs(dist - pulse) < 0.25 then
                    pixel(x, y, "o", colors.lime)
                elseif dist < 1 then
                    pixel(x, y, ".", colors.green)
                end
            end
        end

        pixel(cx, cy, "X", colors.white)

        local energy = math.floor(
            (math.sin(frame * 0.15) + 1) * 50
        )

        centerText(height - 3,
            "OUTPUT " .. energy .. "%", colors.green)

        progressBar(
            barX, height - 2, barW, energy, colors.lime
        )

        centerText(height, "SIMULATION ONLY", colors.gray)

        render()
        sleep(0.12)
    end
end

-- 5. STARFIELD / HYPERSPACE
local function starfield(seconds)
    local cx = (width + 1) / 2
    local cy = (height + 1) / 2
    local count = math.max(10, math.min(
        45, math.floor(width * height / 7)
    ))

    local stars = {}

    local function resetStar(s)
        s.x = cx
        s.y = cy
        s.vx = (math.random() - 0.5) * 1.5
        s.vy = (math.random() - 0.5) * 0.9
        s.age = 0
    end

    for i = 1, count do
        local s = {}
        resetStar(s)

        s.age = math.random(0, 20)
        s.x = cx + s.vx * s.age * 0.8
        s.y = cy + s.vy * s.age * 0.8

        stars[i] = s
    end

    for frame = 1, math.floor(seconds / 0.12) do
        clearFrame()

        for _, s in ipairs(stars) do
            local speed = 0.1 + s.age * 0.035

            s.x = s.x + s.vx * speed
            s.y = s.y + s.vy * speed
            s.age = s.age + 1

            if s.age > 28 or s.x < 1 or s.x > width
                or s.y < 1 or s.y > height then
                resetStar(s)
            end

            local x = math.floor(s.x + 0.5)
            local y = math.floor(s.y + 0.5)

            if s.age > 14 then
                pixel(x, y, "*", colors.white)
            else
                pixel(x, y, ".", colors.lime)
            end
        end

        drawText(2, 1, "DEEP SPACE // FLIGHT", colors.lime)
        centerText(height, "NAV SYSTEM ACTIVE", colors.green)

        render()
        sleep(0.12)
    end
end

-- AUTOMATIC ANIMATION CYCLE
while true do
    startup()
    matrixRain(8)
    radar(8)
    energyCore(7)
    starfield(8)
end
