import javafx.scene.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.geometry.Point3D;

/**
 * CelestialBody: すべての天体の基底抽象クラス。
 * 3Dモデル、ラベル、およびカメラに追従するビルボードロジック、
 * そしてカテゴリ別の軌道表示管理を一手に引き受ける「親玉」クラスです。
 * 公転軌道の可視化（OrbitLine）および表示・非表示のトグル制御機能を完備。
 * @author きん ＆ Gemini
 */
public abstract class CelestialBody {

	/**
	 * 天体の分類を定義する列挙型。
	 * キー操作（1, 2, 3）による表示の振り分けに使用。
	 */
	public enum Category { 
		PLANET,	  // 1: 主要惑星（円軌道）
		ELLIPTICAL,  // 2: 楕円・傾斜天体（準惑星・外縁天体）
		COMET		// 3: 彗星（特殊演出あり）
	}

	// --- フィールド定義 ---

	/**
	 * カテゴリー。天体名表示（123キー）のために利用
	 * 派生クラスで必ず上書きすること
	 */ 
	protected Category category = Category.PLANET; // デフォルトは惑星

	protected Node node;			// 天体の3Dモデル（Sphere, Group等）
	protected Text textLabel;		// 天体名を表示する2Dテキストラベル
	protected String name;			// 表示天体名
	protected String nameJa;		// 天体名（日）
	protected String nameEn;		// 天体名（英）
	protected Group orbitGroup;		// 軌道ラインを保持する専用グループ

	protected SubScene subScene3D;	// 座標変換のリファレンスとなる3Dサブシーン

	// --- 座標・物理パラメータ ---
	protected double x;					// 現在のX座標
	protected double y;					// 現在のY座標
	protected double z;					// 現在のZ座標
	protected double speed;				// 公転スピード（角速度）
	protected double currentAngle;		// 現在の公転角度
	protected double timeScale = 1.0;	// スピード（全体速度）

	/**
	 * コンストラクタ（角度ランダム生成用）
	 * @param nameJa 天体名（日）
	 * @param nameEn 天体名（英）
	 * @param speed スピード
	 */
	public CelestialBody(String nameJa, String nameEn, double speed) {
		// デフォルトでは角度をランダムにするため -1 を指定して委譲
		this(nameJa, nameEn, speed, -1);
	}

	/**
	 * コンストラクタ（開始角度指定用）
	 * @param nameJa 天体名（日）
	 * @param nameEn 天体名（英）
	 * @param speed スピード
	 * @param startAngle 初期の角度（-1 の場合はランダムな 0-360 の値を生成）
	 */
	public CelestialBody(String nameJa, String nameEn, double speed, double startAngle) {

		this.nameJa = nameJa;
		this.nameEn = nameEn;
		this.name = nameJa; // デフォルトは日本語にしておく
		this.speed = speed;

		// 角度の初期化ロジック
		if (startAngle != -1) {
			this.currentAngle = startAngle;
		} else {
			// 全天体が同じ位置からスタートしないよう、初期位相を散らす
			this.currentAngle = Math.random() * 360;
		}

		// ラベルの生成と装飾
		if (name != null && !name.isEmpty()) {
			this.textLabel = new Text(name);
			// 視認性を考慮したカラー（シアン系・透過あり）
			this.textLabel.setFont(new Font("Arial", 10));
			this.textLabel.setScaleX(1.2);
			this.textLabel.setFill(Color.web("#00FFFF", 0.6));
		}

		// 軌道ライン用グループの初期化
		// 実際の描画データ（Cylinder等）は派生クラスの buildOrbitLine で生成される。
		this.orbitGroup = new Group();
		this.orbitGroup.setVisible(false); // 初期状態は非表示（くちゃくちゃ防止）

	} // end CelestialBody constructor

	/**
	 * update: 毎フレームの更新処理。
	 * 「位置計算」と「表示反映」を明確に分離し、数学的正確さを担保。
	 * @param rotX カメラ（世界）のX回転（逆行列用）
	 * @param rotY カメラ（世界）のY回転
	 * @param camDist カメラ距離
	 * @param sizeFactor ズーム倍率
	 * @param showLabels 天体ラベル表示
	 */
	public void update(double rotX, double rotY, double camDist, double sizeFactor, boolean showLabels) {

		// 1. 座標計算（各クラス固有の数学モデルを実行）
		computePosition();

		// 2. 3Dモデルの位置を更新
		if (node != null) {
			node.setTranslateX(x);
			node.setTranslateY(y);
			node.setTranslateZ(z);
		}

		// 3. ラベル（2D/3Dハイブリッド）のビルボード処理
		if (textLabel != null) {
			this.applyBillboard(rotX, rotY, camDist, sizeFactor, showLabels);
		}

	} // end update

	/**
	 * computePosition: 座標計算の抽象メソッド。
	 * 円、楕円、あるいは彗星独自の回避ロジックなどを派生クラスで実装する。
	 */
	protected abstract void computePosition();

	/**
	 * applyBillboard: 3D天体の位置に追従する2Dラベルの座標同期。
	 * * 【職人デバッグの成果】
	 * localToScene(Point3D.ZERO, true) を採用することで、
	 * SubScene 固有のオフセットを完全に排除し、全画面モードでもズレない
	 * 正確な追従を実現する。
	 * @param rotX カメラ（世界）のX回転（逆行列用）
	 * @param rotY カメラ（世界）のY回転
	 * @param camDist カメラ距離
	 * @param sizeFactor ズーム倍率
	 * @param showLabels 天体ラベル表示
	 */
	protected void applyBillboard(double rotX, double rotY, double camDist, double sizeFactor, boolean showLabels) {

		if (textLabel == null || node == null || textLabel.getParent() == null) return;

		// 天体ラベルを表示する必要が無い場合はリターン
		if(!showLabels) return;

		// 3D空間上の天体の位置（Scene座標）を取得
		Point3D scenePos = node.localToScene(Point3D.ZERO);

		// カメラを取得し、天体の位置を「カメラから見た座標」に変換
		Camera cam = subScene3D.getCamera();
		Point3D posInCam = cam.sceneToLocal(scenePos);

		// 判定：Zが0以下ならカメラの後ろ（または近すぎ）なので非表示
		// JavaFXのPerspectiveCamera(true)は、常に+Z方向が「前」です。
		if (posInCam.getZ() <= 0) {
			textLabel.setVisible(false);
			return;
		}

		// 表示に戻す
		textLabel.setVisible(true);

		// 前方にいる時だけ、2Dスクリーン座標を計算して表示
		Point3D localPos = node.localToScene(Point3D.ZERO, true);

		// ラベルを天体の少し上(Y-15)に配置
		textLabel.setTranslateX(localPos.getX());
		textLabel.setTranslateY(localPos.getY() - 15);
		textLabel.setTranslateZ(0); // 2Dレイヤー上のためZは0

	} // end applyBillboard

	// --- アクセッサ (Getters / Setters) ---

	public Node getNode() { return node; }

	public Text getTextLabel() { return textLabel; }

	public Group getOrbitGroup() { return orbitGroup; }

	// 言語スイッチ用
	public void updateLabelLanguage(String lang) {

		if (textLabel == null) return;
		this.name = "ja".equals(lang) ? nameJa : nameEn;
		textLabel.setText(this.name);

	}

	/**
	 * ラベルの表示・非表示を制御。
	 */
	public void setLabelVisible(boolean visible) {
		if (textLabel != null) textLabel.setVisible(visible);
	}

	/**
	 * 軌道ラインの表示・非表示を制御。
	 * 特定のカテゴリを強調するために使用。
	 */
	public void setOrbitVisible(boolean visible) {
		if (orbitGroup != null) orbitGroup.setVisible(visible);
	}

	/**
	 * 所属する3D SubSceneを関連付け。
	 */
	public void setSubScene3D(SubScene subScene3D) {
		this.subScene3D = subScene3D;
	}

	/**
	 * 天体のカテゴリを設定。
	 */
	public void setCategory(Category category) {
		this.category = category;
	}

	/**
	 * 天体のカテゴリを取得。
	 */
	public Category getCategory() {
		return category;
	}

	/**
	 * スピード（全体速度）を設定。
	 */
	public void setTimeScale(double scale) {
		this.timeScale = scale;
	}

} // end abstract class CelestialBody
