CCCPaste
-- nanomachine_client.lua
-- Designed by fxllenfx for fx-Studios
-- Advanced Nanomachine Control System v2.0

local component = require("component")
local event = require("event")
local term = require("term")
local gpu = component.gpu
local serialization = require("serialization")

-- Config
local SERVER_ADDR = nil
local PORT = 2000
local modem = component.modem
local nano = component.nanomachines

-- UI State
local currentScreen = "main"
local selectedProfile = nil
local scrollOffset = 0
local statusData = {}
local profiles = {}
local discoveryActive = false
local discoveryProgress = 0

-- Colors
local COLOR_BG = 0x000000
local COLOR_FG = 0xFFFFFF
local COLOR_HEADER = 0x00FFFF
local COLOR_SUCCESS = 0x00FF00
local COLOR_WARNING = 0xFFFF00
local COLOR_ERROR = 0xFF0000
local COLOR_ACTIVE = 0x00FF00
local COLOR_INACTIVE = 0x888888
local COLOR_BUTTON = 0x0066CC
local COLOR_SELECTED = 0xFF6600
local COLOR_FX = 0xFF00FF

-- Boot Animation
local function bootAnimation()
  term.clear()
  gpu.setBackground(COLOR_BG)
  gpu.setResolution(80, 25)

  local startY = 8

  -- fx-Studios stylized text (using box drawing characters)
  gpu.setForeground(COLOR_FX)
  local logo1 = "███████╗██╗  ██╗      ███████╗████████╗██╗   ██╗██████╗ ██╗ ██████╗ ███████╗"
  local logo2 = "██╔════╝╚██╗██╔╝      ██╔════╝╚══██╔══╝██║   ██║██╔══██╗██║██╔═══██╗██╔════╝"
  local logo3 = "█████╗   ╚███╔╝ █████╗███████╗   ██║   ██║   ██║██║  ██║██║██║   ██║███████╗"
  local logo4 = "██╔══╝   ██╔██╗ ╚════╝╚════██║   ██║   ██║   ██║██║  ██║██║██║   ██║╚════██║"
  local logo5 = "██║     ██╔╝ ██╗      ███████║   ██║   ╚██████╔╝██████╔╝██║╚██████╔╝███████║"
  local logo6 = "╚═╝     ╚═╝  ╚═╝      ╚══════╝   ╚═╝    ╚═════╝ ╚═════╝ ╚═╝ ╚═════╝ ╚══════╝"

  -- Animated reveal
  gpu.set(1, startY, logo1)
  os.sleep(0.12)
  gpu.set(1, startY + 1, logo2)
  os.sleep(0.12)
  gpu.set(1, startY + 2, logo3)
  os.sleep(0.12)
  gpu.set(1, startY + 3, logo4)
  os.sleep(0.12)
  gpu.set(1, startY + 4, logo5)
  os.sleep(0.12)
  gpu.set(1, startY + 5, logo6)

  os.sleep(0.5)

  -- Subtitle
  gpu.setForeground(COLOR_HEADER)
  local subtitle = "NANOMACHINE CONTROL SYSTEM v2.0"
  gpu.set(math.floor((80 - #subtitle) / 2), startY + 7, subtitle)

  os.sleep(0.3)

  -- Credits
  gpu.setForeground(COLOR_FX)
  local credit = "» Designed by fxllenfx «"
  gpu.set(math.floor((80 - #credit) / 2), startY + 9, credit)

  os.sleep(0.6)

  -- Loading bar
  gpu.setForeground(COLOR_SUCCESS)
  local loadText = "[ Initialisiere System ]"
  gpu.set(math.floor((80 - #loadText) / 2), startY + 11, loadText)

  local barWidth = 50
  local barX = math.floor((80 - barWidth - 2) / 2)
  local barY = startY + 12

  gpu.setForeground(COLOR_INACTIVE)
  gpu.set(barX, barY, "[" .. string.rep("░", barWidth) .. "]")

  for i = 1, barWidth do
    local color = COLOR_FX
    if i < barWidth * 0.33 then
      color = 0xFF0000
    elseif i < barWidth * 0.66 then
      color = 0xFFFF00
    else
      color = COLOR_SUCCESS
    end

    gpu.setForeground(color)
    gpu.set(barX + i, barY, "█")
    os.sleep(0.03)
  end

  os.sleep(0.4)

  -- System checks
  local checks = {
    {text = "Modem erkannt", status = modem ~= nil},
    {text = "Wireless aktiv", status = modem and modem.isWireless()},
    {text = "Nanomachines verbunden", status = nano ~= nil},
    {text = "Inputs verfuegbar", status = nano and nano.getInputCount() > 0}
  }

  local checkY = startY + 14

  for i, check in ipairs(checks) do
    os.sleep(0.25)

    if check.status then
      gpu.setForeground(COLOR_SUCCESS)
      gpu.set(20, checkY + i - 1, "[✓]")
    else
      gpu.setForeground(COLOR_ERROR)
      gpu.set(20, checkY + i - 1, "[✗]")
    end

    gpu.setForeground(COLOR_FG)
    gpu.set(25, checkY + i - 1, check.text)
  end

  os.sleep(0.8)

  -- Final message with animation
  local readyMsg = ">>> SYSTEM BEREIT <<<"
  gpu.setForeground(COLOR_SUCCESS)

  for i = 1, 3 do
    gpu.set(math.floor((80 - #readyMsg) / 2), checkY + 5, readyMsg)
    os.sleep(0.3)
    gpu.set(math.floor((80 - #readyMsg) / 2), checkY + 5, string.rep(" ", #readyMsg))
    os.sleep(0.2)
  end

  gpu.set(math.floor((80 - #readyMsg) / 2), checkY + 5, readyMsg)
  os.sleep(1.2)
end

-- Check for nanomachines
local function checkNanomachines()
  if not nano then
    term.clear()
    gpu.setForeground(COLOR_ERROR)
    print("╔════════════════════════════════════════╗")
    print("║  FEHLER: Keine Nanomachines gefunden! ║")
    print("╚════════════════════════════════════════╝")
    print("")
    gpu.setForeground(COLOR_FG)
    print("Stelle sicher dass:")
    print("  [1] Du Nanomachines konsumiert hast")
    print("  [2] Sie geladen sind")
    print("  [3] Du eine Wireless Network Card hast")
    print("")
    gpu.setForeground(COLOR_WARNING)
    print("Druecke eine Taste zum Beenden...")
    event.pull("key_down")
    return false
  end
  return true
end

-- Server Setup
local function setupServer()
  term.clear()
  gpu.setForeground(COLOR_HEADER)
  print("╔═══════════════════════════════════════════╗")
  print("║    NANOMACHINE CLIENT SETUP               ║")
  print("╚═══════════════════════════════════════════╝")
  print("")
  gpu.setForeground(COLOR_FG)
  print("Server-Adresse eingeben:")
  gpu.setForeground(COLOR_INACTIVE)
  print("(oder Enter druecken fuer Standalone-Modus)")
  print("")
  gpu.setForeground(COLOR_FG)
  io.write("> ")
  SERVER_ADDR = io.read()

  if SERVER_ADDR == "" then
    SERVER_ADDR = nil
    print("")
    gpu.setForeground(COLOR_WARNING)
    print("⚠ Standalone-Modus (kein Server)")
    gpu.setForeground(COLOR_INACTIVE)
    print("Profile werden lokal gespeichert")
  else
    print("")
    gpu.setForeground(COLOR_SUCCESS)
    print("✓ Server gesetzt: " .. SERVER_ADDR:sub(1, 8) .. "...")
    gpu.setForeground(COLOR_INACTIVE)
    print("Profile werden auf Server synchronisiert")
  end

  gpu.setForeground(COLOR_FG)
  print("")
  print("Taste druecken zum Fortfahren...")
  event.pull("key_down")
end

-- Communication
local function sendCommand(command, data)
  if not SERVER_ADDR then return end

  local serialized = data and serialization.serialize(data) or nil
  modem.send(SERVER_ADDR, PORT, command, serialized)
end

local function requestData(command)
  if not SERVER_ADDR then return nil end

  sendCommand(command)
  local timeout = os.time() + 2

  while os.time() < timeout do
    local _, _, sender, _, _, cmd, serializedData = event.pull(0.1, "modem_message")
    if cmd and cmd == command .. "_RESPONSE" then
      if serializedData then
        local success, data = pcall(serialization.unserialize, serializedData)
        if success then
          return data
        end
      end
      return {}
    end
  end

  return nil
end

-- Nanomachine Control
local function updateNanoStatus()
  if not nano then return end

  statusData.power = nano.getPowerState()
  statusData.maxPower = nano.getMaxPower()
  statusData.totalInputs = nano.getInputCount()

  -- Count active inputs
  statusData.activeInputs = 0
  for i = 1, statusData.totalInputs do
    if nano.getInput(i) then
      statusData.activeInputs = statusData.activeInputs + 1
    end
  end

  -- Get active effects
  local effects = nano.getActiveEffects()
  statusData.activeEffects = effects and #effects or 0
  statusData.effects = effects or {}

  statusData.lastUpdate = os.time()
end

local function setInput(inputId, active)
  if not nano or inputId < 1 or inputId > statusData.totalInputs then
    return false
  end

  nano.setInput(inputId, active)
  return true
end

local function toggleInput(inputId)
  if not nano then return false end

  local currentState = nano.getInput(inputId)
  return setInput(inputId, not currentState)
end

local function disableAllInputs()
  if not nano then return end

  for i = 1, statusData.totalInputs do
    setInput(i, false)
  end

  statusData.currentProfile = nil
end

local function getCurrentInputState()
  if not nano then return {} end

  local inputs = {}
  for i = 1, statusData.totalInputs do
    if nano.getInput(i) then
      table.insert(inputs, i)
    end
  end
  return inputs
end

local function applyInputState(inputs)
  if not nano then return end

  -- Disable all first
  disableAllInputs()

  -- Enable specified inputs
  for _, inputId in ipairs(inputs) do
    setInput(inputId, true)
  end
end

-- Profile Management
local function loadProfiles()
  if SERVER_ADDR then
    local serverProfiles = requestData("GET_PROFILES")
    if serverProfiles then
      profiles = serverProfiles
      return
    end
  end

  -- Default profiles if no server or server unavailable
  if #profiles == 0 then
    profiles = {
      {name = "Safe", description = "Nur Regeneration", inputs = {}},
      {name = "Combat", description = "Kampf-Modus", inputs = {}},
      {name = "Mining", description = "Bergbau-Modus", inputs = {}}
    }
  end
end

local function saveCurrentAsProfile(name, description)
  local inputs = getCurrentInputState()

  local profile = {
    name = name,
    description = description or "Eigenes Profil",
    inputs = inputs,
    created = os.time()
  }

  table.insert(profiles, profile)

  -- Send to server if connected
  if SERVER_ADDR then
    sendCommand("SAVE_PROFILE", profile)
  end

  return true
end

local function activateProfile(profileName)
  local profile = nil
  for _, p in ipairs(profiles) do
    if p.name == profileName then
      profile = p
      break
    end
  end

  if not profile then
    return false
  end

  applyInputState(profile.inputs)
  statusData.currentProfile = profileName

  return true
end

-- Draw Functions
local function drawHeader(title)
  gpu.setBackground(COLOR_BG)
  gpu.setForeground(COLOR_FX)
  gpu.fill(1, 1, 80, 3, " ")

  local fx = "fx-Studios"
  gpu.set(2, 1, fx)

  gpu.setForeground(COLOR_HEADER)
  local titleText = "╔═══ " .. title .. " ═══╗"
  gpu.set(math.floor((80 - #titleText) / 2), 2, titleText)

  gpu.setForeground(COLOR_INACTIVE)
  gpu.set(70, 1, "v2.0")
end

local function drawButton(x, y, text, width, active)
  local bg = active and COLOR_SELECTED or COLOR_BUTTON
  gpu.setBackground(bg)
  gpu.setForeground(COLOR_FG)

  local padding = math.floor((width - #text) / 2)
  local buttonText = string.rep(" ", padding) .. text .. string.rep(" ", width - padding - #text)

  gpu.set(x, y, buttonText)
  gpu.setBackground(COLOR_BG)
end

local function drawStatusBar(y, label, value, maxValue, color)
  gpu.setForeground(COLOR_FG)
  gpu.set(2, y, label .. ":")

  local barWidth = 40
  local barX = 25
  local percentage = maxValue > 0 and (value / maxValue) or 0
  local filled = math.floor(barWidth * percentage)

  gpu.setForeground(color)
  gpu.set(barX, y, "[" .. string.rep("█", filled) .. string.rep("░", barWidth - filled) .. "]")

  gpu.setForeground(COLOR_FG)
  gpu.set(barX + barWidth + 3, y, string.format("%d/%d (%.1f%%)", value, maxValue, percentage * 100))
end

-- Main Dashboard Screen
local function drawMainScreen()
  term.clear()
  drawHeader("NANOMACHINE CONTROL CENTER")

  gpu.setForeground(COLOR_FG)

  -- Status Section
  gpu.setForeground(COLOR_HEADER)
  gpu.set(2, 5, "═══ SYSTEM STATUS ═══")
  gpu.setForeground(COLOR_FG)

  drawStatusBar(7, "Power", statusData.power or 0, statusData.maxPower or 100, COLOR_SUCCESS)

  gpu.set(2, 9, "Aktive Inputs: " .. (statusData.activeInputs or 0) .. "/" .. (statusData.totalInputs or 0))
  gpu.set(2, 10, "Aktive Effekte: " .. (statusData.activeEffects or 0))

  -- Display active effects
  if statusData.effects and #statusData.effects > 0 then
    gpu.setForeground(COLOR_SUCCESS)
    local effectText = table.concat(statusData.effects, ", ")
    if #effectText > 75 then
      effectText = effectText:sub(1, 72) .. "..."
    end
    gpu.set(2, 11, "Effekte: " .. effectText)
    gpu.setForeground(COLOR_FG)
  end

  -- Current Profile
  gpu.setForeground(COLOR_HEADER)
  gpu.set(2, 13, "═══ AKTUELLES PROFIL ═══")
  gpu.setForeground(COLOR_FG)

  if statusData.currentProfile then
    gpu.setForeground(COLOR_SUCCESS)
    gpu.set(2, 14, "● " .. statusData.currentProfile)
  else
    gpu.setForeground(COLOR_INACTIVE)
    gpu.set(2, 14, "○ Kein Profil aktiv")
  end

  -- Server Status
  gpu.setForeground(COLOR_HEADER)
  gpu.set(45, 13, "═══ SERVER ═══")
  gpu.setForeground(COLOR_FG)

  if SERVER_ADDR then
    gpu.setForeground(COLOR_SUCCESS)
    gpu.set(45, 14, "● Verbunden")
    gpu.setForeground(COLOR_INACTIVE)
    gpu.set(45, 15, SERVER_ADDR:sub(1, 16) .. "...")
  else
    gpu.setForeground(COLOR_WARNING)
    gpu.set(45, 14, "○ Standalone")
  end

  gpu.setForeground(COLOR_FG)

  -- Navigation Buttons
  gpu.setForeground(COLOR_HEADER)
  gpu.set(2, 18, "═══ SCHNELLZUGRIFF ═══")

  drawButton(2, 20, "1: Profile", 15, false)
  drawButton(19, 20, "2: Manuell", 15, false)
  drawButton(36, 20, "3: Discovery", 15, false)
  drawButton(53, 20, "4: Effekte", 15, false)

  drawButton(2, 22, "E: Notaus", 15, false)
  drawButton(19, 22, "S: Speichern", 17, false)
  drawButton(38, 22, "R: Refresh", 15, false)

  -- Footer
  gpu.setForeground(COLOR_INACTIVE)
  gpu.set(2, 24, "Q: Beenden")
  gpu.setForeground(COLOR_FX)
  gpu.set(60, 24, "by fxllenfx")
end

-- Profile Selection Screen
local function drawProfileScreen()
  term.clear()
  drawHeader("PROFILE")

  gpu.setForeground(COLOR_FG)

  if not profiles or #profiles == 0 then
    gpu.setForeground(COLOR_WARNING)
    gpu.set(2, 5, "Keine Profile vorhanden")
    gpu.setForeground(COLOR_FG)
    gpu.set(2, 7, "Druecke S um aktuellen Status als Profil zu speichern")
    gpu.set(2, 8, "Druecke B um zurueck zu gehen")
    return
  end

  gpu.setForeground(COLOR_HEADER)
  gpu.set(2, 5, "═══ VERFUEGBARE PROFILE ═══")
  gpu.setForeground(COLOR_FG)

  for i, profile in ipairs(profiles) do
    if i > scrollOffset and i <= scrollOffset + 12 then
      local y = 7 + (i - scrollOffset - 1)
      local isActive = statusData.currentProfile == profile.name

      if isActive then
        gpu.setForeground(COLOR_SUCCESS)
        gpu.set(2, y, "▶ ")
      else
        gpu.setForeground(COLOR_INACTIVE)
        gpu.set(2, y, "  ")
      end

      gpu.setForeground(COLOR_FG)
      gpu.set(5, y, string.format("[%d] %s", i, profile.name))

      if profile.description then
        gpu.setForeground(COLOR_INACTIVE)
        gpu.set(30, y, "- " .. profile.description)
      end

      gpu.setForeground(COLOR_INACTIVE)
      local inputCount = profile.inputs and #profile.inputs or 0
      gpu.set(65, y, "(" .. inputCount .. " inputs)")
    end
  end

  -- Instructions
  gpu.setForeground(COLOR_HEADER)
  gpu.set(2, 21, "═══ STEUERUNG ═══")
  gpu.setForeground(COLOR_FG)
  gpu.set(2, 22, "Zahl: Profil aktivieren")
  gpu.set(2, 23, "B: Zurueck")
end

-- Manual Control Screen
local function drawManualScreen()
  term.clear()
  drawHeader("MANUELLE STEUERUNG")

  gpu.setForeground(COLOR_FG)

  if not statusData.totalInputs or statusData.totalInputs == 0 then
    gpu.setForeground(COLOR_WARNING)
    gpu.set(2, 5, "Keine Input-Daten verfuegbar")
    return
  end

  gpu.setForeground(COLOR_HEADER)
  gpu.set(2, 5, "═══ INPUT KANAELE ═══")
  gpu.setForeground(COLOR_FG)

  local cols = 4
  local maxPerScreen = 40
  local row = 0

  for i = 1, math.min(statusData.totalInputs, maxPerScreen) do
    local isActive = nano.getInput(i)
    local col = (i - 1) % cols
    local x = 2 + (col * 19)
    local y = 7 + row

    if col == 0 and i > 1 then
      row = row + 1
    end

    local status = isActive and " ON" or "OFF"
    local color = isActive and COLOR_SUCCESS or COLOR_INACTIVE

    gpu.setForeground(color)
    gpu.set(x, y, string.format("[%2d] %s", i, status))
  end

  -- Instructions
  gpu.setForeground(COLOR_HEADER)
  gpu.set(2, 20, "═══ STEUERUNG ═══")
  gpu.setForeground(COLOR_FG)
  gpu.set(2, 21, "Zahl: Input umschalten")
  gpu.set(2, 22, "X: Alle deaktivieren")
  gpu.set(2, 23, "B: Zurueck")
end

-- Discovery Screen
local function drawDiscoveryScreen()
  term.clear()
  drawHeader("EFFEKT DISCOVERY")

  gpu.setForeground(COLOR_FG)

  gpu.setForeground(COLOR_HEADER)
  gpu.set(2, 5, "═══ DISCOVERY MODUS ═══")
  gpu.setForeground(COLOR_FG)

  if discoveryActive then
    gpu.setForeground(COLOR_SUCCESS)
    gpu.set(2, 7, "● Discovery laeuft...")
    gpu.setForeground(COLOR_FG)
    gpu.set(2, 9, "Fortschritt: " .. discoveryProgress .. "%")
  else
    gpu.setForeground(COLOR_INACTIVE)
    gpu.set(2, 7, "○ Discovery inaktiv")
  end

  gpu.setForeground(COLOR_HEADER)
  gpu.set(2, 12, "═══ STEUERUNG ═══")
  gpu.setForeground(COLOR_FG)
  gpu.set(2, 13, "S: Discovery starten")
  gpu.set(2, 14, "P: Discovery pausieren")
  gpu.set(2, 15, "R: Discovery zuruecksetzen")
  gpu.set(2, 16, "B: Zurueck")

  gpu.setForeground(COLOR_WARNING)
  gpu.set(2, 19, "⚠ Warnung: Discovery testet systematisch alle")
  gpu.set(2, 20, "  Input-Kombinationen. Dies kann mehrere Minuten dauern.")
end

-- Effects Screen
local function drawEffectsScreen()
  term.clear()
  drawHeader("AKTIVE EFFEKTE")

  gpu.setForeground(COLOR_HEADER)
  gpu.set(2, 5, "═══ EFFEKT DETAILS ═══")
  gpu.setForeground(COLOR_FG)

  if statusData.effects and #statusData.effects > 0 then
    for i, effect in ipairs(statusData.effects) do
      local y = 7 + i - 1
      if y < 20 then
        gpu.setForeground(COLOR_SUCCESS)
        gpu.set(2, y, "● " .. effect)
      end
    end
  else
    gpu.setForeground(COLOR_INACTIVE)
    gpu.set(2, 7, "Keine aktiven Effekte")
  end

  gpu.setForeground(COLOR_HEADER)
  gpu.set(2, 21, "═══ STEUERUNG ═══")
  gpu.setForeground(COLOR_FG)
  gpu.set(2, 22, "B: Zurueck")
end

-- Input Handling
local function handleMainInput(char)
  if char == string.byte("1") then
    currentScreen = "profiles"
  elseif char == string.byte("2") then
    currentScreen = "manual"
  elseif char == string.byte("3") then
    currentScreen = "discovery"
  elseif char == string.byte("4") then
    currentScreen = "effects"
  elseif char == string.byte("e") or char == string.byte("E") then
    disableAllInputs()
    updateNanoStatus()
  elseif char == string.byte("s") or char == string.byte("S") then
    term.clear()
    print("Profilname eingeben:")
    local name = io.read()
    if name and name ~= "" then
      print("Beschreibung (optional):")
      local desc = io.read()
      saveCurrentAsProfile(name, desc ~= "" and desc or nil)
      loadProfiles()
    end
  elseif char == string.byte("r") or char == string.byte("R") then
    updateNanoStatus()
    if SERVER_ADDR then
      loadProfiles()
    end
  elseif char == string.byte("q") or char == string.byte("Q") then
    return false
  end
  return true
end

local function handleProfileInput(char)
  if char == string.byte("b") or char == string.byte("B") then
    currentScreen = "main"
  elseif char >= string.byte("0") and char <= string.byte("9") then
    local num = char - string.byte("0")
    if profiles[num] then
      activateProfile(profiles[num].name)
      updateNanoStatus()
    end
  end
end

local function handleManualInput(char)
  if char == string.byte("b") or char == string.byte("B") then
    currentScreen = "main"
  elseif char == string.byte("x") or char == string.byte("X") then
    disableAllInputs()
    updateNanoStatus()
  elseif char >= string.byte("0") and char <= string.byte("9") then
    local num = char - string.byte("0")
    if num <= statusData.totalInputs then
      toggleInput(num)
      updateNanoStatus()
    end
  end
end

local function handleDiscoveryInput(char)
  if char == string.byte("b") or char == string.byte("B") then
    currentScreen = "main"
  elseif char == string.byte("s") or char == string.byte("S") then
    discoveryActive = true
    if SERVER_ADDR then
      sendCommand("START_DISCOVERY")
    end
  elseif char == string.byte("p") or char == string.byte("P") then
    discoveryActive = false
    if SERVER_ADDR then
      sendCommand("PAUSE_DISCOVERY")
    end
  elseif char == string.byte("r") or char == string.byte("R") then
    discoveryProgress = 0
    discoveryActive = true
    if SERVER_ADDR then
      sendCommand("RESET_DISCOVERY")
    end