Code Sketch


yoiiiiiiiiiiiiiiii
By: Mhalsakant School
Category: Programming
import javax.swing._
import javax.swing.border._
import java.awt._
import java.awt.event._
import java.awt.geom._
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import java.io._
import java.util.Random
import scala.math._
import scala.collection.mutable.ArrayBuffer

val BG = new Color(7, 10, 20)
val PANEL = new Color(14, 20, 34)
val PANEL2 = new Color(19, 28, 46)
val CARD = new Color(22, 32, 52)
val TEXT = new Color(235, 243, 252)
val MUTED = new Color(165, 181, 198)
val ACCENT = new Color(90, 190, 255)
val GREEN = new Color(90, 220, 150)
val ORANGE = new Color(255, 174, 80)
val GOLD = new Color(255, 216, 115)
val RED = new Color(245, 100, 110)
val PURPLE = new Color(176, 120, 255)
val CYAN = new Color(80, 235, 230)

def makeLabel(t: String, size: Int, bold: Boolean): JLabel = {
  val l = new JLabel(t)
  l.setForeground(TEXT)
  l.setFont(new Font("SansSerif", if (bold) Font.BOLD else Font.PLAIN, size))
  l
}

def makeButton(t: String): JButton = {
  val b = new JButton(t)
  b.setForeground(TEXT)
  b.setBackground(PANEL2)
  b.setFocusPainted(false)
  b.setFont(new Font("SansSerif", Font.BOLD, 13))
  b
}

def makeArea(): JTextArea = {
  val a = new JTextArea()
  a.setForeground(TEXT)
  a.setBackground(new Color(9, 14, 25))
  a.setCaretColor(TEXT)
  a.setFont(new Font("Monospaced", Font.PLAIN, 13))
  a.setLineWrap(true)
  a.setWrapStyleWord(true)
  a.setBorder(new EmptyBorder(10, 10, 10, 10))
  a
}

def makeField(): JTextField = {
  val f = new JTextField()
  f.setForeground(TEXT)
  f.setBackground(new Color(9, 14, 25))
  f.setCaretColor(TEXT)
  f.setFont(new Font("SansSerif", Font.PLAIN, 14))
  f
}

def titledPanel(title: String): JPanel = {
  val p = new JPanel(new BorderLayout(8, 8))
  p.setBackground(PANEL)
  p.setBorder(BorderFactory.createCompoundBorder(
    BorderFactory.createLineBorder(new Color(46, 69, 98), 1),
    new EmptyBorder(10, 10, 10, 10)
  ))
  p.add(makeLabel(title, 17, true), BorderLayout.NORTH)
  p
}

def showInfo(frame: JFrame, title: String, msg: String): Unit = {
  JOptionPane.showMessageDialog(frame, msg, title, JOptionPane.INFORMATION_MESSAGE)
}

def showWarn(frame: JFrame, title: String, msg: String): Unit = {
  JOptionPane.showMessageDialog(frame, msg, title, JOptionPane.WARNING_MESSAGE)
}

val random = new Random()
var animationTick: Int = 0
var selectedModule: String = "COMMAND CENTER"
var lastGeneratedIdea: String = ""
var lastBlueprint: String = ""
var lastResearchPlan: String = ""
var lastSkyReport: String = ""
var lastAstroReport: String = ""
var referenceImage: BufferedImage = null
val notebookEntries = ArrayBuffer[String]()

val categories = Array(
  "Clean Mobility",
  "Air & Water",
  "Energy",
  "Waste & Materials",
  "Agriculture",
  "Safety",
  "Smart Home",
  "Urban Systems",
  "Space & Astronomy",
  "Education",
  "Health Technology",
  "Disaster Response"
)

val technologies = Array(
  "heat exchanger",
  "membrane separation",
  "phase-change storage",
  "regenerative braking",
  "solar conversion",
  "sensor fusion",
  "modular filtration",
  "thermal storage",
  "machine vision",
  "digital twin",
  "edge controller",
  "recycled composite"
)

val outputs = Array(
  "cleaner exhaust",
  "recoverable water",
  "measurable energy savings",
  "lower waste",
  "early warning",
  "automated irrigation",
  "resource recovery",
  "safe shutdown",
  "high-efficiency transport",
  "research data",
  "visual sky guidance",
  "maintainable machinery"
)

val constraints = Array(
  "low cost",
  "small footprint",
  "easy maintenance",
  "modular design",
  "low noise",
  "low power",
  "replaceable components",
  "sensor-based monitoring",
  "recyclable materials",
  "offline operation",
  "weather resilience",
  "student-friendly prototype"
)

val energyModes = Array(
  "Battery-electric",
  "Hydrogen fuel-cell concept",
  "Biofuel",
  "Synthetic e-fuel",
  "Hybrid electric",
  "Solar-assisted electric",
  "Regenerative electric"
)

val machineBlocks = Array(
  "Air Intake",
  "Particle Pre-Filter",
  "Fine Filter",
  "Activated Carbon",
  "Heat Exchanger",
  "Condenser",
  "Water Tank",
  "Water Treatment",
  "Oxygen Separation Concept",
  "Exhaust Monitoring",
  "Battery Pack",
  "Motor",
  "Regenerative Brake",
  "Solar Roof",
  "Thermal Store",
  "Sensor Hub",
  "Controller",
  "Safety Shutdown",
  "Data Logger",
  "Output Display"
)

def saveText(name: String, text: String): Unit = {
  try {
    val out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(name), "UTF-8"))
    out.write(text)
    out.close()
  } catch {
    case _: Exception => ()
  }
}

def appendNotebook(text: String): Unit = {
  notebookEntries += text
  try {
    val out = new PrintWriter(new OutputStreamWriter(new FileOutputStream("FutureDiscoveryNotebook.txt", true), "UTF-8"))
    out.println()
    out.println("============================================================")
    out.println(new java.util.Date().toString)
    out.println("============================================================")
    out.println(text)
    out.close()
  } catch {
    case _: Exception => ()
  }
}

def generateInventionIdea(category: String): String = {
  val cat = if (category == "AUTO") categories(random.nextInt(categories.length)) else category
  val tech = technologies(random.nextInt(technologies.length))
  val out = outputs(random.nextInt(outputs.length))
  val con = constraints(random.nextInt(constraints.length))
  val blockA = machineBlocks(random.nextInt(machineBlocks.length))
  val blockB = machineBlocks(random.nextInt(machineBlocks.length))
  val blockC = machineBlocks(random.nextInt(machineBlocks.length))
  "FUTURE INVENTION CONCEPT\n\n" +
  "Domain: " + cat + "\n" +
  "Core technology: " + tech + "\n" +
  "Main goal: " + out + "\n" +
  "Design constraint: " + con + "\n\n" +
  "SYSTEM CONCEPT\n" +
  "1. Input / source: real-world resource or signal\n" +
  "2. Processing: " + blockA + "\n" +
  "3. Conversion / control: " + blockB + "\n" +
  "4. Recovery / output: " + blockC + "\n" +
  "5. Monitoring: Sensor Hub + Data Logger\n" +
  "6. Safety: Safety Shutdown + manual override\n\n" +
  "WHY IT IS INTERESTING\n" +
  "- Connects two or more engineering ideas in a new combination.\n" +
  "- Can be modelled digitally before building hardware.\n" +
  "- Produces measurable outputs instead of visual claims.\n\n" +
  "VALIDATION QUESTIONS\n" +
  "- What is the actual energy input?\n" +
  "- What is the mass flow or resource flow?\n" +
  "- What losses occur at every stage?\n" +
  "- Which parts need commercial data sheets?\n" +
  "- What safety limits must be checked?\n" +
  "- What small prototype can test the first assumption?\n\n" +
  "NOVELTY NOTE\n" +
  "This is a generated concept combination, not proof that nobody has invented it.\n" +
  "Search patents, papers and existing products before claiming novelty."
}

def buildResearchPlan(idea: String): String = {
  val seed = if (idea.trim.isEmpty) "a machine that recovers useful resources from everyday processes" else idea.trim
  "RESEARCH + INVENTION WORKFLOW\n\n" +
  "IDEA\n" + seed + "\n\n" +
  "STEP 1 - Define the problem\n" +
  "- Who has the problem?\n" +
  "- What resource is wasted?\n" +
  "- What result can be measured?\n\n" +
  "STEP 2 - Build the system boundary\n" +
  "- Inputs\n- Processes\n- Outputs\n- Waste streams\n- Safety boundaries\n\n" +
  "STEP 3 - Find current knowledge\n" +
  "- Search textbooks and university sources.\n" +
  "- Search manufacturer datasheets.\n" +
  "- Search standards and regulations.\n" +
  "- Search patents and prior-art databases.\n" +
  "- Record publication date and source.\n\n" +
  "STEP 4 - First-principles model\n" +
  "- Mass balance\n- Energy balance\n- Efficiency\n- Cost per unit output\n- Maintenance burden\n\n" +
  "STEP 5 - Smallest safe experiment\n" +
  "- Change one important variable at a time.\n" +
  "- Record numbers.\n" +
  "- Define a stop condition before testing.\n\n" +
  "STEP 6 - Compare against a baseline\n" +
  "- Existing method\n- Proposed method\n- Improvement / trade-off\n\n" +
  "STEP 7 - Document\n" +
  "- Version number\n- Assumptions\n- Raw observations\n- Photos / diagrams\n- What failed and why\n"
}

def buildArchitectureText(selectedBlocks: Array[String], idea: String): String = {
  val b = new StringBuilder
  b.append("MACHINE ARCHITECTURE\n")
  b.append("============================================================\n")
  b.append("Idea: " + (if (idea.trim.isEmpty) "Untitled machine" else idea.trim) + "\n\n")
  var i = 0
  while (i < selectedBlocks.length) {
    b.append((i + 1) + ". " + selectedBlocks(i) + "\n")
    i += 1
  }
  b.append("\nSIGNAL / RESOURCE FLOW\n")
  b.append("SOURCE -> INTAKE -> CONDITIONING -> CONVERSION -> OUTPUT\n")
  b.append("                         |-> SENSOR HUB -> DATA LOGGER\n")
  b.append("                         |-> SAFETY SHUTDOWN\n\n")
  b.append("ENGINEERING CHECKS\n")
  b.append("- Input power or energy source must be identified.\n")
  b.append("- Every conversion stage has losses.\n")
  b.append("- Filters need pressure-drop and maintenance data.\n")
  b.append("- Water from air comes from atmospheric water vapour, not from CO2.\n")
  b.append("- Oxygen separation and water generation are separate functions.\n")
  b.append("- Exhaust treatment reduces pollutants; it does not make exhaust into oxygen automatically.\n")
  b.append("- Real hardware requires sensors, validation and safe procedures.\n")
  b.append("\nPROTOTYPE ORDER\n")
  b.append("1) Simulation -> 2) Bench model -> 3) Instrumented prototype -> 4) Reliability test\n")
  b.toString
}

def normalizeDeg(v: Double): Double = {
  var x = v % 360.0
  if (x < 0.0) x += 360.0
  x
}

def clamp(v: Double, lo: Double, hi: Double): Double = {
  Math.max(lo, Math.min(hi, v))
}

def julianDay(year: Int, month: Int, day: Int, hour: Double): Double = {
  var y = year
  var m = month
  if (m <= 2) {
    y -= 1
    m += 12
  }
  val a = Math.floor(y / 100.0)
  val bb = 2.0 - a + Math.floor(a / 4.0)
  Math.floor(365.25 * (y + 4716)) +
  Math.floor(30.6001 * (m + 1)) +
  day + bb - 1524.5 + hour / 24.0
}

def gmstDeg(jd: Double): Double = {
  normalizeDeg(280.46061837 + 360.98564736629 * (jd - 2451545.0))
}

def planetMeanLongitude(jd: Double, base: Double, rate: Double): Double = {
  normalizeDeg(base + rate * (jd - 2451545.0))
}

def planetEclipticLon(name: String, jd: Double): Double = {
  name match {
    case "Mercury" => planetMeanLongitude(jd, 252.25084, 4.09233445)
    case "Venus"   => planetMeanLongitude(jd, 181.97973, 1.60213034)
    case "Mars"    => planetMeanLongitude(jd, 355.43300, 0.52402078)
    case "Jupiter" => planetMeanLongitude(jd, 34.35148, 0.08308530)
    case "Saturn"  => planetMeanLongitude(jd, 50.07747, 0.03344423)
    case "Sun"     => normalizeDeg(280.460 + 0.9856474 * (jd - 2451545.0))
    case "Moon"    => normalizeDeg(218.316 + 13.176396 * (jd - 2451545.0))
    case _         => 0.0
  }
}

def eclipticToEquatorial(lonDeg: Double): Array[Double] = {
  val eps = Math.toRadians(23.439291)
  val lon = Math.toRadians(normalizeDeg(lonDeg))
  val x = Math.cos(lon)
  val y = Math.cos(eps) * Math.sin(lon)
  val z = Math.sin(eps) * Math.sin(lon)
  val ra = Math.atan2(y, x)
  val dec = Math.atan2(z, Math.sqrt(x * x + y * y))
  Array(normalizeDeg(Math.toDegrees(ra)), Math.toDegrees(dec))
}

def equatorialToHorizontal(raDeg: Double, decDeg: Double, latDeg: Double, lonDeg: Double, jd: Double): Array[Double] = {
  val lst = normalizeDeg(gmstDeg(jd) + lonDeg)
  val hourAngle = Math.toRadians(normalizeDeg(lst - raDeg))
  val dec = Math.toRadians(decDeg)
  val lat = Math.toRadians(latDeg)
  val sinAlt = Math.sin(lat) * Math.sin(dec) + Math.cos(lat) * Math.cos(dec) * Math.cos(hourAngle)
  val alt = Math.asin(clamp(sinAlt, -1.0, 1.0))
  val y = -Math.sin(hourAngle) * Math.cos(dec)
  val x = Math.sin(dec) * Math.cos(lat) - Math.cos(dec) * Math.sin(lat) * Math.cos(hourAngle)
  val az = Math.atan2(y, x)
  Array(Math.toDegrees(alt), normalizeDeg(Math.toDegrees(az)))
}

def directionName(az: Double): String = {
  val dirs = Array("N", "NE", "E", "SE", "S", "SW", "W", "NW")
  dirs(Math.floor(normalizeDeg(az + 22.5) / 45.0).toInt % 8)
}

def signName(longitude: Double): String = {
  val names = Array(
    "Mesha / Aries", "Vrishabha / Taurus", "Mithuna / Gemini",
    "Karka / Cancer", "Simha / Leo", "Kanya / Virgo",
    "Tula / Libra", "Vrishchika / Scorpio", "Dhanu / Sagittarius",
    "Makara / Capricorn", "Kumbha / Aquarius", "Meena / Pisces"
  )
  names(Math.floor(normalizeDeg(longitude) / 30.0).toInt % 12)
}

def parseIntSafe(s: String, fallback: Int): Int = {
  try { s.trim.toInt } catch { case _: Exception => fallback }
}

def parseDoubleSafe(s: String, fallback: Double): Double = {
  try { s.trim.toDouble } catch { case _: Exception => fallback }
}

val frame = new JFrame("FUTURE DISCOVERY LAB OMEGA - Invent, Model, Research, Explore")
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
frame.setSize(1450, 900)
frame.setMinimumSize(new Dimension(1120, 720))
frame.setLocationRelativeTo(null)

val root = new JPanel(new BorderLayout(8, 8))
root.setBackground(BG)
root.setBorder(new EmptyBorder(8, 8, 8, 8))

val header = new JPanel(new BorderLayout(8, 4))
header.setBackground(BG)
val headerLeft = new JPanel(new GridLayout(2, 1, 2, 2))
headerLeft.setBackground(BG)
headerLeft.add(makeLabel("FUTURE DISCOVERY LAB OMEGA", 28, true))
headerLeft.add(makeLabel("Invent ? Model ? Research ? Simulate ? Explore the Sky", 14, false))
header.add(headerLeft, BorderLayout.WEST)
val liveStatus = makeLabel("SYSTEM READY ? OFFLINE ? LOCAL MODE", 13, true)
liveStatus.setHorizontalAlignment(SwingConstants.RIGHT)
header.add(liveStatus, BorderLayout.EAST)
root.add(header, BorderLayout.NORTH)

val tabs = new JTabbedPane()
tabs.setBackground(BG)
tabs.setForeground(TEXT)

// COMMAND CENTER
val commandPanel = new JPanel(new BorderLayout(10, 10))
commandPanel.setBackground(PANEL)
commandPanel.setBorder(new EmptyBorder(10, 10, 10, 10))

val commandTop = new JPanel(new GridLayout(1, 3, 10, 10))
commandTop.setBackground(PANEL)

val ideasCard = titledPanel("DISCOVERY ENGINE")
val ideasArea = makeArea()
ideasArea.setEditable(false)
ideasArea.setText("Generate invention concepts by mixing domains, technologies, outputs and constraints.\n\nThe generator produces concept combinations and validation questions.\nIt does not claim that a generated idea is guaranteed to be globally new.")
ideasCard.add(new JScrollPane(ideasArea), BorderLayout.CENTER)

val researchCard = titledPanel("RESEARCH ENGINE")
val researchArea = makeArea()
researchArea.setEditable(false)
researchArea.setText("Turn an idea into a researchable engineering question.\n\nTrack assumptions, measurements, sources, experiments and failures.")
researchCard.add(new JScrollPane(researchArea), BorderLayout.CENTER)

val skyCard = titledPanel("SKY + ASTRO LAB")
val skyArea = makeArea()
skyArea.setEditable(false)
skyArea.setText("ASTRONOMY: calculate an educational approximate sky direction.\nTRADITIONAL ASTROLOGY: explore cultural chart concepts separately.\n\nDo not mix cultural interpretations with physical astronomy.")
skyCard.add(new JScrollPane(skyArea), BorderLayout.CENTER)

commandTop.add(ideasCard)
commandTop.add(researchCard)
commandTop.add(skyCard)
commandPanel.add(commandTop, BorderLayout.NORTH)

val commandCanvas = new JPanel {
  override def paintComponent(graphics: Graphics): Unit = {
    super.paintComponent(graphics)
    val g = graphics.asInstanceOf[Graphics2D]
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
    g.setColor(new Color(8, 14, 28))
    g.fillRect(0, 0, getWidth, getHeight)
    val cx = getWidth / 2
    val cy = getHeight / 2

    g.setColor(new Color(20, 35, 60))
    var ring = 1
    while (ring <= 5) {
      val rr = ring * 62 + 25
      g.drawOval(cx - rr, cy - rr, rr * 2, rr * 2)
      ring += 1
    }

    val orbit = (animationTick * 1.5) % 360.0
    val ox = cx + Math.cos(Math.toRadians(orbit)) * 250.0
    val oy = cy + Math.sin(Math.toRadians(orbit)) * 115.0
    g.setColor(GOLD)
    g.fillOval(cx - 26, cy - 26, 52, 52)
    g.setColor(ACCENT)
    g.fillOval(ox.toInt - 10, oy.toInt - 10, 20, 20)

    val nodes = Array(
      ("IDEA FORGE", cx - 360, cy - 140, ACCENT),
      ("MACHINE LAB", cx + 170, cy - 145, GREEN),
      ("ENERGY LAB", cx - 390, cy + 105, ORANGE),
      ("AIR / WATER", cx + 180, cy + 110, CYAN),
      ("SKY LAB", cx - 95, cy - 210, PURPLE),
      ("RESEARCH", cx - 105, cy + 205, GOLD)
    )

    var i = 0
    while (i < nodes.length) {
      val n = nodes(i)
      g.setColor(new Color(n._4.getRed, n._4.getGreen, n._4.getBlue, 80))
      g.fillRoundRect(n._2 - 5, n._3 - 5, 150, 58, 18, 18)
      g.setColor(n._4)
      g.drawRoundRect(n._2 - 5, n._3 - 5, 150, 58, 18, 18)
      g.setFont(new Font("SansSerif", Font.BOLD, 12))
      g.drawString(n._1, n._2 + 12, n._3 + 30)
      g.setColor(new Color(105, 130, 160))
      g.drawLine(cx, cy, n._2 + 68, n._3 + 25)
      i += 1
    }

    g.setFont(new Font("SansSerif", Font.BOLD, 22))
    g.setColor(TEXT)
    g.drawString("DISCOVERY CORE", cx - 78, cy + 88)
    g.setFont(new Font("SansSerif", Font.PLAIN, 13))
    g.setColor(MUTED)
    g.drawString("Question -> Model -> Measurement -> Evidence -> Next Version", cx - 238, cy + 112)
  }
}
commandCanvas.setPreferredSize(new Dimension(800, 440))
commandPanel.add(new JScrollPane(commandCanvas), BorderLayout.CENTER)

val commandButtons = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 6))
commandButtons.setBackground(PANEL)
val quickIdea = makeButton("GENERATE NEW INVENTION")
val quickResearch = makeButton("TURN IDEA INTO RESEARCH PLAN")
val quickSky = makeButton("OPEN PLANET SKY LAB")
val quickExport = makeButton("SAVE COMMAND REPORT")
commandButtons.add(quickIdea)
commandButtons.add(quickResearch)
commandButtons.add(quickSky)
commandButtons.add(quickExport)
commandPanel.add(commandButtons, BorderLayout.SOUTH)
tabs.addTab("COMMAND CENTER", commandPanel)

// IDEA FORGE
val ideaPanel = new JPanel(new BorderLayout(10, 10))
ideaPanel.setBackground(PANEL)
ideaPanel.setBorder(new EmptyBorder(10, 10, 10, 10))
val ideaControls = new JPanel(new GridBagLayout())
ideaControls.setBackground(PANEL)
val ig = new GridBagConstraints()
ig.insets = new Insets(5, 5, 5, 5)
ig.fill = GridBagConstraints.HORIZONTAL

ig.gridx = 0; ig.gridy = 0
ideaControls.add(makeLabel("Domain", 13, true), ig)
val categoryBox = new JComboBox[String]()
categoryBox.addItem("AUTO")
categories.foreach(categoryBox.addItem)
ig.gridx = 1
ideaControls.add(categoryBox, ig)

ig.gridx = 0; ig.gridy = 1
ideaControls.add(makeLabel("Your idea / problem", 13, true), ig)
val ideaInput = makeField()
ideaInput.setText("Make a cleaner vehicle system that recovers useful resources from waste streams.")
ig.gridx = 1
ideaControls.add(ideaInput, ig)

val generateIdeaButton = makeButton("GENERATE IDEA")
val buildResearchButton = makeButton("RESEARCH THIS IDEA")
val saveIdeaButton = makeButton("SAVE IDEA")
ig.gridx = 2; ig.gridy = 0
ideaControls.add(generateIdeaButton, ig)
ig.gridx = 2; ig.gridy = 1
ideaControls.add(buildResearchButton, ig)
ideaPanel.add(ideaControls, BorderLayout.NORTH)

val ideaResult = makeArea()
ideaResult.setEditable(false)
ideaResult.setText("Idea Forge ready.\n\nType a problem, machine concept or invention goal.\nThe generator will produce a structured concept plus validation questions.")
ideaPanel.add(new JScrollPane(ideaResult), BorderLayout.CENTER)

val ideaBottom = new JPanel(new FlowLayout(FlowLayout.LEFT))
ideaBottom.setBackground(PANEL)
ideaBottom.add(saveIdeaButton)
val noveltyButton = makeButton("NOVELTY CHECKLIST")
val riskButton = makeButton("ENGINEERING RISK CHECK")
ideaBottom.add(noveltyButton)
ideaBottom.add(riskButton)
ideaPanel.add(ideaBottom, BorderLayout.SOUTH)
tabs.addTab("IDEA FORGE", ideaPanel)

// MACHINE ARCHITECT
val machinePanel = new JPanel(new BorderLayout(10, 10))
machinePanel.setBackground(PANEL)
machinePanel.setBorder(new EmptyBorder(10, 10, 10, 10))

val machineTop = new JPanel(new BorderLayout(8, 8))
machineTop.setBackground(PANEL)
val machineName = makeField()
machineName.setText("Future Resource Recovery Machine")
machineTop.add(makeLabel("PROJECT:", 13, true), BorderLayout.WEST)
machineTop.add(machineName, BorderLayout.CENTER)

val machineButtons = new JPanel(new FlowLayout(FlowLayout.RIGHT))
machineButtons.setBackground(PANEL)
val addBlockButton = makeButton("ADD BLOCK")
val removeBlockButton = makeButton("REMOVE")
val autoBuildButton = makeButton("AUTO ARCHITECTURE")
val loadImageButton = makeButton("REFERENCE IMAGE")
machineButtons.add(addBlockButton)
machineButtons.add(removeBlockButton)
machineButtons.add(autoBuildButton)
machineButtons.add(loadImageButton)
machineTop.add(machineButtons, BorderLayout.EAST)
machinePanel.add(machineTop, BorderLayout.NORTH)

val machineList = new DefaultListModel[String]()
val machineListBox = new JList[String](machineList)
machineListBox.setForeground(TEXT)
machineListBox.setBackground(new Color(9, 14, 25))
machineListBox.setFont(new Font("Monospaced", Font.PLAIN, 13))

val blockBox = new JComboBox[String]()
machineBlocks.foreach(blockBox.addItem)

val palette = new JPanel(new BorderLayout(8, 8))
palette.setBackground(PANEL)
palette.setBorder(BorderFactory.createLineBorder(new Color(46, 69, 98)))
palette.add(makeLabel("BLOCK PALETTE", 15, true), BorderLayout.NORTH)
palette.add(blockBox, BorderLayout.CENTER)
palette.add(new JScrollPane(machineListBox), BorderLayout.SOUTH)

val machineCanvas = new JPanel {
  override def paintComponent(graphics: Graphics): Unit = {
    super.paintComponent(graphics)
    val g = graphics.asInstanceOf[Graphics2D]
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
    g.setColor(new Color(7, 12, 22))
    g.fillRect(0, 0, getWidth, getHeight)

    g.setColor(new Color(23, 42, 67))
    var yy = 20
    while (yy < getHeight) { g.drawLine(0, yy, getWidth, yy); yy += 40 }
    var xx = 20
    while (xx < getWidth) { g.drawLine(xx, 0, xx, getHeight); xx += 40 }

    g.setFont(new Font("SansSerif", Font.BOLD, 18))
    g.setColor(TEXT)
    g.drawString("MACHINE DIGITAL BLUEPRINT", 24, 30)

    var index = 0
    while (index < machineList.size()) {
      val name = machineList.get(index)
      val bx = 35 + (index % 3) * 245
      val by = 65 + (index / 3) * 105
      val pulse = 8 + ((animationTick + index * 17) % 16)
      val bc =
        if (name.toLowerCase.contains("safety")) RED
        else if (name.toLowerCase.contains("sensor")) PURPLE
        else if (name.toLowerCase.contains("water")) CYAN
        else if (name.toLowerCase.contains("energy") || name.toLowerCase.contains("battery") || name.toLowerCase.contains("motor")) ORANGE
        else ACCENT
      g.setColor(new Color(bc.getRed, bc.getGreen, bc.getBlue, 35))
      g.fillRoundRect(bx - 6, by - 6, 190, 58, 16, 16)
      g.setColor(bc)
      g.drawRoundRect(bx - 6, by - 6, 190, 58, 16, 16)
      g.fillOval(bx + 168, by + 17, pulse, pulse)
      g.setFont(new Font("SansSerif", Font.BOLD, 12))
      g.drawString(name, bx + 10, by + 26)
      if (index < machineList.size() - 1) {
        val nx = 35 + ((index + 1) % 3) * 245
        val ny = 65 + ((index + 1) / 3) * 105 + 22
        g.setColor(new Color(90, 135, 175))
        g.drawLine(bx + 186, by + 23, nx, ny)
        g.fillOval(nx - 4, ny - 4, 8, 8)
      }
      index += 1
    }

    if (referenceImage != null) {
      val iw = Math.min(230, referenceImage.getWidth)
      val ih = Math.min(160, referenceImage.getHeight)
      val scaled = referenceImage.getScaledInstance(iw, ih, java.awt.Image.SCALE_SMOOTH)
      g.drawImage(scaled, getWidth - iw - 18, getHeight - ih - 18, null)
      g.setColor(GOLD)
      g.drawString("REFERENCE IMAGE", getWidth - iw - 18, getHeight - ih - 24)
    }
  }
}
machineCanvas.setPreferredSize(new Dimension(820, 610))
machinePanel.add(palette, BorderLayout.WEST)
machinePanel.add(new JScrollPane(machineCanvas), BorderLayout.CENTER)
val machineStatus = makeLabel("No architecture generated yet.", 13, false)
machinePanel.add(machineStatus, BorderLayout.SOUTH)
tabs.addTab("MACHINE ARCHITECT", machinePanel)

// ENERGY + MOBILITY
val energyPanel = new JPanel(new BorderLayout(10, 10))
energyPanel.setBackground(PANEL)
energyPanel.setBorder(new EmptyBorder(10, 10, 10, 10))

val energyLeft = titledPanel("POWERTRAIN OPTIONS")
val energyText = makeArea()
energyText.setEditable(false)
energyText.setText(
  "PETROL / DIESEL:\nCombustion engine -> exhaust -> after-treatment -> atmosphere.\n\n" +
  "BATTERY-ELECTRIC:\nBattery -> inverter -> motor -> wheels -> regenerative braking.\n\n" +
  "HYDROGEN FUEL-CELL CONCEPT:\nStored hydrogen -> fuel cell -> electricity -> motor.\n" +
  "This studio treats hydrogen as a design concept only; real systems require professional engineering and strict safety controls.\n\n" +
  "BIOFUEL / E-FUEL:\nChemical fuel -> combustion -> exhaust treatment -> drive.\n\n" +
  "IMPORTANT:\nSmoke is a combustion by-product, not an oxygen source.\nExhaust-cleaning and oxygen-generation are separate engineering functions."
)
energyLeft.add(new JScrollPane(energyText), BorderLayout.CENTER)

val energyRight = titledPanel("COMPARE + MODEL")
val energyModeBox = new JComboBox[String]()
energyModes.foreach(energyModeBox.addItem)
val energyArea = makeArea()
energyArea.setEditable(false)
val energyButtons = new JPanel(new FlowLayout(FlowLayout.LEFT))
energyButtons.setBackground(PANEL)
val compareEnergyButton = makeButton("COMPARE")
val vehicleIdeaButton = makeButton("GENERATE VEHICLE IDEA")
energyButtons.add(energyModeBox)
energyButtons.add(compareEnergyButton)
energyButtons.add(vehicleIdeaButton)
energyRight.add(energyButtons, BorderLayout.NORTH)
energyRight.add(new JScrollPane(energyArea), BorderLayout.CENTER)
energyPanel.add(energyLeft, BorderLayout.WEST)
energyPanel.add(energyRight, BorderLayout.CENTER)
tabs.addTab("ENERGY + MOBILITY", energyPanel)

// AIR + WATER
var humidity: Double = 60.0
var ambientC: Double = 27.0
var coilC: Double = 8.0
var airflow: Double = 900.0
var collectionEfficiency: Double = 88.0
var airWaterRunning: Boolean = true

def satVaporPressurePa(tempC: Double): Double = {
  val es = 0.61094 * Math.exp((17.625 * tempC) / (tempC + 243.04))
  es * 1000.0
}

def humidityRatio(tempC: Double, rh: Double, pressurePa: Double): Double = {
  val pv = satVaporPressurePa(tempC) * clamp(rh, 0.0, 100.0) / 100.0
  val p = Math.max(1000.0, pressurePa)
  if (pv >= p * 0.98) 0.0 else 0.62198 * pv / (p - pv)
}

def waterYieldLph(): Double = {
  val p = 101325.0
  val wIn = humidityRatio(ambientC, humidity, p)
  val pvCoil = satVaporPressurePa(coilC)
  val wCoil = if (pvCoil >= p * 0.98) 0.0 else 0.62198 * pvCoil / (p - pvCoil)
  val hfg = Math.max(1.0, wIn - wCoil)
  val dryAir = airflow / 0.86
  val kg = dryAir * hfg * 0.68 * collectionEfficiency / 100.0 / 1000.0
  Math.max(0.0, kg)
}

val waterPanel = new JPanel(new BorderLayout(10, 10))
waterPanel.setBackground(PANEL)
waterPanel.setBorder(new EmptyBorder(10, 10, 10, 10))
val waterInfo = titledPanel("AIR -> CLEANING -> CONDENSATION -> WATER")
val waterReport = makeArea()
waterReport.setEditable(false)

def refreshWaterReport(): Unit = {
  val dewEstimate =
    if (humidity <= 0.0) -50.0
    else {
      val aa = 17.625
      val bb = 243.04
      val gamma = Math.log(humidity / 100.0) + aa * ambientC / (bb + ambientC)
      (bb * gamma) / (aa - gamma)
    }
  val yieldL = if (airWaterRunning) waterYieldLph() else 0.0
  val energy = 1.2 + (airflow / 1000.0) * 0.35
  val lPerKwh = if (energy > 0.0) yieldL / energy else 0.0

  waterReport.setText(
    "AIR-TO-WATER ENGINEERING ESTIMATE\n" +
    "------------------------------------------------------------\n" +
    "Ambient temperature     : " + f"$ambientC%.1f" + " C\n" +
    "Relative humidity       : " + f"$humidity%.1f" + " %\n" +
    "Approx. dew point       : " + f"$dewEstimate%.1f" + " C\n" +
    "Coil temperature        : " + f"$coilC%.1f" + " C\n" +
    "Airflow                 : " + f"$airflow%.0f" + " m3/h\n" +
    "Collection efficiency   : " + f"$collectionEfficiency%.0f" + " %\n\n" +
    "Estimated recovered water: " + f"$yieldL%.3f" + " L/h\n" +
    "Illustrative power model : " + f"$energy%.2f" + " kW\n" +
    "Estimated water / kWh    : " + f"$lPerKwh%.3f" + " L/kWh\n\n" +
    "ENGINEERING NOTES\n" +
    "- Water comes from atmospheric H2O vapour.\n" +
    "- CO2 is not converted into water in this model.\n" +
    "- Dirty air filtration and water generation are separate stages.\n" +
    "- Potable use needs appropriate treatment, maintenance and testing.\n" +
    "- Values are simplified estimates, not certified field measurements."
  )
}
waterInfo.add(new JScrollPane(waterReport), BorderLayout.CENTER)

val waterControls = new JPanel(new GridLayout(2, 5, 6, 6))
waterControls.setBackground(PANEL)
val humiditySlider = new JSlider(5, 95, 60)
val tempSlider = new JSlider(-5, 45, 27)
val coilSlider = new JSlider(2, 20, 8)
val airflowSlider = new JSlider(200, 2500, 900)
val collectionSlider = new JSlider(20, 100, 88)

def sliderCell(name: String, s: JSlider): JPanel = {
  val p = new JPanel(new BorderLayout())
  p.setBackground(PANEL2)
  p.add(makeLabel(name, 11, true), BorderLayout.NORTH)
  p.add(s, BorderLayout.CENTER)
  p
}

waterControls.add(sliderCell("Humidity %", humiditySlider))
waterControls.add(sliderCell("Ambient C", tempSlider))
waterControls.add(sliderCell("Coil C", coilSlider))
waterControls.add(sliderCell("Airflow m3/h", airflowSlider))
waterControls.add(sliderCell("Collection %", collectionSlider))

val waterControlButtons = new JPanel(new FlowLayout(FlowLayout.LEFT))
waterControlButtons.setBackground(PANEL)
val waterToggle = makeButton("PAUSE / RUN")
val waterReportSave = makeButton("SAVE WATER REPORT")
waterControlButtons.add(waterToggle)
waterControlButtons.add(waterReportSave)

waterPanel.add(waterInfo, BorderLayout.CENTER)
val waterSouth = new JPanel(new BorderLayout())
waterSouth.setBackground(PANEL)
waterSouth.add(waterControls, BorderLayout.CENTER)
waterSouth.add(waterControlButtons, BorderLayout.SOUTH)
waterPanel.add(waterSouth, BorderLayout.SOUTH)
tabs.addTab("AIR + WATER", waterPanel)

// PLANET SKY LAB
val skyPanel = new JPanel(new BorderLayout(10, 10))
skyPanel.setBackground(PANEL)
skyPanel.setBorder(new EmptyBorder(10, 10, 10, 10))

val skyInputs = new JPanel(new GridLayout(2, 10, 6, 6))
skyInputs.setBackground(PANEL)
val yearField = makeField(); yearField.setText("2026")
val monthField = makeField(); monthField.setText("9")
val dayField = makeField(); dayField.setText("25")
val hourField = makeField(); hourField.setText("19")
val minuteField = makeField(); minuteField.setText("30")
val latField = makeField(); latField.setText("18.52")
val lonField = makeField(); lonField.setText("73.86")
val planetBox = new JComboBox[String]()
Array("Sun", "Moon", "Mercury", "Venus", "Mars", "Jupiter", "Saturn").foreach(planetBox.addItem)

skyInputs.add(makeLabel("YEAR", 11, true)); skyInputs.add(yearField)
skyInputs.add(makeLabel("MONTH", 11, true)); skyInputs.add(monthField)
skyInputs.add(makeLabel("DAY", 11, true)); skyInputs.add(dayField)
skyInputs.add(makeLabel("HOUR UTC", 11, true)); skyInputs.add(hourField)
skyInputs.add(makeLabel("MINUTE", 11, true)); skyInputs.add(minuteField)
skyInputs.add(makeLabel("LAT", 11, true)); skyInputs.add(latField)
skyInputs.add(makeLabel("LON", 11, true)); skyInputs.add(lonField)
skyInputs.add(makeLabel("TARGET", 11, true)); skyInputs.add(planetBox)
val calculateSkyButton = makeButton("CALCULATE DIRECTION")
val multiPlanetButton = makeButton("SCAN ALL PLANETS")
skyInputs.add(calculateSkyButton)
skyInputs.add(multiPlanetButton)

val skyResult = makeArea()
skyResult.setEditable(false)

val skyView = new JPanel {
  override def paintComponent(graphics: Graphics): Unit = {
    super.paintComponent(graphics)
    val g = graphics.asInstanceOf[Graphics2D]
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
    g.setColor(new Color(4, 8, 18))
    g.fillRect(0, 0, getWidth, getHeight)
    val horizon = getHeight - 95
    g.setColor(new Color(22, 35, 55))
    g.fillRect(0, horizon, getWidth, 95)
    g.setColor(new Color(33, 52, 78))
    g.fillRect(0, horizon - 4, getWidth, 4)
    g.setColor(GOLD)
    g.fillOval(getWidth - 125, horizon - 110, 58, 58)
    var i = 0
    while (i < 36) {
      val x = (i * 97 + 33) % Math.max(1, getWidth)
      val y = 20 + (i * 43) % Math.max(1, horizon - 35)
      val alpha = 100 + ((i * 31 + animationTick) % 130)
      g.setColor(new Color(180, 210, 240, alpha))
      g.fillOval(x, y, 2 + (i % 3), 2 + (i % 3))
      i += 1
    }
    val target = planetBox.getSelectedItem.toString
    val pulse = 5 + ((animationTick / 3) % 9)
    val py = horizon - 150 + Math.sin(animationTick * 0.05) * 35
    val px = getWidth / 2 + Math.cos(animationTick * 0.025) * 180
    g.setColor(ACCENT)
    g.fillOval(px.toInt - pulse, py.toInt - pulse, pulse * 2, pulse * 2)
    g.setColor(TEXT)
    g.setFont(new Font("SansSerif", Font.BOLD, 16))
    g.drawString(target, px.toInt + 12, py.toInt - 8)
    g.setFont(new Font("SansSerif", Font.PLAIN, 12))
    g.setColor(MUTED)
    g.drawString("Interactive educational sky view ? approximate", 18, 24)
    g.drawString("N ? NE ? E ? SE ? S ? SW ? W ? NW", 18, horizon + 26)
  }
}
skyView.setPreferredSize(new Dimension(850, 500))
val skyCenter = new JPanel(new BorderLayout(8, 8))
skyCenter.setBackground(PANEL)
skyCenter.add(new JScrollPane(skyView), BorderLayout.CENTER)
skyCenter.add(new JScrollPane(skyResult), BorderLayout.SOUTH)
skyPanel.add(skyInputs, BorderLayout.NORTH)
skyPanel.add(skyCenter, BorderLayout.CENTER)

val skyNowButton = makeButton("USE LAPTOP CURRENT TIME")
val skyNowInfo = makeLabel("Manual location required for direction.", 12, false)
val skyNowPanel = new JPanel(new FlowLayout(FlowLayout.LEFT))
skyNowPanel.setBackground(PANEL)
skyNowPanel.add(skyNowButton)
skyNowPanel.add(skyNowInfo)
skyPanel.add(skyNowPanel, BorderLayout.SOUTH)

skyNowButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    try {
      val cal = java.util.Calendar.getInstance()
      val yy = cal.get(java.util.Calendar.YEAR)
      val mm = cal.get(java.util.Calendar.MONTH) + 1
      val dd = cal.get(java.util.Calendar.DAY_OF_MONTH)
      val localHour = cal.get(java.util.Calendar.HOUR_OF_DAY) + cal.get(java.util.Calendar.MINUTE) / 60.0
      val offsetMillis = cal.get(java.util.Calendar.ZONE_OFFSET) + cal.get(java.util.Calendar.DST_OFFSET)
      val utcHour = localHour - offsetMillis.toDouble / 3600000.0
      yearField.setText(yy.toString)
      monthField.setText(mm.toString)
      dayField.setText(dd.toString)
      hourField.setText(f"$utcHour%.2f")
      skyNowInfo.setText("Laptop time loaded as UTC approximation; set your location.")
      liveStatus.setText("LAPTOP TIME LOADED")
    } catch {
      case _: Exception => showWarn(frame, "SKY LAB", "Could not read laptop time.")
    }
  }
})

tabs.addTab("PLANET SKY LAB", skyPanel)

// TRADITIONAL ASTROLOGY
val astroPanel = new JPanel(new BorderLayout(10, 10))
astroPanel.setBackground(PANEL)
astroPanel.setBorder(new EmptyBorder(10, 10, 10, 10))
val astroTop = new JPanel(new GridLayout(2, 8, 6, 6))
astroTop.setBackground(PANEL)
val astroYear = makeField(); astroYear.setText("2008")
val astroMonth = makeField(); astroMonth.setText("1")
val astroDay = makeField(); astroDay.setText("1")
val astroHour = makeField(); astroHour.setText("12")
val astroLat = makeField(); astroLat.setText("18.52")
val astroLon = makeField(); astroLon.setText("73.86")
val astroRun = makeButton("BUILD TRADITIONAL CHART")
astroTop.add(makeLabel("YEAR",11,true)); astroTop.add(astroYear)
astroTop.add(makeLabel("MONTH",11,true)); astroTop.add(astroMonth)
astroTop.add(makeLabel("DAY",11,true)); astroTop.add(astroDay)
astroTop.add(makeLabel("HOUR",11,true)); astroTop.add(astroHour)
astroTop.add(makeLabel("LAT",11,true)); astroTop.add(astroLat)
astroTop.add(makeLabel("LON",11,true)); astroTop.add(astroLon)
astroTop.add(astroRun)
astroTop.add(makeLabel("CULTURAL MODE",11,true))

val astroResult = makeArea()
astroResult.setEditable(false)
astroResult.setText(
  "TRADITIONAL ASTROLOGY LAB\n\n" +
  "This module is for cultural / traditional chart exploration.\n" +
  "The numerical positions are simplified educational approximations.\n" +
  "Astrological interpretations are not presented as scientifically established causal effects."
)

val astroCanvas = new JPanel {
  override def paintComponent(graphics: Graphics): Unit = {
    super.paintComponent(graphics)
    val g = graphics.asInstanceOf[Graphics2D]
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
    g.setColor(new Color(10, 7, 22))
    g.fillRect(0,0,getWidth,getHeight)
    val cx = getWidth / 2
    val cy = getHeight / 2
    val rr = Math.min(getWidth, getHeight) / 2 - 25
    g.setColor(new Color(60, 45, 100))
    var i = 1
    while (i <= 6) {
      val r = rr * i / 6
      g.drawOval(cx-r, cy-r, r*2, r*2)
      i += 1
    }
    var a = 0
    while (a < 12) {
      val ang = Math.toRadians(a * 30.0 + animationTick * 0.15)
      val x = cx + Math.cos(ang) * rr
      val y = cy + Math.sin(ang) * rr
      g.setColor(new Color(130, 110, 180))
      g.drawLine(cx, cy, x.toInt, y.toInt)
      a += 1
    }
    g.setColor(GOLD)
    g.fillOval(cx - 25, cy - 25, 50, 50)
    g.setColor(TEXT)
    g.setFont(new Font("Serif", Font.BOLD, 17))
    g.drawString("12-SIGN SKY WHEEL", cx - 75, cy + rr + 22)
  }
}
astroCanvas.setPreferredSize(new Dimension(650, 500))
val astroSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(astroCanvas), new JScrollPane(astroResult))
astroSplit.setResizeWeight(0.52)
astroPanel.add(astroTop, BorderLayout.NORTH)
astroPanel.add(astroSplit, BorderLayout.CENTER)
tabs.addTab("TRADITIONAL ASTROLOGY", astroPanel)

// RESEARCH PLANNER
val researchPanel = new JPanel(new BorderLayout(10, 10))
researchPanel.setBackground(PANEL)
researchPanel.setBorder(new EmptyBorder(10, 10, 10, 10))
val researchTop = new JPanel(new BorderLayout(8, 8))
researchTop.setBackground(PANEL)
val researchIdeaField = makeField()
researchIdeaField.setText("How can a small machine reduce resource waste while staying low-power and maintainable?")
val researchGenerate = makeButton("BUILD RESEARCH PLAN")
researchTop.add(makeLabel("QUESTION:", 13, true), BorderLayout.WEST)
researchTop.add(researchIdeaField, BorderLayout.CENTER)
researchTop.add(researchGenerate, BorderLayout.EAST)
val researchText = makeArea()
researchText.setEditable(false)
researchText.setText(buildResearchPlan(researchIdeaField.getText))
val researchChecklist = makeArea()
researchChecklist.setEditable(false)
researchChecklist.setText(
  "RESEARCH CHECKLIST\n\n" +
  "[ ] Define the problem\n[ ] Define measurable output\n[ ] Identify existing solutions\n" +
  "[ ] Search patents / prior art\n[ ] Search papers / books\n[ ] Read manufacturer datasheets\n" +
  "[ ] Record assumptions\n[ ] Create a baseline\n[ ] Build the smallest safe prototype\n" +
  "[ ] Measure results\n[ ] Write what failed\n[ ] Update the design\n[ ] Repeat"
)
val researchBody = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(researchText), new JScrollPane(researchChecklist))
researchBody.setResizeWeight(0.72)
researchPanel.add(researchTop, BorderLayout.NORTH)
researchPanel.add(researchBody, BorderLayout.CENTER)
tabs.addTab("RESEARCH PLANNER", researchPanel)

// PROTOTYPE SIMULATOR
val simPanel = new JPanel(new BorderLayout(10,10))
simPanel.setBackground(PANEL)
simPanel.setBorder(new EmptyBorder(10,10,10,10))
val simControl = new JPanel(new GridLayout(4,2,6,6))
simControl.setBackground(PANEL)
val inputPowerSlider = new JSlider(100, 5000, 1000)
val efficiencySlider = new JSlider(5, 98, 70)
val runtimeSlider = new JSlider(1, 24, 1)
val factorSlider = new JSlider(10, 200, 100)
simControl.add(makeLabel("INPUT POWER W", 12, true)); simControl.add(inputPowerSlider)
simControl.add(makeLabel("EFFICIENCY %", 12, true)); simControl.add(efficiencySlider)
simControl.add(makeLabel("RUNTIME HOURS", 12, true)); simControl.add(runtimeSlider)
simControl.add(makeLabel("OUTPUT FACTOR %", 12, true)); simControl.add(factorSlider)
val simReport = makeArea()
simReport.setEditable(false)

def refreshSimulation(): Unit = {
  val inputW = inputPowerSlider.getValue.toDouble
  val eff = efficiencySlider.getValue.toDouble
  val runtime = runtimeSlider.getValue.toDouble
  val factor = factorSlider.getValue.toDouble / 100.0
  val inputKwh = inputW / 1000.0 * runtime
  val outputKwh = inputKwh * eff / 100.0 * factor
  val loss = Math.max(0.0, inputKwh - outputKwh)
  simReport.setText(
    "DIGITAL PROTOTYPE SIMULATOR\n\n" +
    "Input energy : " + f"$inputKwh%.3f" + " kWh\n" +
    "Model output : " + f"$outputKwh%.3f" + " kWh-equivalent\n" +
    "Model loss   : " + f"$loss%.3f" + " kWh\n" +
    "Efficiency   : " + f"$eff%.1f" + " %\n\n" +
    "Use this as a first-order model.\nA real machine needs measured component data and validation."
  )
}
simPanel.add(simControl, BorderLayout.NORTH)
simPanel.add(new JScrollPane(simReport), BorderLayout.CENTER)
val simBottom = new JPanel(new FlowLayout(FlowLayout.LEFT))
simBottom.setBackground(PANEL)
val simReset = makeButton("RESET")
val simSave = makeButton("SAVE SIMULATION")
simBottom.add(simReset)
simBottom.add(simSave)
simPanel.add(simBottom, BorderLayout.SOUTH)
tabs.addTab("PROTOTYPE SIMULATOR", simPanel)

// NOTEBOOK
val notePanel = new JPanel(new BorderLayout(10,10))
notePanel.setBackground(PANEL)
notePanel.setBorder(new EmptyBorder(10,10,10,10))
val noteTop = new JPanel(new FlowLayout(FlowLayout.LEFT))
noteTop.setBackground(PANEL)
val noteTitleField = makeField()
noteTitleField.setText("My next invention")
val noteSaveButton = makeButton("SAVE NOTE")
val noteExportButton = makeButton("EXPORT NOTEBOOK")
noteTop.add(makeLabel("TITLE:", 12, true))
noteTop.add(noteTitleField)
noteTop.add(noteSaveButton)
noteTop.add(noteExportButton)
val noteEditor = makeArea()
noteEditor.setText(
  "Problem:\n\nHypothesis:\n\nInputs:\n\nExpected output:\n\nExperiment:\n\nObserved result:\n\nWhat failed:\n\nNext version:\n"
)
notePanel.add(noteTop, BorderLayout.NORTH)
notePanel.add(new JScrollPane(noteEditor), BorderLayout.CENTER)
tabs.addTab("DISCOVERY NOTEBOOK", notePanel)


// KNOWLEDGE INTAKE
val knowledgePanel = new JPanel(new BorderLayout(10, 10))
knowledgePanel.setBackground(PANEL)
knowledgePanel.setBorder(new EmptyBorder(10, 10, 10, 10))

val knowledgeTop = new JPanel(new GridLayout(2, 1, 6, 6))
knowledgeTop.setBackground(PANEL)
val sourceField = makeField()
sourceField.setText("https://")
val knowledgeQuery = makeField()
knowledgeQuery.setText("search terms for my invention")
knowledgeTop.add(sourceField)
knowledgeTop.add(knowledgeQuery)

val knowledgeArea = makeArea()
knowledgeArea.setEditable(false)
knowledgeArea.setText(
  "KNOWLEDGE INTAKE WORKFLOW\n\n" +
  "1. Search several independent sources.\n" +
  "2. Record the date, title and author.\n" +
  "3. Separate established facts from opinions.\n" +
  "4. Compare at least two sources for important claims.\n" +
  "5. Save the useful part into your project notebook.\n" +
  "6. Turn one open question into one measurable experiment.\n\n" +
  "This module does not fetch or verify internet content by itself."
)

val knowledgeButtons = new JPanel(new FlowLayout(FlowLayout.LEFT))
knowledgeButtons.setBackground(PANEL)
val openSourceButton = makeButton("OPEN SOURCE")
val saveSourceButton = makeButton("SAVE SOURCE NOTE")
val buildQuestionButton = makeButton("MAKE RESEARCH QUESTION")
knowledgeButtons.add(openSourceButton)
knowledgeButtons.add(saveSourceButton)
knowledgeButtons.add(buildQuestionButton)

knowledgePanel.add(knowledgeTop, BorderLayout.NORTH)
knowledgePanel.add(new JScrollPane(knowledgeArea), BorderLayout.CENTER)
knowledgePanel.add(knowledgeButtons, BorderLayout.SOUTH)
tabs.addTab("KNOWLEDGE INTAKE", knowledgePanel)

// DISCOVERY MATRIX
val matrixPanel = new JPanel(new BorderLayout(10,10))
matrixPanel.setBackground(PANEL)
matrixPanel.setBorder(new EmptyBorder(10,10,10,10))

val matrixTop = new JPanel(new BorderLayout(8,8))
matrixTop.setBackground(PANEL)
val matrixGoal = makeField()
matrixGoal.setText("Solve a resource-waste problem with a compact machine.")
val matrixGenerate = makeButton("GENERATE 12 CONCEPTS")
matrixTop.add(makeLabel("GOAL:",13,true), BorderLayout.WEST)
matrixTop.add(matrixGoal, BorderLayout.CENTER)
matrixTop.add(matrixGenerate, BorderLayout.EAST)

val matrixArea = makeArea()
matrixArea.setEditable(false)
matrixPanel.add(matrixTop, BorderLayout.NORTH)
matrixPanel.add(new JScrollPane(matrixArea), BorderLayout.CENTER)
tabs.addTab("DISCOVERY MATRIX", matrixPanel)

// PROJECT ROADMAP
val roadmapPanel = new JPanel(new BorderLayout(10,10))
roadmapPanel.setBackground(PANEL)
roadmapPanel.setBorder(new EmptyBorder(10,10,10,10))
val roadmapArea = makeArea()
roadmapArea.setEditable(false)
roadmapArea.setText(
  "PROJECT ROADMAP\n\n" +
  "PHASE 0 - Problem\n" +
  "[ ] Define who needs it\n[ ] Define current pain point\n\n" +
  "PHASE 1 - Science / Engineering\n" +
  "[ ] Identify inputs\n[ ] Identify outputs\n[ ] Estimate losses\n[ ] Select measurable variables\n\n" +
  "PHASE 2 - Digital prototype\n" +
  "[ ] Draw block diagram\n[ ] Create first-order model\n[ ] Run edge cases\n\n" +
  "PHASE 3 - Physical prototype\n" +
  "[ ] Safety review\n[ ] Choose parts\n[ ] Instrument the system\n[ ] Keep a test log\n\n" +
  "PHASE 4 - Validation\n" +
  "[ ] Compare to baseline\n[ ] Repeat tests\n[ ] Record uncertainty\n[ ] Document failures\n\n" +
  "PHASE 5 - Improvement\n" +
  "[ ] Remove unnecessary complexity\n[ ] Improve maintainability\n[ ] Re-test\n[ ] Update version number"
)
val roadmapButton = makeButton("SAVE ROADMAP")
roadmapPanel.add(new JScrollPane(roadmapArea), BorderLayout.CENTER)
val roadmapBottom = new JPanel(new FlowLayout(FlowLayout.LEFT))
roadmapBottom.setBackground(PANEL)
roadmapBottom.add(roadmapButton)
roadmapPanel.add(roadmapBottom, BorderLayout.SOUTH)
tabs.addTab("PROJECT ROADMAP", roadmapPanel)

// REFERENCE IMAGE
val imagePanel = new JPanel(new BorderLayout(10,10))
imagePanel.setBackground(PANEL)
imagePanel.setBorder(new EmptyBorder(10,10,10,10))
val imageText = makeArea()
imageText.setEditable(false)
imageText.setText(
  "REFERENCE IMAGE WORKBENCH\n\n" +
  "Load an image of a sketch, machine, circuit or concept board.\n" +
  "The studio keeps the image locally and shows basic size information.\n\n" +
  "A visual reference can guide a design, but it does not prove how real hardware works."
)

val imagePreview = new JPanel {
  override def paintComponent(graphics: Graphics): Unit = {
    super.paintComponent(graphics)
    val g = graphics.asInstanceOf[Graphics2D]
    g.setColor(new Color(8, 12, 22))
    g.fillRect(0,0,getWidth,getHeight)
    if (referenceImage != null) {
      val scale = Math.min(
        (getWidth - 30).toDouble / Math.max(1, referenceImage.getWidth),
        (getHeight - 60).toDouble / Math.max(1, referenceImage.getHeight)
      )
      val iw = Math.max(1, (referenceImage.getWidth * scale).toInt)
      val ih = Math.max(1, (referenceImage.getHeight * scale).toInt)
      val x = (getWidth - iw) / 2
      val y = (getHeight - ih) / 2
      g.drawImage(referenceImage.getScaledInstance(iw, ih, java.awt.Image.SCALE_SMOOTH), x, y, null)
      g.setColor(GOLD)
      g.drawString(referenceImage.getWidth + " x " + referenceImage.getHeight, 15, 20)
    } else {
      g.setColor(MUTED)
      g.drawString("No reference image loaded.", 25, 35)
    }
  }
}
imagePreview.setPreferredSize(new Dimension(720, 560))
val imageButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT))
imageButtonPanel.setBackground(PANEL)
val imageOpen = makeButton("OPEN IMAGE")
val imageClear = makeButton("CLEAR IMAGE")
imageButtonPanel.add(imageOpen)
imageButtonPanel.add(imageClear)
imagePanel.add(imageButtonPanel, BorderLayout.NORTH)
imagePanel.add(new JScrollPane(imagePreview), BorderLayout.CENTER)
imagePanel.add(new JScrollPane(imageText), BorderLayout.EAST)
tabs.addTab("REFERENCE IMAGE", imagePanel)

// GUIDE
val infoPanel = new JPanel(new BorderLayout(10,10))
infoPanel.setBackground(PANEL)
infoPanel.setBorder(new EmptyBorder(12,12,12,12))
val infoArea = makeArea()
infoArea.setEditable(false)
infoArea.setText(
  "FUTURE DISCOVERY LAB OMEGA\n\n" +
  "Purpose:\n" +
  "- Build invention concepts.\n- Turn ideas into architectures.\n- Run first-order models.\n- Explore approximate planetary directions.\n- Keep research notes.\n\n" +
  "SCIENCE SEPARATIONS\n" +
  "1. Astronomy = physical celestial observation and physics.\n" +
  "2. Traditional astrology = cultural interpretive practice.\n" +
  "3. Generated concepts are not proof of novelty.\n" +
  "4. Simulators are models, not measurements.\n\n" +
  "NEW KNOWLEDGE WORKFLOW\nQuestion -> Sources -> Evidence -> Model -> Test -> Measure -> Document -> Improve\n\n" +
  "For real hardware, use appropriate engineering review and safety controls."
)
infoPanel.add(new JScrollPane(infoArea), BorderLayout.CENTER)
tabs.addTab("LAB GUIDE", infoPanel)

// WIRING
generateIdeaButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    val category = categoryBox.getSelectedItem.toString
    lastGeneratedIdea = generateInventionIdea(category)
    ideaResult.setText(lastGeneratedIdea)
    appendNotebook("IDEA FORGE\n" + lastGeneratedIdea)
    liveStatus.setText("IDEA GENERATED ? VALIDATE WITH EVIDENCE")
  }
})

saveIdeaButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    val text = if (lastGeneratedIdea.isEmpty) ideaResult.getText else lastGeneratedIdea
    saveText("FutureIdea.txt", text)
    liveStatus.setText("IDEA SAVED LOCALLY")
  }
})

buildResearchButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    lastResearchPlan = buildResearchPlan(ideaInput.getText)
    researchIdeaField.setText(ideaInput.getText)
    researchText.setText(lastResearchPlan)
    tabs.setSelectedComponent(researchPanel)
    liveStatus.setText("RESEARCH PLAN READY")
  }
})

noveltyButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    val idea = if (ideaInput.getText.trim.isEmpty) "Untitled concept" else ideaInput.getText.trim
    ideaResult.setText(
      "NOVELTY CHECKLIST\n\nIdea: " + idea + "\n\n" +
      "[ ] Exact phrase search\n[ ] Similar product search\n[ ] Patent search\n" +
      "[ ] Academic paper search\n[ ] Standards / regulations check\n[ ] Commercial feasibility comparison\n\n" +
      "Do not write 'never invented' until evidence supports it."
    )
    liveStatus.setText("NOVELTY CHECKLIST READY")
  }
})

riskButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    ideaResult.setText(
      "ENGINEERING RISK CHECK\n\n" +
      "1. Energy source and maximum input\n2. Heat generation\n3. Flow / pressure assumptions\n" +
      "4. Electrical isolation\n5. Moving parts\n6. Materials and corrosion\n7. Failure modes\n" +
      "8. Emergency shutdown\n9. Environmental conditions\n10. Measurement uncertainty\n\n" +
      "For real hardware, involve a qualified adult / engineer for safety-critical work."
    )
    liveStatus.setText("RISK CHECK COMPLETE")
  }
})

quickIdea.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    tabs.setSelectedComponent(ideaPanel)
    generateIdeaButton.doClick()
  }
})

quickResearch.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    tabs.setSelectedComponent(researchPanel)
    researchGenerate.doClick()
  }
})

quickSky.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    tabs.setSelectedComponent(skyPanel)
  }
})

quickExport.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    saveText(
      "FutureDiscoveryCommandReport.txt",
      "FUTURE DISCOVERY LAB OMEGA REPORT\n\n" +
      "Selected module: " + selectedModule + "\n\n" +
      "LATEST IDEA\n" + lastGeneratedIdea + "\n\n" +
      "LATEST RESEARCH\n" + lastResearchPlan + "\n\n" +
      "LATEST SKY REPORT\n" + lastSkyReport + "\n\n" +
      "LATEST ASTRO REPORT\n" + lastAstroReport + "\n\n" +
      "WATER MODEL\n" + waterReport.getText + "\n"
    )
    liveStatus.setText("COMMAND REPORT SAVED")
  }
})

addBlockButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    machineList.addElement(blockBox.getSelectedItem.toString)
    machineCanvas.repaint()
    machineStatus.setText(machineList.size() + " blocks in architecture")
  }
})

removeBlockButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    val index = machineListBox.getSelectedIndex
    if (index >= 0) {
      machineList.remove(index)
      machineCanvas.repaint()
      machineStatus.setText(machineList.size() + " blocks in architecture")
    }
  }
})

autoBuildButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    machineList.clear()
    Array(
      "Air Intake", "Particle Pre-Filter", "Fine Filter", "Heat Exchanger",
      "Condenser", "Water Tank", "Sensor Hub", "Controller",
      "Safety Shutdown", "Data Logger", "Output Display"
    ).foreach(machineList.addElement)
    val blocks = new Array[String](machineList.size())
    var i = 0
    while (i < machineList.size()) {
      blocks(i) = machineList.get(i)
      i += 1
    }
    lastBlueprint = buildArchitectureText(blocks, machineName.getText)
    machineCanvas.repaint()
    machineStatus.setText("AUTO ARCHITECTURE READY ? " + machineList.size() + " blocks")
    saveText("MachineBlueprint.txt", lastBlueprint)
  }
})

loadImageButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    val chooser = new JFileChooser()
    val result = chooser.showOpenDialog(frame)
    if (result == JFileChooser.APPROVE_OPTION) {
      try {
        referenceImage = ImageIO.read(chooser.getSelectedFile)
        machineCanvas.repaint()
        imagePreview.repaint()
        if (referenceImage != null) {
          imageText.setText(
            "REFERENCE IMAGE\n\nFile: " + chooser.getSelectedFile.getName +
            "\nSize: " + referenceImage.getWidth + " x " + referenceImage.getHeight +
            "\n\nVisual reference loaded successfully."
          )
          liveStatus.setText("REFERENCE IMAGE LOADED")
        }
      } catch {
        case _: Exception => showWarn(frame, "IMAGE", "Could not read the selected image.")
      }
    }
  }
})

compareEnergyButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    val mode = energyModeBox.getSelectedItem.toString
    val report =
      "POWERTRAIN REVIEW\n\nSelected concept: " + mode + "\n\n" +
      (mode match {
        case "Battery-electric" =>
          "Battery -> inverter -> motor -> wheels.\nMeasure Wh/km, battery temperature, charging efficiency and regenerative recovery."
        case "Hydrogen fuel-cell concept" =>
          "Hydrogen storage -> fuel cell -> electricity -> motor.\nMeasure system efficiency, thermal behaviour and storage losses."
        case "Biofuel" =>
          "Fuel -> combustion -> after-treatment -> drive.\nMeasure fuel energy, emissions and maintenance."
        case "Synthetic e-fuel" =>
          "E-fuel -> combustion -> after-treatment -> drive.\nMeasure lifecycle energy use and exhaust performance."
        case "Hybrid electric" =>
          "Engine + battery + motor + control strategy.\nMeasure engine map, battery cycling and recovered braking energy."
        case "Solar-assisted electric" =>
          "Solar input -> charge system -> battery -> motor.\nMeasure harvested energy versus vehicle demand."
        case _ =>
          "Motion -> generator -> storage -> motor.\nMeasure recovered braking energy and conversion losses."
      })
    energyArea.setText(report)
  }
})

vehicleIdeaButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    val mode = energyModeBox.getSelectedItem.toString
    lastGeneratedIdea =
      "VEHICLE INVENTION CONCEPT\n\nPowertrain: " + mode + "\n\n" +
      "Modules:\n- propulsion source\n- controller\n- thermal management\n" +
      "- monitoring\n- recovery where applicable\n- safety shutdown\n- data logger\n\n" +
      "Research question:\nWhich subsystem gives the largest measurable improvement per unit cost or energy?"
    energyArea.setText(lastGeneratedIdea)
    appendNotebook(lastGeneratedIdea)
  }
})

humiditySlider.addChangeListener(_ => { humidity = humiditySlider.getValue.toDouble; refreshWaterReport() })
tempSlider.addChangeListener(_ => { ambientC = tempSlider.getValue.toDouble; refreshWaterReport() })
coilSlider.addChangeListener(_ => { coilC = coilSlider.getValue.toDouble; refreshWaterReport() })
airflowSlider.addChangeListener(_ => { airflow = airflowSlider.getValue.toDouble; refreshWaterReport() })
collectionSlider.addChangeListener(_ => { collectionEfficiency = collectionSlider.getValue.toDouble; refreshWaterReport() })

waterToggle.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    airWaterRunning = !airWaterRunning
    refreshWaterReport()
    liveStatus.setText(if (airWaterRunning) "AIR-WATER MODEL RUNNING" else "AIR-WATER MODEL PAUSED")
  }
})

waterReportSave.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    saveText("AirWaterEngineeringReport.txt", waterReport.getText)
    liveStatus.setText("AIR-WATER REPORT SAVED")
  }
})

calculateSkyButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    try {
      val y = parseIntSafe(yearField.getText, 2026)
      val m = parseIntSafe(monthField.getText, 9)
      val d = parseIntSafe(dayField.getText, 25)
      val h = parseDoubleSafe(hourField.getText, 19.5)
      val lat = parseDoubleSafe(latField.getText, 18.52)
      val lon = parseDoubleSafe(lonField.getText, 73.86)
      val target = planetBox.getSelectedItem.toString
      val jd = julianDay(y, m, d, h)
      val eclLon = planetEclipticLon(target, jd)
      val eq = eclipticToEquatorial(eclLon)
      val hor = equatorialToHorizontal(eq(0), eq(1), lat, lon, jd)
      lastSkyReport =
        "PLANET SKY DIRECTION REPORT\n\n" +
        "Target                 : " + target + "\n" +
        "Date / UTC hour       : " + y + "-" + m + "-" + d + " / " + h + "\n" +
        "Observer latitude     : " + lat + "\n" +
        "Observer longitude    : " + lon + "\n\n" +
        "Approx. ecliptic lon  : " + f"${eclLon}%.2f" + " deg\n" +
        "Approx. RA            : " + f"${eq(0)}%.2f" + " deg\n" +
        "Approx. declination   : " + f"${eq(1)}%.2f" + " deg\n" +
        "Approx. altitude      : " + f"${hor(0)}%.2f" + " deg\n" +
        "Approx. azimuth       : " + f"${hor(1)}%.2f" + " deg\n" +
        "Compass direction     : " + directionName(hor(1)) + "\n\n" +
        "Educational approximation; use a dedicated astronomy app for precision."
      skyResult.setText(lastSkyReport)
      skyView.repaint()
      liveStatus.setText("SKY DIRECTION CALCULATED")
    } catch {
      case _: Exception => showWarn(frame, "SKY LAB", "Please check date, time, latitude and longitude.")
    }
  }
})

multiPlanetButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    try {
      val y = parseIntSafe(yearField.getText, 2026)
      val m = parseIntSafe(monthField.getText, 9)
      val d = parseIntSafe(dayField.getText, 25)
      val h = parseDoubleSafe(hourField.getText, 19.5)
      val lat = parseDoubleSafe(latField.getText, 18.52)
      val lon = parseDoubleSafe(lonField.getText, 73.86)
      val jd = julianDay(y, m, d, h)
      val names = Array("Sun", "Moon", "Mercury", "Venus", "Mars", "Jupiter", "Saturn")
      val sb = new StringBuilder
      sb.append("ALL PLANET SKY SCAN\n\n")
      var i = 0
      while (i < names.length) {
        val ecl = planetEclipticLon(names(i), jd)
        val eq = eclipticToEquatorial(ecl)
        val hor = equatorialToHorizontal(eq(0), eq(1), lat, lon, jd)
        sb.append(names(i) + " | Alt " + f"${hor(0)}%.1f" + " deg | Az " + f"${hor(1)}%.1f" + " deg | " + directionName(hor(1)) + "\n")
        i += 1
      }
      lastSkyReport = sb.toString
      skyResult.setText(lastSkyReport + "\nApproximate educational output.")
      liveStatus.setText("ALL PLANETS SCANNED")
    } catch {
      case _: Exception => showWarn(frame, "SKY LAB", "Please check observer inputs.")
    }
  }
})

astroRun.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    try {
      val y = parseIntSafe(astroYear.getText, 2008)
      val m = parseIntSafe(astroMonth.getText, 1)
      val d = parseIntSafe(astroDay.getText, 1)
      val h = parseDoubleSafe(astroHour.getText, 12.0)
      val jd = julianDay(y, m, d, h)
      val names = Array("Sun","Moon","Mercury","Venus","Mars","Jupiter","Saturn")
      val sb = new StringBuilder
      sb.append("TRADITIONAL ASTROLOGY EXPLORER\n\n")
      var i = 0
      while (i < names.length) {
        val ecl = planetEclipticLon(names(i), jd)
        sb.append(names(i) + " | simplified longitude " + f"${ecl}%.2f" + " deg | sign " + signName(ecl) + "\n")
        i += 1
      }
      sb.append("\nTRADITIONAL INTERPRETATION SPACE\n")
      sb.append("Use this area to study traditional meanings from books or teachers.\n")
      sb.append("The app does not present astrology as a scientifically proven causal mechanism.\n")
      lastAstroReport = sb.toString
      astroResult.setText(lastAstroReport)
      astroCanvas.repaint()
      liveStatus.setText("TRADITIONAL CHART BUILT")
    } catch {
      case _: Exception => showWarn(frame, "ASTROLOGY", "Please check the date and time.")
    }
  }
})

researchGenerate.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    lastResearchPlan = buildResearchPlan(researchIdeaField.getText)
    researchText.setText(lastResearchPlan)
    appendNotebook("RESEARCH PLAN\n" + lastResearchPlan)
    liveStatus.setText("RESEARCH PLAN GENERATED")
  }
})

inputPowerSlider.addChangeListener(_ => refreshSimulation())
efficiencySlider.addChangeListener(_ => refreshSimulation())
runtimeSlider.addChangeListener(_ => refreshSimulation())
factorSlider.addChangeListener(_ => refreshSimulation())


openSourceButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    try {
      val raw = sourceField.getText.trim
      if (raw.startsWith("http://") || raw.startsWith("https://")) {
        java.awt.Desktop.getDesktop.browse(new java.net.URI(raw))
        liveStatus.setText("SOURCE OPENED IN BROWSER")
      } else {
        showWarn(frame, "KNOWLEDGE INTAKE", "Please enter a full http:// or https:// source.")
      }
    } catch {
      case _: Exception => showWarn(frame, "KNOWLEDGE INTAKE", "Browser could not open this source.")
    }
  }
})

saveSourceButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    val note =
      "SOURCE: " + sourceField.getText.trim + "\n" +
      "QUERY: " + knowledgeQuery.getText.trim + "\n" +
      "NOTES:\n" + knowledgeArea.getText
    appendNotebook(note)
    saveText("KnowledgeSourceNote.txt", note)
    liveStatus.setText("KNOWLEDGE NOTE SAVED")
  }
})

buildQuestionButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    val q = knowledgeQuery.getText.trim
    researchIdeaField.setText("What measurable evidence can confirm or reject this question: " + q)
    lastResearchPlan = buildResearchPlan(researchIdeaField.getText)
    researchText.setText(lastResearchPlan)
    tabs.setSelectedComponent(researchPanel)
    liveStatus.setText("RESEARCH QUESTION CREATED")
  }
})

matrixGenerate.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    val goal = matrixGoal.getText.trim
    val sb = new StringBuilder
    sb.append("DISCOVERY MATRIX\n\n")
    var i = 1
    while (i <= 12) {
      val tech = technologies(random.nextInt(technologies.length))
      val out = outputs(random.nextInt(outputs.length))
      val con = constraints(random.nextInt(constraints.length))
      val cat = categories(random.nextInt(categories.length))
      sb.append(i + ". " + cat + "\n")
      sb.append("   Goal: " + goal + "\n")
      sb.append("   Technology: " + tech + "\n")
      sb.append("   Output: " + out + "\n")
      sb.append("   Constraint: " + con + "\n\n")
      i += 1
    }
    matrixArea.setText(sb.toString)
    appendNotebook(sb.toString)
    liveStatus.setText("12-CONCEPT DISCOVERY MATRIX READY")
  }
})

roadmapButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    saveText("ProjectRoadmap.txt", roadmapArea.getText)
    liveStatus.setText("ROADMAP SAVED")
  }
})

simReset.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    inputPowerSlider.setValue(1000)
    efficiencySlider.setValue(70)
    runtimeSlider.setValue(1)
    factorSlider.setValue(100)
    refreshSimulation()
  }
})

simSave.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    saveText("PrototypeSimulation.txt", simReport.getText)
    liveStatus.setText("SIMULATION SAVED")
  }
})

noteSaveButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    appendNotebook(noteTitleField.getText + "\n\n" + noteEditor.getText)
    liveStatus.setText("NOTE SAVED TO LOCAL NOTEBOOK")
  }
})

noteExportButton.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    saveText("FutureDiscoveryNotebook_Export.txt", notebookEntries.mkString("\n\n====================\n\n"))
    liveStatus.setText("NOTEBOOK EXPORTED")
  }
})

imageOpen.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = loadImageButton.doClick()
})

imageClear.addActionListener(new ActionListener {
  override def actionPerformed(e: ActionEvent): Unit = {
    referenceImage = null
    imagePreview.repaint()
    machineCanvas.repaint()
    imageText.setText("REFERENCE IMAGE WORKBENCH\n\nNo image loaded.\n")
  }
})

tabs.addChangeListener(_ => {
  val idx = tabs.getSelectedIndex
  if (idx >= 0) {
    selectedModule = tabs.getTitleAt(idx)
    liveStatus.setText("MODULE: " + selectedModule)
  }
})

root.add(tabs, BorderLayout.CENTER)

Array(
  "Air Intake", "Particle Pre-Filter", "Fine Filter", "Heat Exchanger",
  "Condenser", "Water Tank", "Sensor Hub", "Controller",
  "Safety Shutdown", "Data Logger"
).foreach(machineList.addElement)

lastGeneratedIdea = generateInventionIdea("AUTO")
ideaResult.setText(lastGeneratedIdea)
lastResearchPlan = buildResearchPlan(ideaInput.getText)
researchText.setText(lastResearchPlan)

refreshWaterReport()
refreshSimulation()

val animationTimer = new javax.swing.Timer(
  45,
  new ActionListener {
    override def actionPerformed(e: ActionEvent): Unit = {
      animationTick = (animationTick + 1) % 1000000
      if (airWaterRunning && animationTick % 12 == 0) refreshWaterReport()
      commandCanvas.repaint()
      machineCanvas.repaint()
      skyView.repaint()
      astroCanvas.repaint()
      imagePreview.repaint()
    }
  }
)

frame.setContentPane(root)
frame.setSize(1450, 900)
frame.setLocationRelativeTo(null)
frame.setVisible(true)

SwingUtilities.invokeLater(new Runnable {
  override def run(): Unit = {
    animationTimer.start()
    ideaInput.requestFocusInWindow()
  }
})