import os
import re
import numpy as np
import pandas as pd

def get_external_lib():
    script_dir = os.path.dirname(os.path.abspath(__file__))
    # パスは XRD_GUI.py から見た相対パスに注意  
    module_path = os.path.join(os.path.dirname(script_dir), "XRD_GUI_lib.py")
    if os.path.isfile(module_path):
        import importlib.util
        spec = importlib.util.spec_from_file_location("XRD_GUI_lib", module_path)
        lib = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(lib)
        return lib
    print("GUI_XRD_Lib is not found")
    return None

XRD_GUI_lib = get_external_lib()

def parse_xrd_file(filepath, external_lib=None):
    """
    XRDデータファイルを解析する（純粋なロジックのみ）。
    
    Args:
        filepath (str): ファイルパス
        external_lib: XRD_GUI_lib オブジェクト（外部パーサーを使用する場合）
        
    Returns:
        tuple: (sample_name, angles, intensities, error_msg)
               成功時は error_msg は None。失敗時は (None, None, None, "エラー内容") を返す。
    """
    sample_name = os.path.splitext(os.path.basename(filepath))[0]
    angles = []
    intensities = []

    # --- 1. 外部ライブラリ (XRD_GUI_lib) の試行 ---
    if external_lib is not None and hasattr(external_lib, "parse_xrd"):
        try:
            # 外部ライブラリの結果を取得
            res_name, res_angles, res_intensities = external_lib.parse_xrd(filepath)
            return res_name, res_angles, res_intensities, None
        except Exception as e:
            # 外部エラーを記録して内蔵パーサーへ（呼び出し側に伝えるためのフラグを立てても良い）
            print(f"DEBUG: 外部パーサー失敗、内蔵へフォールバック: {e}")

    # --- 2. 内蔵パーサーロジック ---
    start_reading_keywords = ["Step", "ScanSpeed"]
    reading_data = False

    try:
        with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
            for line_number, line in enumerate(f):
                line = line.strip()
                if not line:
                    continue

                if not reading_data:
                    if line.startswith("Sample"):
                        parts = line.split('\t', 1)
                        if len(parts) > 1:
                            sample_name = parts[1].strip()
                    
                    if any(keyword in line for keyword in start_reading_keywords):
                        reading_data = True
                    continue

                try:
                    parts = line.replace(',', ' ').split()
                    if len(parts) >= 2:
                        angles.append(float(parts[0]))
                        intensities.append(float(parts[1]))
                    else:
                        break 
                except (ValueError, IndexError):
                    break
        
        if not angles or not intensities:
            return None, None, None, f"ファイル '{os.path.basename(filepath)}' から有効な数値データを抽出できませんでした。"

        return sample_name, np.array(angles), np.array(intensities), None

    except FileNotFoundError:
        return None, None, None, f"ファイルが見つかりません: {filepath}"
    except Exception as e:
        return None, None, None, f"ファイルの読み込み中に予期せぬエラーが発生しました: {e}"
    
def parse_reference_file(filepath, external_lib=None, wavelength=None): # wavelength引数を追加
    if external_lib is not None and hasattr(external_lib, "parse_reference"):
        try:
            # 波長を引数に追加して呼び出す
            name, pos, inten, hkls, raw = external_lib.parse_reference(
                filepath, xmin=0.0, xmax=120.0, wavelength=wavelength
            )
            if pos is not None and len(pos) > 0:
                return name, np.array(pos), np.array(inten), hkls, raw
        except Exception as e:
            print(f"外部パーサー parse_reference 失敗: {e}")

    return parse_reference_plaintext(filepath)

def parse_reference_plaintext(filepath,external_lib=None):
        reference_name = os.path.splitext(os.path.basename(filepath))[0]

        positions = []
        intensities = []
        hkls = []
        raw_indices = []

        reading_data = False
        idx_2theta = None
        idx_I = None
        bad_rows = 0
        total_rows = 0

        def is_unit_token(tok: str) -> bool:
            tok = tok.strip()
            return tok.startswith("(") and tok.endswith(")")

        def to_float_safe(s: str) -> float:
            # 変な文字が混ざっても数値だけ抜いてfloat化
            s2 = re.sub(r"[^0-9eE\+\-\.]", "", s)
            if s2 in ("", ".", "-", "+"):
                raise ValueError
            return float(s2)

        try:
            with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
                for line in f:
                    line = line.strip()
                    if not line:
                        continue

                    parts = line.split()
                    if len(parts) < 3:
                        continue

                    # --- ヘッダ検出 ---
                    if (not reading_data) and ("h" in [p.lower() for p in parts]) and ("k" in [p.lower() for p in parts]) and ("l" in [p.lower() for p in parts]):
                        header_clean = [p for p in parts if not is_unit_token(p)]
                        header_lower = [p.lower() for p in header_clean]

                        idx_I = None
                        for i, p in enumerate(header_lower):
                            if p == "i" or p == "intensity":
                                idx_I = i
                                break

                        idx_2theta = None
                        for i, p in enumerate(header_clean):
                            pl = p.lower()
                            if ("2θ" in p) or ("2theta" in pl) or ("theta" in pl) or ("2th" in pl):
                                idx_2theta = i
                                break

                        # 2θが取れない場合は I の左を採用（あなたの形式に強い）
                        if idx_2theta is None and idx_I is not None and idx_I - 1 >= 0:
                            idx_2theta = idx_I - 1
                        # I が取れない場合は 2θ の右
                        if idx_I is None and idx_2theta is not None and idx_2theta + 1 < len(header_clean):
                            idx_I = idx_2theta + 1

                        print(f"[Header] idx_2theta={idx_2theta}, idx_I={idx_I}, header_clean={header_clean}")
                        reading_data = True
                        continue

                    if not reading_data:
                        continue

                    # --- データ行 ---
                    try:
                        h = int(parts[0]); k = int(parts[1]); l = int(parts[2])
                    except Exception:
                        continue

                    if idx_2theta is None or idx_I is None:
                        continue

                    # 列数不足はスキップ（breakしない）
                    if len(parts) <= max(idx_2theta, idx_I):
                        bad_rows += 1
                        continue

                    total_rows += 1
                    try:
                        pos_val = to_float_safe(parts[idx_2theta])
                        int_val = to_float_safe(parts[idx_I])
                    except Exception:
                        bad_rows += 1
                        continue

                    positions.append(pos_val)
                    intensities.append(int_val)
                    hkls.append(f"({h} {k} {l})")
                    raw_indices.append({"h": h, "k": k, "l": l, "i": -(h + k)})

            if not positions:
                print(f"[WARN] no peaks parsed: total_rows={total_rows}, bad_rows={bad_rows}")
                return None, None, None, None, None

            positions = np.array(positions, dtype=float)
            intensities = np.array(intensities, dtype=float)

            # ref表示用に正規化
            maxI = float(np.max(intensities)) if intensities.size else 1.0
            intensities = intensities / maxI if maxI > 0 else np.zeros_like(intensities)

            print(f"[Parsed] N={len(positions)} 2theta(min,max)=({positions.min():.2f},{positions.max():.2f}) bad_rows={bad_rows}")
            return reference_name, positions, intensities, hkls, raw_indices

        except Exception as e:
            print(f"Parse error (parse_reference_plaintext): {e}")
            return None, None, None, None, None