/**
 * DAS TLW-MODELL: KOSMISCHE EXPANSION & STRUKTURBILDUNG
 * Autor: xqskys.de
 * Architektur: Normales, physikalisches Raumzeit-Modell (Kein Fraktal)
 * Optimiert für den Processing 4.5.6 Java-Editor (Linux-x64)
 */

// --- KOSMISCHE PARAMETER ---
float zoom = 1.0f;
float offsetX = 0.0f; 
float offsetY = 0.0f;

// Physikalische Konstanten des TLW-Modells
final float PHI_TLW  = 0.381966f; 
final float GAMMA    = 1.618033f; 
final float LAMBDA   = 0.133700f; 

float timeScale = 0.001f;
boolean dynamicTime = false; 
boolean isPaused = false;
float fixedTime = 0.001f;

// Variable für den globalen Startzustand
boolean simulationStarted = false;

// Interaktions-Variablen fuer freies Navigieren (Pan)
boolean isDragging = false;
float lastMouseX, lastMouseY;

// Variablen für die Zeitleiste (Timeline Slider)
float sliderX = 50.0f;
float sliderY = 715.0f;
float sliderW = 924.0f;
float sliderH = 10.0f;
float sliderPos = 0.0f; 
boolean isSliderDragging = false;

// Event-Management & Textbeschreibungen
int currentEvent = 0;
String epocheName = "DIE SINGULARITÄT";
String physikDaten = "T = 0 | Dichte: oo | Entropie: S -> 0";
String leschErklaerung = "Sehen Sie sich das an: Ein Punkt, unendlich klein, unendlich dicht. Die gesamte Raumzeit, all Ihre Atome, zusammengepresst im absoluten Nichts vor dem ersten Atemzug des Kosmos.";

// Array für physikalische Materie-Zentren (Galaxien-Filamente)
int numGalaxies = 180;
float[] galX = new float[numGalaxies];
float[] galY = new float[numGalaxies];
float[] galMass = new float[numGalaxies];
float[] galAngle = new float[numGalaxies];
float[] galRadius = new float[numGalaxies];

// Button-Koordinaten im HUD
float btnX = 45.0f;
float btnY = 222.0f;
float btnW = 180.0f;
float btnH = 28.0f;

// Start-Button Koordinaten
float startBtnX;
float startBtnY;
float startBtnW = 280.0f;
float startBtnH = 50.0f;

void setup() {
  size(1024, 768);
  windowTitle("TLW-Modell: Die Physik der Wirklichkeit");
  frameRate(30);
  
  startBtnX = ((float)width / 2.0f) - (startBtnW / 2.0f);
  startBtnY = ((float)height / 2.0f) - (startBtnH / 2.0f) + 180.0f; 
  
  randomSeed(42); 
  for (int i = 0; i < numGalaxies; i++) {
    galAngle[i] = random(TWO_PI);
    galRadius[i] = pow(random(1.0f), 1.5f) * 380.0f; 
    galMass[i] = random(1.0f, 5.0f);
    
    galX[i] = cos(galAngle[i]) * galRadius[i];
    galY[i] = sin(galAngle[i]) * galRadius[i];
  }
  
  updateTimeFromSlider();
}

void draw() {
  background(2, 2, 8); 
  
  if (simulationStarted && !isPaused) {
    if (dynamicTime) {
      timeScale = map(mouseX, 0, width, 0.001f, 3.5f);
      float currentProgress = (timeScale - 0.001f) / (3.5f - 0.001f);
      sliderPos = sqrt(currentProgress); 
    } else {
      timeScale = fixedTime;
    }
  } else if (!simulationStarted) {
    timeScale = 0.001f; 
  }
  
  float expansionFactor = (timeScale * GAMMA) + (log(timeScale + 1.0f) * PHI_TLW) + (pow(timeScale, 2) * LAMBDA);
  if (expansionFactor < 0.01f) expansionFactor = 0.01f;

  pushMatrix();
  translate((float)width / 2.0f + offsetX * zoom, (float)height / 2.0f + offsetY * zoom);
  scale(zoom);
  
  stroke(0, 255, 200, 15);
  strokeWeight(1.0f / zoom);
  noFill();
  int gridCircles = 8;
  for (int i = 1; i <= gridCircles; i++) {
    ellipse(0, 0, i * 120.0f * expansionFactor, i * 120.0f * expansionFactor);
  }
  
  for (int i = 0; i < numGalaxies; i++) {
    float currentX = galX[i] * expansionFactor;
    float currentY = galY[i] * expansionFactor;
    
    float waveEffect = cos(galRadius[i] * 0.05f - timeScale * 2.0f) * 12.0f * (timeScale / 3.5f);
    currentX += cos(galAngle[i]) * waveEffect;
    currentY += sin(galAngle[i]) * waveEffect;
    
    if (timeScale > 0.5f) {
      stroke(0, 150, 255, map(timeScale, 0.5f, 3.5f, 0, 35));
      strokeWeight(0.8f / zoom);
      for (int j = i + 1; j < numGalaxies; j += 15) {
        float nextX = galX[j] * expansionFactor + cos(galAngle[j]) * waveEffect;
        float nextY = galY[j] * expansionFactor + sin(galAngle[j]) * waveEffect;
        if (dist(currentX, currentY, nextX, nextY) < 180.0f * expansionFactor) {
          line(currentX, currentY, nextX, nextY);
        }
      }
    }
    
    float sizeFactor = galMass[i] * map(timeScale, 0.001f, 3.5f, 1.0f, 2.5f);
    noStroke();
    
    if (timeScale < 0.2f) {
      fill(255, 200, 100, 220); 
    } else if (timeScale < 1.5f) {
      fill(0, 255, 230, 200); 
    } else {
      fill(255, 100 + galMass[i]*20, 150, 180); 
    }
    
    ellipse(currentX, currentY, sizeFactor, sizeFactor);
    
    if (galMass[i] > 4.2f) {
      fill(0, 255, 200, 30);
      ellipse(currentX, currentY, sizeFactor * 3.0f, sizeFactor * 3.0f);
    }
  }
  popMatrix();
  
  if (simulationStarted) {
    drawLeschHUD();
    drawTimelineSlider();
  } else {
    drawWelcomeScreen();
  }
}

void drawWelcomeScreen() {
  noStroke();
  fill(0, 0, 0, 160);
  rect(0, 0, width, height);
  
  textAlign(CENTER);
  fill(0, 255, 200);
  textSize(28);
  text("TLW-MODELL // REALE KOSMOLOGIE", (float)width / 2.0f, (float)height / 2.0f - 60.0f);
  
  fill(220, 230, 255, 180);
  textSize(13);
  text("Physikalische Simulation der Raum-Expansion und Filament-Bildung", (float)width / 2.0f, (float)height / 2.0f - 30.0f);
  
  boolean mouseOverStart = (mouseX >= startBtnX && mouseX <= startBtnX + startBtnW && mouseY >= startBtnY && mouseY <= startBtnY + startBtnH);
  
  if (mouseOverStart) {
    fill(0, 255, 200, 60);
    stroke(0, 255, 200, 240);
    strokeWeight(2);
  } else {
    fill(5, 20, 35, 200);
    stroke(0, 255, 200, 100);
    strokeWeight(1);
  }
  
  rect(startBtnX, startBtnY, startBtnW, startBtnH, 8);
  
  fill(255);
  textSize(14);
  textAlign(CENTER, CENTER);
  text("SIMULATION STARTEN", startBtnX + startBtnW / 2.0f, startBtnY + startBtnH / 2.0f - 1.0f);
  
  textAlign(LEFT, BASELINE);
}

void drawLeschHUD() {
  noStroke();
  fill(5, 12, 22, 210);
  rect(25, 25, 460, 245, 12);
  stroke(0, 255, 230, 80);
  strokeWeight(1);
  noFill();
  rect(25, 25, 460, 245, 12); 
  
  fill(0, 255, 200);
  textSize(13);
  text("TLW-COSMOLOGICAL PHYSICAL ENGINE // VER: 4.5.6", 45, 52);
  
  if (isPaused) {
    fill(255, 50, 50);
    text("STATUS: GEPAUST", 335, 52);
  }
  
  stroke(0, 255, 200, 40);
  line(45, 62, 465, 62);
  
  fill(180, 210, 255);
  textSize(11);
  text("METRISCHER EXPANSIONS-FAKTOR R(t):", 45, 80);
  fill(255);
  float currentR = (timeScale * GAMMA) + (log(timeScale + 1.0f) * PHI_TLW) + (pow(timeScale, 2) * LAMBDA);
  text(nf(currentR, 1, 4) + " M-Einheiten | Zoom: " + nf(zoom, 1, 2) + "x", 265, 80);
  
  fill(180, 210, 255);
  text("KOSMOLOGISCHE VA-ENERGIE (A):", 45, 98);
  fill(255);
  text(nf(LAMBDA, 1, 6) + " eV  |  t: " + nf(timeScale, 1, 4), 265, 98);
  
  fill(180, 210, 255);
  text("PHYSIKALISCHER STATUS:", 45, 116);
  fill(255, 120, 120);
  text(physikDaten, 265, 116);
  
  stroke(255, 230, 100, 30);
  line(45, 130, 465, 130);
  
  fill(255, 225, 100);
  textSize(14);
  text("Epoche: " + epocheName, 45, 150);
  
  fill(235, 240, 250);
  textSize(11);
  text(leschErklaerung, 45, 168, 420, 55);
  
  boolean mouseOverBtn = (mouseX >= btnX && mouseX <= btnX + btnW && mouseY >= btnY && mouseY <= btnY + btnH);
  if (mouseOverBtn) {
    fill(0, 255, 200, 40);
    stroke(0, 255, 200, 180);
  } else {
    fill(0, 255, 200, 15);
    stroke(0, 255, 200, 80);
  }
  rect(btnX, btnY, btnW, btnH, 6);
  
  fill(0, 255, 200);
  textSize(11);
  textAlign(CENTER, CENTER);
  text("FREIE MAUS-ZEITREISE", btnX + btnW/2.0f, btnY + btnH/2.0f - 1.0f);
  textAlign(LEFT, BASELINE); 
  
  fill(150, 180, 225);
  if (dynamicTime) {
    text("Aktiv: Maus-X steuert Evolution", btnX + btnW + 15.0f, btnY + btnH/2.0f + 4.0f);
  } else {
    text("Aktiv: Zeitleiste unten steuert", btnX + btnW + 15.0f, btnY + btnH/2.0f + 4.0f);
  }
  
  fill(0, 255, 200, 120);
  text("SIM-FPS: " + int(frameRate), width - 120, 45);
}

void drawTimelineSlider() {
  noStroke();
  fill(5, 12, 22, 190);
  rect(sliderX - 15, sliderY - 25, sliderW + 30, sliderH + 45, 8);
  stroke(0, 255, 200, 50);
  noFill();
  rect(sliderX - 15, sliderY - 25, sliderW + 30, sliderH + 45, 8);
  
  fill(0, 255, 200, 180);
  textSize(10);
  text("URKNALL (T=0)", sliderX, sliderY - 10);
  textAlign(CENTER);
  text("ELEMENT-BILDUNG", sliderX + sliderW * 0.35f, sliderY - 10);
  text("STERNEN-ZÜNDUNG", sliderX + sliderW * 0.68f, sliderY - 10);
  textAlign(RIGHT);
  text("GEGENWART (13.8 Mrd. J.)", sliderX + sliderW, sliderY - 10);
  textAlign(LEFT); 
  
  stroke(255, 255, 255, 40);
  strokeWeight(2);
  line(sliderX, sliderY + sliderH/2, sliderX + sliderW, sliderY + sliderH/2);
  
  stroke(0, 255, 200, 120);
  strokeWeight(4);
  point(sliderX, sliderY + sliderH/2);
  point(sliderX + sliderW * 0.35f, sliderY + sliderH/2);
  point(sliderX + sliderW * 0.68f, sliderY + sliderH/2);
  point(sliderX + sliderW, sliderY + sliderH/2);
  
  float knobX = sliderX + sliderPos * sliderW;
  noStroke();
  if (isSliderDragging || (mouseX >= knobX - 10 && mouseX <= knobX + 10 && mouseY >= sliderY - 5 && mouseY <= sliderY + sliderH + 5)) {
    fill(0, 255, 200);
    ellipse(knobX, sliderY + sliderH/2, 16, 16);
    stroke(0, 255, 200, 80);
    strokeWeight(2);
    noFill();
    ellipse(knobX, sliderY + sliderH/2, 24, 24);
  } else {
    fill(255, 230, 100);
    ellipse(knobX, sliderY + sliderH/2, 12, 12);
  }
  
  fill(255, 255, 255, 90);
  textSize(11);
  text("UNTEN: Zeitleiste ziehen | TASTE P: Pause | MAUSRAD: Zoom im Raum | MAUS ZIEHEN IM RAUM: Ausschnitt verschieben", 45, height - 42);
}

void updateTimeFromSlider() {
  float nonLinearFactor = sliderPos * sliderPos; 
  fixedTime = map(nonLinearFactor, 0.0f, 1.0f, 0.001f, 3.5f);
  
  if (sliderPos < 0.12f) {
    currentEvent = 1;
    epocheName = "KOSMISCHE INFLATION (Urknall)";
    physikDaten = "T + 10^-36 Sekunden | Entkopplung";
    leschErklaerung = "Sehen Sie sich das an: Der Raum expandiert explosionsartig aus einem winzigen Punkt. Die Dichte sinkt rasant, die fundamentale Ur-Symmetrie der Kräfte bricht auf.";
} else if (sliderPos >= 0.12f && sliderPos < 0.52f) {currentEvent = 2;epocheName = "NUKLEOSYNTHESE & ELEMENTBILDUNG";physikDaten = "T + 3 Minuten bis 380.000 Jahre";leschErklaerung = "Das Universum kühlt ab. Erste Protonen und Neutronen verschmelzen zu Wasserstoff und Helium. Das Licht ist noch im heißen Plasma gefangen, bevor der Raum durchsichtig wird.";} else if (sliderPos >= 0.52f && sliderPos < 0.88f) {currentEvent = 3;epocheName = "DAS DUNKE ZEITALTER & STERNENZÜNDUNG";physikDaten = "T + 400 Millionen Jahre | Gravitations-Kollaps";leschErklaerung = "Die Gravitation gewinnt! Erste riesige Gaswolken stürzen in sich zusammen. In den Zentren verdichtet sich die Materie so extrem, bis die ersten gigantischen Sonnen zünden. Das kosmische Netz erwacht.";} else {currentEvent = 4;epocheName = "DIE KOSMISCHE GEGENWART";physikDaten = "T + 13,8 Mrd. Jahre | Flvessige Balance";leschErklaerung = "Heute. Die Expansion hat die Galaxienhaufen weit auseinandergetrieben, während sie im Inneren über Gravitations-Filamente stabil gebunden bleiben. Ein dynamisches, perfekt ausbalanciertes System.";}}void mouseWheel(MouseEvent event) {if (!simulationStarted) return;float e = event.getCount();if (e < 0) {zoom *= 1.15f;} else {zoom /= 1.15f;}if (zoom < 0.3f) zoom = 0.3f;if (zoom > 30.0f) zoom = 30.0f;}void keyPressed() {if (!simulationStarted) return;if (key == 'p' || key == 'P') {isPaused = !isPaused;}}void mousePressed() {if (!simulationStarted) {if (mouseX >= startBtnX && mouseX <= startBtnX + startBtnW && mouseY >= startBtnY && mouseY <= startBtnY + startBtnH) {simulationStarted = true;}return;}if (mouseX >= sliderX && mouseX <= sliderX + sliderW && mouseY >= sliderY - 10 && mouseY <= sliderY + sliderH + 15) {isSliderDragging = true;dynamicTime = false;sliderPos = constrain((mouseX - sliderX) / sliderW, 0.0f, 1.0f);updateTimeFromSlider();return;}if (mouseX >= btnX && mouseX <= btnX + btnW && mouseY >= btnY && mouseY <= btnY + btnH) {dynamicTime = true;currentEvent = 0;epocheName = "DYNAMISCHER ZEITFLUSS (Interaktiv)";physikDaten = "T = variabel | Zeit-Dimension geöffnet";leschErklaerung = "Führen Sie jetzt selbst Regie über das Universum. Bewegen Sie Ihre Maus von links nach rechts, um Äonen vergehen und Galaxien kollabieren zu lassen. Unendliche Spielwiese!";return;}if (mouseX > 25 && mouseX < 485 && mouseY > 25 && mouseY < 270) {return;}isDragging = true;lastMouseX = (float)mouseX;lastMouseY = (float)mouseY;}void mouseReleased() {isSliderDragging = false;isDragging = false;}void mouseDragged() {if (!simulationStarted) return;if (isSliderDragging) {sliderPos = constrain((mouseX - sliderX) / sliderW, 0.0f, 1.0f);updateTimeFromSlider();return;}if (isDragging) {float deltaX = ((float)mouseX - lastMouseX) / zoom;float deltaY = ((float)mouseY - lastMouseY) / zoom;offsetX += deltaX;offsetY += deltaY;lastMouseX = (float)mouseX;lastMouseY = (float)mouseY;}}
