import os
from pymatgen.io.vasp import Vasprun
import json
from pathlib import Path
import xmltodict


vasprun_file = "SCF/vasprun.xml"

import json

def recursive_type_conversion(data):
    """
    辞書またはリストを再帰的にパースし、
    '@type' 属性に基づいて '#text' の値を型変換します。
    '@name' 属性があれば、それを新しいキーとして使用します。
    '@comment' 属性があれば、それをキーとし 'r' の内容を数値リストに変換します。
    """
    if isinstance(data, dict):
        new_data = {}

        # 0. @comment と 'r' が同階層にある場合（例: bandデータ）
        if '@comment' in data and 'r' in data and isinstance(data['r'], list):
            comment_key = data['@comment']
            r_list_of_strings = data['r']
            processed_r_list = []
            for s in r_list_of_strings:
                if not isinstance(s, str):
                    # 文字列でない場合はスキップするか、適切なエラー処理
                    continue
                try:
                    # 各文字列をスペースで分割し、floatのリストに変換
                    processed_r_list.append([float(x) for x in s.split()])
                except ValueError:
                    # 変換できない場合は元の文字列を保持
                    processed_r_list.append(s.strip())
            new_data[comment_key] = processed_r_list
            # この要素は変換済みなので、これ以上掘り下げずに返す
            return new_data


        # 1. @name, #text, @type が同じレベルにある場合（INCARパラメータなど）
        if '@name' in data and '#text' in data:
            name = data['@name']
            text_value = data['#text']
            param_type = data.get('@type')

            if name and text_value is not None:
                if param_type == 'int':
                    try:
                        new_data[name] = int(text_value)
                    except ValueError:
                        new_data[name] = text_value
                elif param_type == 'logical':
                    new_data[name] = True if str(text_value).upper() == 'T' else False
                elif param_type == 'string':
                    new_data[name] = str(text_value).strip()
                elif param_type is None and isinstance(text_value, str):
                    try:
                        new_data[name] = float(text_value)
                    except ValueError:
                        new_data[name] = text_value.strip()
                else:
                    new_data[name] = text_value
                return new_data # 変換が完了したので、再帰を終了

        # 2. @name と 'v' が同階層にある場合（basis, positionsなど）
        #    そして 'v' の内容がリスト形式の文字列の場合
        if '@name' in data and 'v' in data and isinstance(data['v'], list):
            name = data['@name']
            list_of_strings = data['v']
            processed_list = []
            for s in list_of_strings:
                if not isinstance(s, str): # ここで型チェックを追加
                    continue
                try:
                    # 各文字列をスペースで分割し、floatのリストに変換
                    processed_list.append([float(x) for x in s.split()])
                except ValueError:
                    # 変換できない場合は元の文字列を保持
                    processed_list.append(s.strip())
            new_data[name] = processed_list
            # この要素は変換済みなので、これ以上掘り下げずに返す
            return new_data

        # 3. 'v' が辞書形式で、@name と #text を持つ場合 (例: RWIGS)
        if 'v' in data and isinstance(data['v'], dict) and '@name' in data['v'] and '#text' in data['v']:
            name = data['v']['@name']
            text_value = data['v']['#text']
            try:
                # 数値のリストに変換
                new_data[name] = [float(x) for x in text_value.split()]
            except ValueError:
                new_data[name] = text_value.strip()
            # この要素は変換済みなので、これ以上掘り下げずに返す
            # del data['v'] # 元の辞書を変更しないためここでは削除しない
            # このパターンは、再帰処理の前に処理されるべきかもしれないが、
            # 'i' や 'v' が辞書の直接の子である場合に再帰的に処理されるようにする

        # 4. 'i' キーの下にリストとして要素がある場合 (例: incarの'i')
        if 'i' in data and isinstance(data['i'], list):
            processed_i = {}
            for item in data['i']:
                result = recursive_type_conversion(item)
                if isinstance(result, dict):
                    processed_i.update(result)
            new_data.update(processed_i)
            # 'i' 以外の残りの兄弟要素を再帰的に処理
            for key, value in data.items():
                if key not in ['@name', '#text', '@type', 'i', 'v', '@comment', 'r']: # 新しいキーを追加
                    new_data[key] = recursive_type_conversion(value)
            return new_data # 'i' が処理されたらここで戻る

        # 5. 'i' キーの下に単一の辞書として要素がある場合 (例: volume)
        if 'i' in data and isinstance(data['i'], dict):
            # 'i' の中の単一の辞書を再帰的に処理
            result = recursive_type_conversion(data['i'])
            if isinstance(result, dict):
                new_data.update(result)
            # 'i' 以外の残りの兄弟要素を再帰的に処理
            for key, value in data.items():
                if key not in ['@name', '#text', '@type', 'i', 'v', '@comment', 'r']: # 新しいキーを追加
                    new_data[key] = recursive_type_conversion(value)
            return new_data

        # 6. 上記以外の一般的な辞書キーを再帰的に処理
        for key, value in data.items():
            result = recursive_type_conversion(value)
            # '@name' と '#text' のパターンで変換された結果を直接追加
            if isinstance(result, dict) and len(result) == 1 and list(result.keys())[0] == key:
                # このケースは、辞書のキーがすでに '@name' で置き換えられている場合
                new_data[key] = result[key]
            else:
                # それ以外は通常のキーとして追加
                new_data[key] = result
        return new_data

    elif isinstance(data, list):
        # リストの各要素を再帰的に処理
        new_list = []
        for item in data:
            result = recursive_type_conversion(item)
            # 'v' や 'r' のリストがすでに変換されている場合はそのまま追加
            if isinstance(result, list) and all(isinstance(elem, list) for elem in result):
                new_list.extend(result)
            elif isinstance(result, dict) and len(result) == 1:
                # '@name' と '#text' / '@comment' と 'r' の変換で生成された単一要素辞書の場合、値を直接追加
                new_list.append(list(result.values())[0])
            else:
                new_list.append(result)
        return new_list

    else:
        # 辞書でもリストでもない場合はそのまま返す
        return data

def parse_vasprun_to_dict(vasprun_filepath):
    """
    VASPのvasprun.xmlファイルを読み込み、辞書型に変換して返します。

    Args:
        vasprun_filepath (str): vasprun.xmlファイルのパス

    Returns:
        dict: vasprun.xmlから読み込まれたデータを辞書型にしたもの
              エラーが発生した場合はNoneを返します。
    """
    if not os.path.exists(vasprun_filepath):
        print(f"エラー: ファイルが見つかりません - {vasprun_filepath}")
        return None

    path = Path(vasprun_file)
    raw_xml = path.read_text(encoding='utf-8')
    parsed = xmltodict.parse(raw_xml)
    d = recursive_type_conversion(parsed)
    print(d)
    exit()

    vasprun = Vasprun(vasprun_filepath)
#    vasprun.incar["ALGO"] = str(vasprun.incar["ALGO"])
    vasprun_dict = vasprun.as_dict()

    return vasprun_dict

if __name__ == "__main__":
    vasprun_data = parse_vasprun_to_dict(vasprun_file)

    if vasprun_data:
        print("vasprun.xmlのデータ（辞書型）:")
        # 辞書の内容が大きいため、一部を表示するか、整形して表示する
        # ここではjson.dumpsを使って整形して表示します。
        # indent=2 で見やすくインデントされます。
        print(json.dumps(vasprun_data, indent=2))

        # 例: エネルギー情報を取得して表示
        # vasprun.xmlの構造によってキーは異なります。
        # この例ではダミーファイルなので、実際のVASPの出力とは異なります。
        # 実際のデータにアクセスするには、辞書の構造を調べる必要があります。
        # print(f"\n最終エネルギー: {vasprun_data['final_energy']}") # 実際のvasprun.xmlから取得する例
    else:
        print("vasprun.xmlの読み込みに失敗しました。")