-- FF_STORY_COMPUTER_GUARD
local function ff_story_computer_allowed()
  local label = os.getComputerLabel and os.getComputerLabel() or nil
  return fs.exists(".ff_story_computer")
    or fs.exists(".ff_titan_computer")
    or fs.exists(".ff_titan_program_id")
    or fs.exists(".ff_titan_allow_autoinstall")
    or fs.exists(".ff_titan_allow_autorun")
    or (label ~= nil and string.sub(string.lower(label), 1, 9) == "ff_titan:")
end

if not ff_story_computer_allowed() then
  return
end
-- TITAN RED LEVEL
-- FUSION REACTOR CONTROL TERMINAL
-- Save as: startup
--
-- Output:
-- back = reactor containment signal
--
-- Intended wiring:
-- Computer back output -> redstone lamp at -483 61 -169
--
-- KubeJS can detect that lamp and start reactor containment logic.

local OUTPUT_SIDE = "back"
local STATE_FILE = ".red_reactor_containment_signal"

local answers = {
  loop = "FLOOD",
  rods = "SCRAM",
  gas = "PURGE",
  field = "CLAMP"
}

local strikes = 0
local maxStrikes = 3

local function hasColor()
  return term.isColor and term.isColor()
end

local function c(col)
  if hasColor() then term.setTextColor(col) end
end

local function cls()
  term.setBackgroundColor(colors.black)
  term.clear()
  term.setCursorPos(1, 1)
end

local function writeLine(text, col)
  c(col or colors.white)
  print(text)
end

local function readClean()
  c(colors.white)
  write("> ")
  local s = read() or ""
  s = string.upper(s)
  s = string.gsub(s, "^%s+", "")
  s = string.gsub(s, "%s+$", "")
  return s
end

local function pause()
  c(colors.gray)
  print("")
  print("[key] back")
  os.pullEvent("key")
end

local function header(title)
  cls()
  c(colors.red)
  print("FUSION REACTOR CONTROL")
  c(colors.gray)
  print("RED LEVEL LOCAL TERMINAL")
  print("---------------------------------------")
  if title then
    c(colors.white)
    print(title)
    c(colors.gray)
    print("---------------------------------------")
  end
  c(colors.white)
end

local function active()
  return fs.exists(STATE_FILE)
end

local function saveActive()
  local f = fs.open(STATE_FILE, "w")
  f.write("REACTOR CONTAINMENT SIGNAL")
  f.close()
end

local function setOutput(on)
  redstone.setOutput(OUTPUT_SIDE, on)
end

local function bootOutput()
  setOutput(active())
end

local function draw(lines)
  for _, line in ipairs(lines) do
    if line == "FLOOD" or line == "SCRAM" or line == "PURGE" or line == "CLAMP" then
      writeLine(line, colors.lime)
    elseif string.find(line, "INCORRECT") or string.find(line, "STRIKE") or string.find(line, "FAILED") then
      writeLine(line, colors.red)
    elseif string.find(line, "SERIAL") or string.find(line, "RULE") or string.find(line, "PANEL") then
      writeLine(line, colors.orange)
    elseif string.find(line, "948") or string.find(line, "240") or string.find(line, "17") then
      writeLine(line, colors.cyan)
    else
      writeLine(line, colors.white)
    end
  end
end

local function page(title, lines)
  header(title)
  draw(lines)
  pause()
end

local function status()
  if active() then
    page("STATUS", {
      "CONTAINMENT SIGNAL: ACTIVE",
      "",
      "Back output is ON.",
      "",
      "Lamp should be lit.",
      "",
      "KubeJS should now treat reactor",
      "containment as armed."
    })
  else
    page("STATUS", {
      "CONTAINMENT SIGNAL: OFFLINE",
      "",
      "Reactor is still exposed.",
      "Containment answer chain required.",
      "",
      "Current local strike count:",
      tostring(strikes) .. " / " .. tostring(maxStrikes)
    })
  end
end

local function filePlate()
  page("CASE PLATE", {
    "PHYSICAL CASE PLATE",
    "",
    "Serial stamped on lower shield:",
    "",
    "T-14N-RX-948-A",
    "",
    "Service date:",
    "17 / 04",
    "",
    "Loop plate:",
    "BETA",
    "",
    "Scram bus:",
    "AMBER",
    "",
    "Do not use remote database values."
  })
end

local function filePanel()
  page("PANEL READINGS", {
    "LOCAL PANEL READINGS",
    "",
    "Core temp:",
    "948",
    "",
    "External pressure:",
    "240 o",
    "",
    "Coolant loop A:",
    "starved",
    "",
    "Coolant loop B:",
    "stable",
    "",
    "Coolant loop C:",
    "starved"
  })
end

local function fileRods()
  page("ROD REGISTER", {
    "MECHANICAL ROD REGISTER",
    "",
    "Rod bank 1:",
    "stuck open",
    "",
    "Rod bank 2:",
    "moving",
    "",
    "Rod bank 3:",
    "moving",
    "",
    "Manual note on panel:",
    "",
    "amber bus means do not test-jog"
  })
end

local function fileGas()
  page("GAS STACK", {
    "GAS STACK LOG",
    "",
    "Vent stack reads heavy.",
    "",
    "Sample tube clouded in the window.",
    "",
    "The alarm did not sound.",
    "",
    "The alarm bulb is missing.",
    "",
    "Someone removed it before lockdown.",
    "",
    "Reason not recorded."
  })
end

local function fileField()
  page("FIELD COILS", {
    "FIELD COIL NOTE",
    "",
    "Coil pair 4 is shaking.",
    "",
    "The casing sound is not pump noise.",
    "",
    "Magnet clamp has one good cycle left",
    "before manual service.",
    "",
    "Manual service team is not coming.",
    "",
    "Do the clamp step last."
  })
end

local function manualLoop()
  page("MANUAL / LOOP", {
    "COOLANT LOOP RULES",
    "",
    "Use first matching rule.",
    "",
    "If two loops are starved:",
    "FLOOD",
    "",
    "If loop B is starved:",
    "ISOLATE",
    "",
    "If serial ends in B:",
    "VENT",
    "",
    "Otherwise:",
    "HOLD"
  })
end

local function manualRods()
  page("MANUAL / RODS", {
    "ROD BANK RULES",
    "",
    "Use first matching rule.",
    "",
    "If scram bus is amber:",
    "SCRAM",
    "",
    "If bank 1 is moving:",
    "BRAKE",
    "",
    "If service date day is odd:",
    "LOCK",
    "",
    "Otherwise:",
    "WAIT"
  })
end

local function manualGas()
  page("MANUAL / STACK", {
    "GAS STACK RULES",
    "",
    "Use first matching rule.",
    "",
    "If alarm bulb is missing:",
    "PURGE",
    "",
    "If external pressure is above 200:",
    "SEAL",
    "",
    "If sample tube is clear:",
    "HOLD",
    "",
    "Otherwise:",
    "VENT"
  })
end

local function manualField()
  page("MANUAL / FIELD", {
    "FIELD COIL RULES",
    "",
    "Use first matching rule.",
    "",
    "If coil pair 4 is shaking:",
    "CLAMP",
    "",
    "If serial contains RX:",
    "GROUND",
    "",
    "If core temp is below 700:",
    "WAIT",
    "",
    "Otherwise:",
    "TRIP"
  })
end

local function noteLocal()
  page("LOCAL NOTE", {
    "UNSENT MAINTENANCE NOTE",
    "",
    "Do not pull values from the database.",
    "",
    "The database still says this reactor",
    "is cold.",
    "",
    "It is not cold.",
    "",
    "Use the panel.",
    "Use the case plate.",
    "Use the manual binder.",
    "",
    "If the case number is missing, leave."
  })
end

local function fail(label, got)
  strikes = strikes + 1

  page("INCORRECT", {
    "CHECK FAILED",
    "",
    "Field:",
    label,
    "",
    "Input:",
    got,
    "",
    "Result:",
    "INCORRECT",
    "",
    "Strike:",
    tostring(strikes) .. " / " .. tostring(maxStrikes),
    "",
    "The terminal did not arm output."
  })
end

local function ask(label, expected)
  header("CONTAINMENT / " .. label)

  draw({
    "Enter one word.",
    "",
    "Field:",
    label,
    ""
  })

  local got = readClean()

  if got == expected then
    c(colors.lime)
    print("")
    print("CORRECT")
    sleep(0.7)
    return true
  end

  c(colors.red)
  print("")
  print("INCORRECT")
  sleep(0.7)
  fail(label, got)
  return false
end

local function lockout()
  page("LOCKOUT", {
    "LOCAL LOCKOUT",
    "",
    "Too many strikes.",
    "",
    "Containment signal refused.",
    "",
    "Cycle terminal power to retry.",
    "",
    "If this is live:",
    "send someone else."
  })
end

local function arm()
  if active() then
    page("ARM", {
      "Already armed.",
      "",
      "Back output remains ON."
    })
    return
  end

  if strikes >= maxStrikes then
    lockout()
    return
  end

  if not ask("COOLANT LOOP", answers.loop) then return end
  if strikes >= maxStrikes then lockout() return end

  if not ask("ROD BANK", answers.rods) then return end
  if strikes >= maxStrikes then lockout() return end

  if not ask("GAS STACK", answers.gas) then return end
  if strikes >= maxStrikes then lockout() return end

  if not ask("FIELD COILS", answers.field) then return end
  if strikes >= maxStrikes then lockout() return end

  saveActive()
  setOutput(true)

  page("ARMED", {
    "ALL CHECKS ACCEPTED",
    "",
    "Coolant loop: OK",
    "Rod bank: OK",
    "Gas stack: OK",
    "Field coils: OK",
    "",
    "REACTOR CONTAINMENT SIGNAL",
    "ACTIVE",
    "",
    "Back output is now ON."
  })
end

bootOutput()

while true do
  header()

  if active() then
    writeLine("CONTAINMENT SIGNAL: ACTIVE", colors.lime)
  else
    writeLine("CONTAINMENT SIGNAL: OFFLINE", colors.red)
  end

  print("")
  writeLine("1. Status")
  writeLine("2. Case plate")
  writeLine("3. Panel readings")
  writeLine("4. Rod register")
  writeLine("5. Gas stack")
  writeLine("6. Field coils")
  writeLine("7. Manual / loop")
  writeLine("8. Manual / rods")
  writeLine("9. Manual / stack")
  writeLine("10. Manual / field")
  writeLine("11. Local note")
  writeLine("A. ARM")
  writeLine("Q. Close")
  print("")

  local input = readClean()

  if input == "Q" or input == "QUIT" or input == "EXIT" then
    cls()
    writeLine("SESSION CLOSED", colors.gray)
    return
  elseif input == "1" then
    status()
  elseif input == "2" then
    filePlate()
  elseif input == "3" then
    filePanel()
  elseif input == "4" then
    fileRods()
  elseif input == "5" then
    fileGas()
  elseif input == "6" then
    fileField()
  elseif input == "7" then
    manualLoop()
  elseif input == "8" then
    manualRods()
  elseif input == "9" then
    manualGas()
  elseif input == "10" then
    manualField()
  elseif input == "11" then
    noteLocal()
  elseif input == "A" or input == "ARM" then
    arm()
  else
    c(colors.red)
    print("Invalid.")
    sleep(0.7)
  end
end