import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.geometry.Pos;
import javafx.geometry.Insets;
import javafx.scene.CacheHint;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.Stream;

import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import java.io.File;

/**
 * Solar Arcadia3D
 * 太陽を中心に太陽系を眺めてみるようにしてみました。
 *
 * われは光を司るものであり、全てを治めるものである。
 * 従うものの軌跡を眺めてみよ。
 *
 * @author きん ＆ Gemini
 */
public class SolarArcadia3D extends Application {

	// --- ウィンドウおよび表示設定 ---
	public static int WIDTH = 1024;
	public static int HEIGHT = 768;

	// ダイナミック・オービット
	private static double ORBIT = 40;
	private static double ORBIT_TIME = 0.001;

	// --- レイヤー構造管理フィールド ---
	private StackPane paneMain = null;	  // 基盤となるスタックペイン（全レイヤーを重ねる）
	private SubScene subScene3D = null;	 // 3Dレンダリング専用（深度テスト、アンチエイリアス有効）
	private SubScene subScene2D = null;	 // 2Dラベル・UI専用（背景透過）
	private Group pane3D = null;			// 3D空間のルートノード
	private javafx.scene.layout.Pane pane2D = null; // 2Dラベルを配置する描画パネル

	// --- カメラおよび宇宙全体の回転制御 ---
	private PerspectiveCamera camera;
	private Rotate worldRotateX = new Rotate(0, Rotate.X_AXIS); // 宇宙全体の俯瞰（ピッチ）操作用
	private Rotate worldRotateY = new Rotate(0, Rotate.Y_AXIS); // 宇宙全体の水平回転（ヨー）操作用
	private double camDist = 0;	  // カメラの原点（太陽）からの距離。ズーム操作の基準値
	private double currentFOV = 75.0; // 視野角
	private double currentRotX = 0;	// 初期の俯瞰角度。少し斜め上から見下ろす設定
	private double currentRotY = 0;	 // 初期の水平回転角度
	private double timeScale = 1.0; // 速度倍率（デフォルトは 1.0）

	private boolean dynamicOrbitEnabled = false; // ダイナミック・オービット
	private double orbitTime = 0.41;

	// --- HUD（操作ガイド）用テキストオブジェクト ---
	private VBox keyGuide = null;
	private Text textTitle = new Text();
	private Text textMove = new Text();
	private Text textZoom = new Text();
	private Text textSpeedInfo = new Text();
	private Text textLabel = new Text();
	private Text textDynamicOrbit = new Text();
	private Text textHUDHidden = new Text();
	private Text textOrbitHidden = new Text();
	private Text textOrbitPlanet = new Text();
	private Text textOrbitElliptic = new Text();
	private Text textOrbitComet = new Text();
	private Text textLang = new Text();

	// --- 天体群および環境エフェクト管理 ---
	private List<CelestialBody> bodies = new ArrayList<>(); // 生成した全天体を一括管理するリスト
	private boolean showLabels = true;					  // スペースキー連動：天体名表示フラグ
	private boolean showOrbits = false;					   // 0キー連動：全軌道の表示・非表示フラグ
	private String lang = "ja";							  // 言語管理：初期値は日本語
	private OortCloud oortCloud = null;					  // 太陽系を包む最外縁の微細天体群
	private SpaceWind spaceWind = null;					  // 宇宙の広がりを演出する星間物質の流れ
	private MilkyWayImage milkyWayImage = null;			  // 全天を包み込む銀河系背景画像

	String[] musicFileNames = {
		"Music_fx_divine_celestial_choir_bright_shimm_L.wav",
		"Music_fx_divine_celestial_choir_bright_shimm_R.wav",
		"Music_fx_ethereal_space_ambient_deep_rhythmi_L.wav",
		"Music_fx_ethereal_space_ambient_deep_rhythmi_R.wav"
	};
	private MediaPlayer[] backgroundMusics = new MediaPlayer[musicFileNames.length];

	@Override
	public void start(Stage stage) {

		// 1. 描画レイヤーとHUD、カメラの物理構築
		setupScene(stage);

		// 2. 太陽系天体群（恒星、惑星、小惑星、彗星、カイパーベルト）の生成と配置
		createUniverse();

		// 小惑星と彗星の軌道を表示
		toggleOrbitByCategory(CelestialBody.Category.ELLIPTICAL);
		toggleOrbitByCategory(CelestialBody.Category.COMET);

		// 3. キーボード入力イベントの登録
		stage.getScene().addEventHandler(KeyEvent.KEY_PRESSED, event -> {

			switch (event.getCode()) {

				// ラベル表示のトグル切り替え
				case SPACE -> { 
					showLabels = !showLabels; 
					bodies.forEach(b -> b.setLabelVisible(showLabels)); 
				}

				// カテゴリ別の軌道表示制御（123キーによる動的な解説演出用）
				case DIGIT1, NUMPAD1 -> toggleOrbitByCategory(CelestialBody.Category.PLANET);
				case DIGIT2, NUMPAD2 -> toggleOrbitByCategory(CelestialBody.Category.ELLIPTICAL);
				case DIGIT3, NUMPAD3 -> toggleOrbitByCategory(CelestialBody.Category.COMET);

				// 全軌道の表示リセット（撮影開始時のクリーンアップ用）
				case DIGIT0, NUMPAD0 -> {
					showOrbits = !showOrbits;
					bodies.forEach(b -> b.setOrbitVisible(showOrbits));
				}

				// カメラ制御：上下左右で宇宙を回転
				case UP	  -> currentRotX -= 2;
				case DOWN	-> currentRotX += 2;
				case LEFT	-> currentRotY -= 2;
				case RIGHT   -> currentRotY += 2;

				// カメラ制御：ズームイン・アウト（極端な値にならないよう制限）
				case PAGE_UP -> {
					currentFOV = Math.max(5, currentFOV - 1); // 望遠側：視野を狭める
					camera.setFieldOfView(currentFOV);
					applyZoom();
				}

				case PAGE_DOWN -> {
					currentFOV = Math.min(160, currentFOV + 1); // 広角側：視野を広げる
					camera.setFieldOfView(currentFOV);
					applyZoom();
				}

				// 言語切り替え：日英ガイドの動的置換
				case L	   -> this.changeLang();

				// HUDの表示・非表示
				case H	   -> keyGuide.setVisible(!keyGuide.isVisible());

				// スピードアップ
				case A  -> {
					timeScale *= 1.2; // 20%ずつ加速
					if (timeScale > 50.0) timeScale = 50.0; // 上限
					applyTimeScale();
				}

				// スピードダウン
				case Z  -> {
					timeScale /= 1.2; // 20%ずつ減速
					if (timeScale < 0.01) timeScale = 0.01; // 下限（停止回避）
					applyTimeScale();
				}

				// ダイナミック・オービット
				case D -> {
					dynamicOrbitEnabled = !dynamicOrbitEnabled;
				}

			} // end switch

		}); // end KEY_PRESSED

		// 4. メイン・アップデートループの起動（AnimationTimerは画面リフレッシュに同期）
		new AnimationTimer() {
			@Override
			public void handle(long now) { updateUniverse(); }
		}.start();

		// タイトル
		stage.setTitle("Solar Arcadia3D 3D");
		// 画面を表示
		stage.show();

		// ループ
		for(String musicFileName: musicFileNames){

			try {

				String path = null;
				int i = 0;

				// 1. まずはリソース（JAR内部など）として探す
				var resource = getClass().getResource(musicFileName);
				if (resource != null) {

					path = resource.toExternalForm();

				// 2. リソースで見つからない場合は、実行ディレクトリのファイルを探す（exe化後など）
				} else {

					File musicFile = new File(musicFileName);
					if (musicFile.exists()) {
						path = musicFile.toURI().toString();
					}

				}

				if (path != null) {

					i++;

					// プレーヤー作成
					Media media = new Media(path);
					MediaPlayer backgroundMusic = new MediaPlayer(media);
					backgroundMusics[i] = backgroundMusic;

					// 無限ループ設定
					backgroundMusic.setCycleCount(MediaPlayer.INDEFINITE);
					// 「静寂」を壊さないよう、ボリュームは控えめに（0.1〜0.3推奨）
					backgroundMusic.setVolume(0.1 + Math.random() * 0.1); 
					// ゆらぎを作る
					backgroundMusic.setRate(0.4 + Math.random() * 0.4); 
					// プレイ
					backgroundMusic.play();

				} else {
					System.err.println("音楽ファイルが見つかりません: " + musicFileName);
				}

			} catch (Exception e) {
				System.err.println("BGMの読み込みに失敗しました: " + e.getMessage());
			}

		} // end for

	} // end start

	/**
	 * setupScene: 3D宇宙と2Dラベル、HUDのレイヤー構造を物理的に構築。
	 * * 【重要】3D SubSceneの上に透過の2D SubSceneを重ねることで、
	 * 3D空間の幾何学的な歪みに左右されない正確なラベル表示とUIを実現しています。
	 */
	private void setupScene(Stage stage) {

		// 全レイヤーを重ねるコンテナ
		this.paneMain = new StackPane();
		this.pane3D = new Group();

		// 宇宙全体の回転軸を定義：Y軸回転（水平）を先に適用することで操作性を向上
		pane3D.getTransforms().setAll(worldRotateY, worldRotateX);

		// 3D描画領域の設定：深度バッファとアンチエイリアスを有効にして高品質な出力を目指す
		this.subScene3D = new SubScene(pane3D, WIDTH, HEIGHT, true, SceneAntialiasing.BALANCED);
		subScene3D.setFill(Color.BLACK);

		// ウィンドウのリサイズにSubSceneを追従させるバインド設定
		subScene3D.widthProperty().bind(paneMain.widthProperty());
		subScene3D.heightProperty().bind(paneMain.heightProperty());

		// 2Dラベル描画用パネルの構築
		this.pane2D = new javafx.scene.layout.Pane();
		this.pane2D.prefWidthProperty().bind(paneMain.widthProperty());
		this.pane2D.prefHeightProperty().bind(paneMain.heightProperty());

		// 2Dオーバーレイサブシーン：背景を透明(TRANSPARENT)にして下の3Dを見せる
		this.subScene2D = new SubScene(pane2D, WIDTH, HEIGHT);
		subScene2D.setFill(Color.TRANSPARENT);
		subScene2D.widthProperty().bind(paneMain.widthProperty());
		subScene2D.heightProperty().bind(paneMain.heightProperty());

		// HUD（操作ガイド）VBoxの構築：画面左上に配置される計器のようなパネル
		keyGuide = new VBox();
		keyGuide.setPadding(new Insets(20));
		keyGuide.setMouseTransparent(true); // マウス入力を透過させ、下の3D空間の回転ドラッグを邪魔しない
		keyGuide.setAlignment(Pos.TOP_LEFT);
		keyGuide.setCache(true);			// 描画キャッシュによりパフォーマンスを維持
		keyGuide.setCacheHint(CacheHint.QUALITY);

		// サイバー感のある計器風フォントスタイル
		String textStyle = "-fx-fill: #C0C0C0; -fx-font-family: 'Courier New'; -fx-font-size: 11px; -fx-font-weight: bold;";
		textTitle.setStyle(textStyle + "-fx-fill: #00FF00;");
		textTitle.setText("--- CONTROLS ---");

		// 全テキストオブジェクトに一括でスタイルを適用
		Stream.of(textLang, textMove, textZoom, textLabel, textDynamicOrbit, textHUDHidden,
				  textOrbitHidden, textOrbitPlanet, textOrbitElliptic, textOrbitComet,
				  textSpeedInfo).forEach(t -> t.setStyle(textStyle));

		// 初期言語セットアップ
		this.lang = "en"; // 初回のchangeLang呼び出しでjaになるようにセット
		this.changeLang();

		// HUDへの要素追加
		keyGuide.getChildren().addAll(textTitle, textLang, textMove, textZoom,
									  textLabel, textDynamicOrbit, textHUDHidden,
									  textOrbitHidden, textOrbitPlanet, textOrbitElliptic,
									   textOrbitComet, textSpeedInfo);

		// レイヤーの重畳：下から 3D SubScene -> 2D SubScene -> HUDガイド
		paneMain.getChildren().addAll(subScene3D, subScene2D, keyGuide);
		StackPane.setAlignment(keyGuide, Pos.TOP_LEFT);

		// カメラ設定：ニア・ファークリッピングを極限まで広げ、巨大な銀河背景まで描画
		camera = new PerspectiveCamera(true);
		camera.setNearClip(0.01);
		camera.setFarClip(5000000.0);
		camera.setTranslateZ(-camDist);

		// Ｙ座標初期位置設定
		double yOffset = -Math.sin(orbitTime) * ORBIT;
		camera.setTranslateY(yOffset);

		// 広角に設定（60度）
		camera.setFieldOfView(currentFOV);
		subScene3D.setCamera(camera);

		// シーンを構築しステージへセット
		Scene scene = new Scene(paneMain, WIDTH, HEIGHT, true, SceneAntialiasing.BALANCED);
		scene.setFill(Color.BLACK);
		stage.setScene(scene);

	} // end setupScene

	/**
	 * createUniverse: 太陽系の物理的初期配置と環境エフェクトの生成
	 * 主要惑星から、カイパーベルト、ラグランジュ点、背景銀河までを統合します。
	 */
	private void createUniverse() {

		// 正規分布用
		Random random = new Random();

		// 1. 光源天体（太陽）とシーン照明
		SunBody sun = new SunBody("", "", 15.0, "image/2k_sun.jpg", Color.ORANGE);
		addBody(sun);
		pane3D.getChildren().add(new PointLight(Color.WHITE)); 
		pane3D.getChildren().add(new AmbientLight(Color.rgb(50, 50, 50)));

		// 2. 内惑星（円軌道モデル）
		addBody(new PlanetBody("水星", "Mercury", 60,  0.32,  3.0, 0.01, "image/2k_mercury.jpg", Color.GRAY));
		addBody(new PlanetBody("金星", "Venus", 90,  0.24,  5.0, 177.3, "image/2k_venus_atmosphere.jpg", Color.GOLD));
		addBody(new PlanetBody("地球", "Earth", 130, 0.2,  5.5, 23.4, "image/2k_earth_daymap.jpg", Color.SKYBLUE));
		addBody(new PlanetBody("火星", "Mars", 180, 0.16,  4.5, 25.2, "image/2k_mars.jpg", Color.ORANGERED));

		// 2.1 外惑星：木星およびトロヤ群のラグランジュ点(L4, L5)のシミュレーション
		// トロヤ群と変数を共有するものは外出ししている
		double jupiterDist = 300.0;
		double jupiterSpeed = 0.1;
		double jupiterRadius = 12.0;
		double jupiterTilt = 3.1;
		double jupiterAngle = 12.0;
		PlanetBody planetJupiter = new PlanetBody("木星", "Jupiter", jupiterDist, jupiterSpeed, jupiterRadius, jupiterTilt, "image/2k_jupiter.jpg", Color.WHEAT, jupiterAngle);
		planetJupiter.addPlanetRing(1.2, 1.5, null, 0.000005);
		addBody(planetJupiter);

		// 木星トロヤ群（各200個ずつ配置）
		double[] lagrangeOffsets = {60.0, -60.0};
		for (double offset : lagrangeOffsets) {
			for (int i = 0; i < 200; i++) {
				double d = jupiterDist + (random.nextGaussian() * 6.0 - 3.0); 
				double s = jupiterSpeed; 
				double r = Math.random() * 0.2 + 0.1;
				double ang = jupiterAngle + offset + (random.nextGaussian() * 8.0 - 4.0);
				addBody(new PlanetBody("", "", d, s, r, 0, null, Color.DARKGRAY, ang));
			}
		}

		// 2.2 外惑星：土星（地軸傾斜26.7度の再現）
		PlanetBody saturn = new PlanetBody("土星", "Saturn", 420, 0.06, 10.0, 26.7, "image/Solarsystemscope_texture_2k_saturn.jpg", Color.KHAKI);
		saturn.addPlanetRing(1.1, 2.3, "image/2k_saturn_ring_alpha.png", 0.1);
		addBody(saturn);

		// 2.3 外惑星：天王星（極端な地軸倒れ）
		PlanetBody uranus = new PlanetBody("天王星", "Uranus", 510, 0.04, 8.0, 97.8, null, Color.LIGHTBLUE);
		uranus.addPlanetRing(1.3, 2.0, null, 0.000015);
		addBody(uranus);

		// 2.4 外惑星：海王星
		PlanetBody neptune = new PlanetBody("海王星", "Neptune", 580, 0.03, 7.8, 28.3, null, Color.CORNFLOWERBLUE);
		neptune.addPlanetRing(1.2, 1.6, null, 0.00002);
		addBody(neptune);

		// 3. 小惑星帯 (Main Belt)：正規分布密度 (300個)
		for (int i = 0; i < 300; i++) {
			double d = 225 + random.nextGaussian() * 10;
			addBody(new PlanetBody("", "", d, 0.1 + Math.random() * 0.005, 0.8, 0.0, null, Color.GRAY));
		}
		addBody(new PlanetBody("ケレス", "Ceres",  235.0, 0.134, 1.5, 0.0, null, Color.SILVER));
		addBody(new EllipticalBody("パラス", "Pallas", 240, 230, 0.106, 10, 0, 34.8, 1.2, null, Color.rgb(180, 180, 200)));

		// 4. 外縁天体（楕円・傾斜カテゴリ）
		addBody(new EllipticalBody("冥王星", "Pluto", 600, 550, 0.02, 100, 0, -17, 3.0, null, Color.LIGHTGRAY));
		addBody(new EllipticalBody("ハウメア", "Haumea", 640, 580, 0.018, 120, 200, -28, 2.5, null, Color.WHITE));
		addBody(new EllipticalBody("マケマケ", "Makemake", 680, 650, 0.016, 80, 45, -29, 2.6, null, Color.TOMATO));
		addBody(new EllipticalBody("エリス", "Eris", 800, 600, 0.014, 200, 150, -44, 2.8, null, Color.LIGHTYELLOW));
		addBody(new EllipticalBody("クワオアー", "Quaoar", 720, 700, 0.012, 50, 20, -8, 2.4, null, Color.CHOCOLATE));
		addBody(new EllipticalBody("オルクス", "Orcus", 610, 560, 0.02, 110, 180, -20, 2.3, null, Color.DARKGRAY));

		// 追加した外縁天体（楕円・傾斜カテゴリ）
		// 1. ヴァルナ (Varuna) - 準惑星候補。かつてはもっと大きく見積もられていた。
		addBody(new EllipticalBody("ヴァルナ", "Varuna", 630, 600, 0.015, 70, 10, -17.2, 2.2, null, Color.SIENNA));
		// 2. イクシオン (Ixion) - 冥王星と同じく海王星と共鳴関係にある「プルティーノ」。
		addBody(new EllipticalBody("イクシオン", "Ixion", 620, 570, 0.019, 90, 80, -19.6, 2.1, null, Color.INDIANRED));
		// 3. ゴングゴング (Gonggong / 共工) - 赤みの強い巨大天体。非常に楕円が深い。
		addBody(new EllipticalBody("共工", "Gonggong", 1100, 500, 0.011, 240, 120, -30.7, 2.7, null, Color.FIREBRICK));
		// 4. 2002 MS4 - 準惑星候補。静かに外縁部に佇む黒っぽい天体。
		addBody(new EllipticalBody("2002 MS4", "2002 MS4", 650, 640, 0.013, 30, 210, -17.7, 2.0, null, Color.DARKSLATEGRAY));
		// 5. サラキア (Salacia) - 軌道は比較的大人しいが、低密度で興味深い天体。
		addBody(new EllipticalBody("サラキア", "Salacia", 620, 590, 0.017, 100, 300, -23.9, 2.2, null, Color.ROSYBROWN));
		// 6. ヴィシュヌ (Vishnu / 2004 EW95) - カイパーベルトにありながら、炭素質という異端児。
		addBody(new EllipticalBody("ヴィシュヌ", "Vishnu", 600, 400, 0.022, 180, 50, -29.3, 1.5, null, Color.DARKBLUE));

		// 5. 彗星群（近日点加速モデル）
		addBody(new CometBody("ハレー彗星", "Halley", 700, 200, 0.15, 500, 45, 18, 1.2, Color.LIGHTBLUE));
		addBody(new CometBody("池谷・関彗星", "Ikeya-Seki", 900, 100, 0.1, 800, 30, 140, 1.5, Color.ALICEBLUE));
		addBody(new CometBody("６７Ｐ彗星", "67P/C-G", 500, 300, 0.1, 200, 180, 7, 1.4, Color.LIGHTGREY));
		addBody(new CometBody("西村彗星", "Nishimura", 600, 100, 0.125, 580, 10, 132, 1.1, Color.SPRINGGREEN));
		addBody(new CometBody("ＳＬ９彗星", "Shoemaker-Levy 9", 320, 150, 0.1, 100, 10, 5, 0.8, Color.MEDIUMPURPLE));
		addBody(new CometBody("百武彗星", "Hyakutake", 1200, 150, 0.2, 1100, 120, 124, 1.3, Color.AQUAMARINE));
		addBody(new CometBody("ヘール・ボップ彗星", "Hale-Bopp", 1000, 300, 0.125, 900, 280, 89, 2.5, Color.WHITE));
		addBody(new CometBody("ラヴジョイ彗星", "Lovejoy", 800, 120, 0.225, 790, 15, 101, 1.0, Color.LIMEGREEN));

		// --- 追加の彗星 ---
		// 1. スウィフト・タットル彗星 (109P/Swift-Tuttle) - ペルセウス座流星群の母天体。
		// 傾斜角 113度。完全に逆走しつつ、公転面を垂直に近い角度で貫通します。
		addBody(new CometBody("スウィフト・タットル", "Swift-Tuttle", 1000, 150, 0.12, 950, 60, 113, 1.5, Color.LIGHTGOLDENRODYELLOW));
		// 2. ディアゴスティーニ彗星 (C/2012 S1 ISON) - アイソン彗星。
		// 太陽に極端に接近した「サングレイザー」。垂直に近い軌道から太陽の鼻先をかすめます。
		addBody(new CometBody("アイソン", "ISON", 1200, 50, 0.2, 1180, 20, 62, 1.3, Color.PALEGOLDENROD));
		// 3. エンケ彗星 (2P/Encke) - 非常に短い周期。
		// 軌道は比較的内側ですが、傾斜角が11度あり、頻繁に内惑星の近くをチョロチョロします。
		addBody(new CometBody("エンケ", "Encke", 400, 150, 0.3, 200, 30, 11, 0.9, Color.LIGHTPINK));
		// 4. ボリソフ彗星 (2I/Borisov) - オウムアムアに続く「第２の恒星間天体」。
		// 傾斜角 44度。他所からやってきて、猛スピードで去っていく放浪者。
		addBody(new CometBody("ボリソフ", "Borisov", 15000, 300, 0.03, 8000, 10, 44, 0.7, Color.LIGHTSTEELBLUE));
		// 5. 1811年の大彗星 (C/1811 F1) - ナポレオン彗星とも呼ばれる。
		// 傾斜角107度。非常に巨大な尾を引いた歴史的名彗星。
		addBody(new CometBody("1811年大彗星", "Great Comet of 1811", 1500, 100, 0.1, 1400, 45, 107, 2.0, Color.GOLDENROD));
		// 6. ドナティ彗星 (C/1858 L1) - 最も美しいと言われた彗星。
		// 傾斜角117度。垂直に近く、かつ逆走する美しいラインを描きます。
		addBody(new CometBody("ドナティ", "Donati", 800, 120, 0.2, 750, 100, 117, 1.8, Color.LIGHTCYAN));
		// 7. 1882年の大彗星 (C/1882 R1) - 太陽のすぐ側を通過したサングレイザー。
		// 傾斜角142度。惑星たちとは全く違う方向から突っ込んできます。
		addBody(new CometBody("1882年大彗星", "Great Sept. Comet", 1100, 50, 0.25, 1080, 200, 142, 1.6, Color.WHITE));
		// 8. パンスターズ彗星 (C/2011 L4) - 21世紀の観測。
		addBody(new CometBody("パンスターズ", "PANSTARRS", 950, 130, 0.18, 900, 30, 84, 1.2, Color.SKYBLUE));
		// 9. ネオワイズ彗星 (C/2020 F3) - 記憶に新しい2020年の彗星。
		addBody(new CometBody("ネオワイズ", "NEOWISE", 700, 80, 0.2, 650, 150, 129, 1.4, Color.LIGHTYELLOW));

		// 6. 特殊軌道・恒星間天体
		addBody(new CometBody("オウムアムア", "'Oumuamua", 20000, 200, 0.02, 10000, 60, 120, 0.5, Color.SADDLEBROWN));
		addBody(new EllipticalBody("セドナ", "Sedna", 1500, 800, 0.004, 1200, 90, -11, 2.5, null, Color.ORANGE));
		addBody(new EllipticalBody("ザ・ゴブリン", "The Goblin", 10000, 800, 0.004, 9000, 300, 11.6, 0.8, null, Color.DARKRED));
		addBody(new EllipticalBody("ドラック", "Drac", 400, 350, 0.08, 50, 0, 103, 0.8, null, Color.DARKRED));

		// --- 追加特殊軌道天体 ---
		// 5. チュリュモフ・ゲラシメンコ彗星（別角度版） - 既に67Pはありますが、
		// 遠日点をさらに遠くへ飛ばした「キロン (Chiron)」のようなケンタウルス族。
		// 彗星と小惑星の中間。傾斜角 7度。
		addBody(new EllipticalBody("キロン", "Chiron", 900, 800, 0.012, 400, 200, -7, 1.8, null, Color.CYAN));
		// 6. 2015 BZ509 (Bee-Zed) - 木星と共鳴しながら、完全に「逆走」している変わり種。
		// 軌道傾斜角はなんと 163度。惑星たちと真っ向からすれ違います。
		addBody(new EllipticalBody("ビー・ゼット", "Bee-Zed", 300, 290, 0.1, 5, 0, 163, 0.8, null, Color.LIME));

		// 7. カイパーベルト：ケプラーの法則に基づく速度分布（500個）
		for (int i = 0; i < 500; i++) {
			double d = 600 + Math.random() * 200;
			double p = (Math.random() - 0.5) * 20;
			double s = 0.016 * Math.sqrt(580.0 / d); 
			addBody(new EllipticalBody("", "", d, d * 0.98, s, d * 0.02, Math.random()*360, p, 0.8, null, Color.WHITESMOKE));
		}

		// 8. 背景・環境エフェクトの構築
		this.oortCloud = new OortCloud(this.pane3D, 500);
		this.spaceWind = new SpaceWind(this.pane3D, 100);
		this.milkyWayImage = new MilkyWayImage(this.pane3D);

	} // end createUniverse

	/**
	 * addBody: マスターリストへの登録と3D/2Dシーンツリーへの天体要素追加。
	 */
	private void addBody(CelestialBody body) {

		// 全天体一括管理リストに追加
		bodies.add(body);

		// 天体本体の追加
		pane3D.getChildren().add(body.getNode());

		// 軌道ラインの追加（初期状態はON）
		if (body.getOrbitGroup() != null) {
			pane3D.getChildren().add(body.getOrbitGroup());
			body.setOrbitVisible(showOrbits);
		}

		// ラベルの追加（2Dレイヤー）
		if (body.getTextLabel() != null) {
			pane2D.getChildren().add(body.getTextLabel());
			body.setSubScene3D(this.subScene3D); // ビルボード計算用にコンテキスト注入
		}

	} // end addBody

	/**
	 * updateUniverse: 毎フレーム実行される全宇宙の同期処理。
	 * カメラの視点、ズーム、天体の物理計算、環境演出を一括で更新します。
	 */
	private void updateUniverse() {

		// 1. 宇宙全体の回転角を反映
		worldRotateX.setAngle(currentRotX);
		worldRotateY.setAngle(currentRotY);

		// 2. カメラのズーム位置（Z座標）反映
		camera.setTranslateZ(-camDist);

		// 3. ダイナミックオービット（Y座標）反映
		if (dynamicOrbitEnabled) {
			// 呼吸の速さを調整
			orbitTime += ORBIT_TIME;
			// ゆったり上下させる
			double yOffset = -Math.sin(orbitTime) * ORBIT;
			camera.setTranslateY(yOffset);
		}

		// 4. ズーム倍率に応じたサイズ補正係数の算出
		// これにより、遠ざかってもラベルや天体が豆粒になりすぎないように制御可能
		double sizeFactor = Math.max(0.5, camDist / 1500.0);

		// 5. 全天体の物理計算と描画の更新
		for(CelestialBody body : bodies){
			// 座標更新、自転、近日点演出、ビルボード処理を一括実行
			body.update(currentRotX, currentRotY, camDist, sizeFactor, showLabels);
		}

		// 6. 環境演出（星間物質の風）の進行
		if (spaceWind != null) spaceWind.updateSpaceWind();

	} // end updateUniverse

	/**
	 * toggleOrbitByCategory: 特定カテゴリに属する全天体の軌道表示を反転させます。
	 * @param cat 反転対象のカテゴリ（PLANET, ELLIPTICAL, COMET）
	 */
	private void toggleOrbitByCategory(CelestialBody.Category cat) {
		bodies.stream()
			.filter(b -> b.getCategory() == cat)
			.forEach(b -> b.setOrbitVisible(!b.getOrbitGroup().isVisible()));
	}

	/**
	 * changeLang: HUDテキストの多言語切り替え処理
	 * 日英の切り替えをします。
	 */
	private void changeLang(){

		// 言語変更
		this.lang = ("ja".equals(this.lang) ? "en" : "ja");

		// HUDの更新
		updateHUDTexts();

		// ★全天体の名前を一斉にスイッチ！
		bodies.forEach(b -> b.updateLabelLanguage(this.lang));

	} // end changeLang

	/**
	 * HUD（ヘッドアップディスプレイ）の更新
	 */
	private void updateHUDTexts(){

		textLang.setText(lang.equals("ja") ? "L：English／Japanese" : "L: English／Japanese");
		textMove.setText(lang.equals("ja") ? "カーソルキー：カメラ回転操作 （Ｘ／Ｙ）" : "Arrow Keys: Rotate Camera (X/Y)");
		textLabel.setText(lang.equals("ja") ? "スペースキー：天体ラベル表示切替" : "Space: Toggle Planet Labels");
		textDynamicOrbit.setText(lang.equals("ja") ? "Ｄキー：ダイナミック・オービット（浮遊感）" : "D: Dynamic Orbit (Floating Motion) ");
		textHUDHidden.setText(lang.equals("ja") ? "Ｈキー：UI表示／非表示" : "H: Show/Hide UI");
		textOrbitHidden.setText(lang.equals("ja") ? "0：軌道ライン全天体非表示" : "0: Hide All Orbit Lines");
		textOrbitPlanet.setText(lang.equals("ja") ? "1：惑星軌道ライン表示" : "1: Show Planetary Orbit Lines");
		textOrbitElliptic.setText(lang.equals("ja") ? "2：楕円軌道天体 軌道ライン表示" : "2: Show Elliptical/Inclined Orbit Lines");
		textOrbitComet.setText(lang.equals("ja") ? "3：彗星軌道ライン表示" : "3: Show Comet Orbit Lines");

		// スピード倍率
		applyTimeScale();

		// 視野角
		applyZoom();

	} // end updateHUDTexts

	/**
	 * 全天体に現在の倍率を適用する
	 */
	private void applyTimeScale() {
		bodies.forEach(b -> b.setTimeScale(timeScale));
		// HUD（テキスト）に現在の倍率を表示すると親切です
		textSpeedInfo.setText(String.format("Time Scale A-UP/Z-DOWN: %.2f x", timeScale));
	}

	/**
	 * 視野角のテキストを変更
	 */
	private void applyZoom() {
		textZoom.setText(lang.equals("ja") 
		? String.format("PageUP/Down ：視野角調整（広角／望遠） [FOV: %.1f]", currentFOV) 
		: String.format("PageUP/Down: Adjust FOV (Wide/Tele) [FOV: %.1f]", currentFOV));
	}

	public static void main(String[] args) {
		launch(args);
	}

}
