#editor executableを選択するentry,ボタンを作る
#テキストファイルを選択するentry、ボタンをつくる
#openai APIかgoogle APIをapi listboxで選択
#apiの選択を変えたら、model listboxの選択肢を変更
#Roleを辞書型リストrole_list = [{"desc": "explanation", "prompt": "prompt"}]で設定し、comboboxにdescのリストを追加して選択
#role combobolxで選択したdescのpromptoをtext boxに表示
#promptを辞書型リストprompt_list = [{"desc": "explanation", "prompt": "prompt"}]で設定し、comboboxにdescのリストを追加して選択
#prompt combobolxで選択したdescのpromptoをtext boxに表示
#temperatureを入力するentry
#max_bytesを入力するentry
#queryボタンを押すと、入力ファイルをmax_bytesまで読み込み、apiに送信。response.txtをoutput_pathに保存するとともに、editorで表示
#query時、アプリ終了時に上記の設定を.iniファイルに保存。次回のアプリ記事同時に読み込む
#スクリプトディレクトリにroles.iniとprompt.iniがあると想定し、
#起動時にそれらを読み込んでrole_listとprompt_listに入れてください。
#これらを読み込む関数は独自実装し、desc="prompt"の形式で"prompt"部分は改行を含む複数行でも読めるようにし、desc=を見つけたらlistに追加してください。行頭が#で始まる場合はコメント行として無視します。

#pip install tkinter ttkthemes openai google-generativeai inifile


import os
import re
import threading
import configparser
try:
    from dotenv import load_dotenv
except:
    print("\nImport error: dotenv")
    input("Install: pip install dotenv")
    exit()
try:
    import tkinter as tk
    import tkinter.font
    from tkinter import ttk, filedialog, messagebox
except:
    print("\nImport error: tkinter")
    input("Install: pip install tkinter")
    exit()
try:
   from ttkthemes import ThemedTk
except:
    print("\nImport error: ttkthemes")
    input("Install: pip install ttkthemes")
    exit()
try:
    import openai
except:
    print("\nWarning: openai is not installed")
    input("install by pip install openai if needed")
    openai = None
try:
    import google.generativeai as genai
except:
    print("\nWarning: google.generativeai is not installed")
    input("install by pip install google-generativeai as genai if needed")
    genai = None


PROGRAM_NAME = "AI Assistant"
editor_default = "code"
openai_models = ["gpt-4o", "gpt-4.5", "o4-mini", "o4-mini-high", "gpt-3.5-turbo"]
openai_model_default = "gpt-4o"
google_models = ["gemini-2.5-flash", "gemini-2.5-pro"]
google_model_default = "gemini-2.5-flash"


script_path = os.path.abspath(__file__)
config_ini = os.path.splitext(script_path)[0] + '.ini'
config_path = "translate.env"


if not os.path.isfile(config_ini):
    script_dir = os.path.dirname(os.path.abspath(__file__))
    config_path = os.path.join(script_dir, config_path)

print()
if os.path.isfile(config_path):
    print(f"config_path: {config_path}")
else:
    print(f"Warning: config_path {config_path} is not found")
load_dotenv(dotenv_path=config_path)

account_inf_path = os.getenv("account_inf_path", "accounts.env")
if os.path.isfile(account_inf_path):
    print(f"account_inf_path: {account_inf_path}")
else:
    print(f"Warning: account_inf_path {account_inf_path} is not found")
load_dotenv(dotenv_path=account_inf_path)

OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
if not OPENAI_API_KEY:
    print("\nWarning: OPENAI_API_KEY environment variable is not set.")

GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
if not GOOGLE_API_KEY:
    print("\nWarning: GOOGLE_API_KEY environment variable is not set.")

API_list = []
API_default = ""

if openai and OPENAI_API_KEY:
    openai_models = openai_models
    openai_model_default = "gpt-4o"
    API_list.append("OpenAI")
    API_default = "OpenAI"
else:
    openai_models = []
    openai_model_default = ""

if genai and GOOGLE_API_KEY:
    google_models = google_models
    google_model_default = "gemini-2.5-flash"
    API_list.append("Google")
    if API_default == "": API_default = "Google"
else:
    google_model = []
    google_model_default = ""

class AIEditorApp:
    def __init__(self, master):
        self.master = master
        master.title(PROGRAM_NAME)
        master.geometry("800x700")

        self.config_file = config_ini
        self.load_config()

        # roles.iniとprompt.iniから読み込む
        self.role_list = self.load_ini_file("roles.ini")
        if not self.role_list: # 読み込み失敗時のデフォルト設定
            self.role_list = [
                {"desc": "文章の要約", "prompt": "以下の文章を要約してください。\n\n"},
                {"desc": "コードのレビュー", "prompt": "以下のPythonコードをレビューし、改善点を提案してください。\n\n"},
                {"desc": "翻訳（日本語→英語）", "prompt": "以下の日本語を英語に翻訳してください。\n\n"},
                {"desc": "キーワード抽出", "prompt": "以下の文章から主要なキーワードを5つ抽出してください。\n\n"}
            ]

        self.prompt_list = self.load_ini_file("prompt.ini")
        if not self.prompt_list: # 読み込み失敗時のデフォルト設定
            self.prompt_list = [
                {"desc": "丁寧な表現", "prompt": "常に丁寧な言葉遣いを心がけてください。"},
                {"desc": "専門的な表現", "prompt": "専門用語を適切に使用し、技術的な視点から記述してください。"},
                {"desc": "創造的な表現", "prompt": "創造的でユニークなアイデアを提案してください。"},
                {"desc": "簡潔な表現", "prompt": "可能な限り簡潔に、要点のみをまとめてください。"}
            ]
        
        # Initialize _line_height_pixels early
        self._line_height_pixels = 0 

        self.create_widgets()
        
        # prompt_text_areaが作成された後にfont metricsを取得
        if self.prompt_text_area:
            self._line_height_pixels = tkinter.font.Font(font=self.prompt_text_area['font']).metrics("linespace")

        self.load_initial_settings()

        master.protocol("WM_DELETE_WINDOW", self.on_closing)

    # INIファイルを読み込む独自関数
    def load_ini_file(self, filename):
        file_path = os.path.join(os.path.dirname(__file__), filename)
        items = []
        current_desc = None
        current_prompt_lines = []

        if not os.path.exists(file_path):
            messagebox.showwarning("ファイルが見つかりません", f"{filename} が見つかりません。デフォルト設定を使用します。")
            return []

        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                for line in f:
                    line = line.strip()
                    if not line or line.startswith('#'):
                        continue

                    match = re.match(r'desc="([^"]*)"', line)
                    if match:
                        if current_desc is not None:
                            items.append({"desc": current_desc, "prompt": "\n".join(current_prompt_lines).strip()})
                        current_desc = match.group(1)
                        current_prompt_lines = []
                    elif current_desc is not None:
                        current_prompt_lines.append(line)
            
            # 最後の項目を追加
            if current_desc is not None:
                items.append({"desc": current_desc, "prompt": "\n".join(current_prompt_lines).strip()})

        except Exception as e:
            messagebox.showerror("ファイル読み込みエラー", f"{filename} の読み込み中にエラーが発生しました: {e}")
            return []
        
        return items

    def create_widgets(self):
        # Editor Executable フレーム
        editor_frame = ttk.LabelFrame(self.master, text="Editor Settings")
        editor_frame.pack(padx=10, pady=5, fill="x")

        ttk.Label(editor_frame, text="Editor Executable:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
        self.editor_executable_entry = ttk.Entry(editor_frame, width=50)
        self.editor_executable_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
        ttk.Button(editor_frame, text="Browse", command=self.browse_editor_executable).grid(row=0, column=2, padx=5, pady=5)
        editor_frame.grid_columnconfigure(1, weight=1)

        # Input Text File フレーム
        input_file_frame = ttk.LabelFrame(self.master, text="Input File Settings")
        input_file_frame.pack(padx=10, pady=5, fill="x")

        ttk.Label(input_file_frame, text="Input Text File:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
        self.input_file_entry = ttk.Entry(input_file_frame, width=50)
        self.input_file_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
        ttk.Button(input_file_frame, text="Browse", command=self.browse_input_file).grid(row=0, column=2, padx=5, pady=5)
        ttk.Button(input_file_frame, text="View", command=lambda: self.view_file(self.input_file_entry.get())).grid(row=0, column=3, padx=5, pady=5)
        input_file_frame.grid_columnconfigure(1, weight=1)

        # Output File Settings フレーム
        output_file_frame = ttk.LabelFrame(self.master, text="Output File Settings")
        output_file_frame.pack(padx=10, pady=5, fill="x")

        ttk.Label(output_file_frame, text="Output File:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
        self.output_file_entry = ttk.Entry(output_file_frame, width=50)
        self.output_file_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
        ttk.Button(output_file_frame, text="Browse", command=self.browse_output_file).grid(row=0, column=2, padx=5, pady=5)
        ttk.Button(output_file_frame, text="View", command=lambda: self.view_file(self.output_file_entry.get())).grid(row=0, column=3, padx=5, pady=5)
        output_file_frame.grid_columnconfigure(1, weight=1)
        
        # API Settings フレーム
        api_frame = ttk.LabelFrame(self.master, text="API Settings")
        api_frame.pack(padx=10, pady=5, fill="x")

        # APIとModelを同じ行に配置
        ttk.Label(api_frame, text="API:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
        self.api_selection = ttk.Combobox(api_frame, values = API_list, state="readonly")
        self.api_selection.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
        self.api_selection.set(API_default) # デフォルト選択
        self.api_selection.bind("<<ComboboxSelected>>", self.update_model_list)
        
        ttk.Label(api_frame, text="Model:").grid(row=0, column=2, padx=5, pady=5, sticky="w")
        self.model_selection = ttk.Combobox(api_frame, state="readonly")
        self.model_selection.grid(row=0, column=3, padx=5, pady=5, sticky="ew")
        self.update_model_list() # 初期モデルリストを生成

        api_frame.grid_columnconfigure(1, weight=1) # API ComboBoxの列を拡張可能に
        api_frame.grid_columnconfigure(3, weight=1) # Model ComboBoxの列を拡張可能に


        # Role and Prompt フレーム
        role_prompt_frame = ttk.LabelFrame(self.master, text="Role & Prompt Settings")
        role_prompt_frame.pack(padx=10, pady=5, fill="x")

        ttk.Label(role_prompt_frame, text="Role:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
        self.role_combobox = ttk.Combobox(role_prompt_frame, values=[role["desc"] for role in self.role_list], state="readonly")
        self.role_combobox.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
        self.role_combobox.bind("<<ComboboxSelected>>", self.display_selected_role_prompt)
        role_prompt_frame.grid_columnconfigure(1, weight=1)

        ttk.Label(role_prompt_frame, text="Prompt Template:").grid(row=1, column=0, padx=5, pady=5, sticky="w")
        self.prompt_template_combobox = ttk.Combobox(role_prompt_frame, values=[prompt["desc"] for prompt in self.prompt_list], state="readonly")
        self.prompt_template_combobox.grid(row=1, column=1, padx=5, pady=5, sticky="ew")
        self.prompt_template_combobox.bind("<<ComboboxSelected>>", self.display_selected_prompt_template)
        role_prompt_frame.grid_columnconfigure(1, weight=1)

        ttk.Label(role_prompt_frame, text="Current Prompt:").grid(row=2, column=0, padx=5, pady=5, sticky="nw")
        
        # テキストエリアとそのリサイズハンドルを保持するためのコンテナフレーム
        self.text_area_container = ttk.Frame(role_prompt_frame)
        self.text_area_container.grid(row=2, column=1, columnspan=2, padx=5, pady=5, sticky="nsew")
        
        self.prompt_text_area = tk.Text(self.text_area_container, height=8, width=70, wrap="word")
        self.prompt_text_area.pack(side="top", fill="both", expand=True)

        # リサイズハンドル（グリッパー）の作成
        self.gripper = ttk.Frame(self.text_area_container, width=15, height=15, relief="raised", borderwidth=1)
        self.gripper.pack(side="bottom", anchor="se", padx=1, pady=1)
        
        # グリッパーのドラッグイベントをバインド
        self.gripper.bind("<ButtonPress-1>", self.on_gripper_press)
        self.gripper.bind("<B1-Motion>", self.on_gripper_drag)
        
        # prompt_text_areaが配置される行のweightを設定し、リサイズ時にテキストエリアが伸びるようにする
        role_prompt_frame.grid_rowconfigure(2, weight=1) 

        # Parameters フレーム
        params_frame = ttk.LabelFrame(self.master, text="Parameters")
        params_frame.pack(padx=10, pady=5, fill="x")

        # TemperatureとMax Bytesを同じ行に配置
        ttk.Label(params_frame, text="Temperature:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
        self.temperature_entry = ttk.Entry(params_frame, width=10)
        self.temperature_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
        self.temperature_entry.insert(0, "0.7") # デフォルト値
        
        ttk.Label(params_frame, text="Max Bytes:").grid(row=0, column=2, padx=5, pady=5, sticky="w")
        self.max_bytes_entry = ttk.Entry(params_frame, width=10)
        self.max_bytes_entry.grid(row=0, column=3, padx=5, pady=5, sticky="ew")
        self.max_bytes_entry.insert(0, "4096") # デフォルト値

        params_frame.grid_columnconfigure(1, weight=1) # Temperature Entryの列を拡張可能に
        params_frame.grid_columnconfigure(3, weight=1) # Max Bytes Entryの列を拡張可能に

        # Query Button and Close Button
        button_frame = ttk.Frame(self.master)
        button_frame.pack(pady=10)

        self.query_button = ttk.Button(button_frame, text="Query AI", command=self.start_query_thread) # 【変更点】スレッド開始関数を呼び出す
        self.query_button.pack(side="left", padx=5)

        # Closeボタンを追加
        self.close_button = ttk.Button(button_frame, text="Close", command=self.on_closing)
        self.close_button.pack(side="left", padx=5)

        # ステータスバーを追加
        self.status_bar = ttk.Label(self.master, text="Ready", relief="sunken", anchor="w")
        self.status_bar.pack(side="bottom", fill="x", ipady=2)

    def browse_editor_executable(self):
        file_path = filedialog.askopenfilename(title="Select Editor Executable")
        if file_path:
            self.editor_executable_entry.delete(0, tk.END)
            self.editor_executable_entry.insert(0, file_path)

    def browse_input_file(self):
        file_path = filedialog.askopenfilename(title="Select Input Text File", filetypes=[("Text files", "*.txt"), ("All files", "*.*")])
        if file_path:
            self.input_file_entry.delete(0, tk.END)
            self.input_file_entry.insert(0, file_path)
            # 入力ファイルが選択されたら、出力ファイル名の初期値を自動生成して設定
            self.output_file_entry.delete(0, tk.END)
            self.output_file_entry.insert(0, self.generate_default_output_file_path(file_path))

    # 出力ファイルを選択する関数
    def browse_output_file(self):
        input_file_path = self.input_file_entry.get()
        initial_dir = os.path.dirname(input_file_path) if os.path.exists(input_file_path) else os.getcwd()
        initial_file = os.path.basename(self.generate_default_output_file_path(input_file_path)) if input_file_path else "output.txt"

        file_path = filedialog.asksaveasfilename(
            title="Select Output File",
            initialdir=initial_dir,
            initialfile=initial_file,
            defaultextension=".txt",
            filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
        )
        if file_path:
            self.output_file_entry.delete(0, tk.END)
            self.output_file_entry.insert(0, file_path)

    # ファイルをエディタで開く共通関数
    def view_file(self, file_path):
        editor_executable = self.editor_executable_entry.get()
        if not editor_executable or not os.path.exists(editor_executable):
            messagebox.showwarning("エディタ未設定", "エディタ実行可能ファイルが設定されていないか、存在しません。")
            return
        if not file_path or not os.path.exists(file_path):
            messagebox.showwarning("ファイルが見つかりません", f"指定されたファイルが見つかりません: {file_path}")
            return
        
        try:
            os.startfile(file_path) # Windows
            # For macOS/Linux, you might use:
            # import subprocess
            # subprocess.Popen([editor_executable, file_path])
        except Exception as e:
            messagebox.showerror("ファイルオープンエラー", f"ファイルをエディタで開けませんでした: {e}")

    # 出力ファイルパスを生成するヘルパー関数 (デフォルトパス用)
    def generate_default_output_file_path(self, input_file_path):
        if not input_file_path:
            return ""
        input_dir = os.path.dirname(input_file_path)
        file_body = os.path.splitext(os.path.basename(input_file_path))[0]
        return os.path.join(input_dir, f"{file_body}-output.txt")


    def update_model_list(self, event=None):
        selected_api = self.api_selection.get()
        if selected_api == "OpenAI":
            self.model_selection['values'] = openai_models
            self.model_selection.set(openai_model_default)
        elif selected_api == "Google":
            self.model_selection['values'] = google_models
            self.model_selection.set(google_model_default)

    def display_selected_role_prompt(self, event=None):
        selected_desc = self.role_combobox.get()
        for role in self.role_list:
            if role["desc"] == selected_desc:
                self.prompt_text_area.delete(1.0, tk.END)
                self.prompt_text_area.insert(tk.END, role["prompt"])
                break

    def display_selected_prompt_template(self, event=None):
        selected_desc = self.prompt_template_combobox.get()
        for prompt in self.prompt_list:
            if prompt["desc"] == selected_desc:
                current_prompt = self.prompt_text_area.get(1.0, tk.END).strip()
                if current_prompt:
                    self.prompt_text_area.insert(tk.END, "\n\n" + prompt["prompt"])
                else:
                    self.prompt_text_area.insert(tk.END, prompt["prompt"])
                break

    def on_gripper_press(self, event):
        # ドラッグ開始時のマウスのY座標とテキストエリアの現在の高さを記録
        self._start_y = event.y_root
        self._start_height_pixels = self.prompt_text_area.winfo_height() # ピクセル単位で高さを取得

    def on_gripper_drag(self, event):
        # マウスの移動量に基づいてテキストエリアの高さを調整
        delta_y = event.y_root - self._start_y
        new_height_pixels = self._start_height_pixels + delta_y
        
        # 最小の高さを設定 (例: 50ピクセル)
        min_height_pixels = 50 
        
        if self._line_height_pixels > 0: # ゼロ除算を避ける
            if new_height_pixels > min_height_pixels:
                # ピクセル単位の高さを行数に変換
                new_height_lines = int(new_height_pixels / self._line_height_pixels)
                # 最小行数を設定 (例: 1行)
                if new_height_lines < 1:
                    new_height_lines = 1
                self.prompt_text_area.configure(height=new_height_lines)

    # 【変更点】run_queryを別スレッドで実行するためのラッパー関数
    def start_query_thread(self):
        self.status_bar.config(text="Querying AI... Please wait.")
        self.query_button.config(state="disabled") # ボタンを無効化して多重クリックを防ぐ
        self.master.update_idletasks() # UIを即座に更新

        query_thread = threading.Thread(target=self.run_query_logic)
        query_thread.start()

    # 【変更点】実際のQuery AIロジック
    def run_query_logic(self):
        editor_executable = self.editor_executable_entry.get()
        input_file = self.input_file_entry.get()
        output_file_path = self.output_file_entry.get()
        
        selected_api = self.api_selection.get()
        selected_model = self.model_selection.get()
        prompt_content = self.prompt_text_area.get(1.0, tk.END).strip()
        
        try:
            temperature = float(self.temperature_entry.get())
            max_bytes = int(self.max_bytes_entry.get())
        except ValueError:
            self.status_bar.config(text="Error: Temperature and Max Bytes must be numbers.")
            messagebox.showerror("入力エラー", "TemperatureとMax Bytesは数値で入力してください。")
            self.query_button.config(state="normal")
            return

        if not output_file_path:
            self.status_bar.config(text="Error: Output file path not specified.")
            messagebox.showerror("ファイルエラー", "出力ファイルパスが指定されていません。")
            self.query_button.config(state="normal")
            return
        
        # 出力ディレクトリが存在することを確認、なければ作成
        output_dir = os.path.dirname(output_file_path)
        if output_dir and not os.path.exists(output_dir):
            try:
                os.makedirs(output_dir)
            except OSError as e:
                self.status_bar.config(text=f"Error creating output directory: {e}")
                messagebox.showerror("ディレクトリエラー", f"出力ディレクトリの作成に失敗しました: {e}")
                self.query_button.config(state="normal")
                return

        if not prompt_content:
            self.status_bar.config(text="Error: Prompt content is empty.")
            messagebox.showerror("プロンプトエラー", "プロンプトが入力されていません。")
            self.query_button.config(state="normal")
            return

        self.status_bar.config(text="Reading input file...")

        if "{{text}}" in prompt_content:
        # 入力ファイルをmax_bytesまで読み込む
            if not input_file or not os.path.exists(input_file):
                self.status_bar.config(text="Error: Input file not selected or does not exist.")
                messagebox.showerror("ファイルエラー", "入力ファイルが選択されていないか、存在しません。")
                self.query_button.config(state="normal")
                self.status_bar.config(text="")
                return

            try:
                with open(input_file, 'rb') as f:
                    content_bytes = f.read(max_bytes)
                    file_content = content_bytes.decode('utf-8', errors='ignore')
            except Exception as e:
                self.status_bar.config(text=f"Error reading input file: {e}")
                messagebox.showerror("ファイル読み込みエラー", f"入力ファイルの読み込み中にエラーが発生しました: {e}")
                self.query_button.config(state="normal")
                return

            full_prompt = prompt_content.replace("{{text}}", file_content)
        else:
            full_prompt = prompt_content
        
        self.status_bar.config(text="Sending query to AI...")
        try:
            if selected_api == "OpenAI":
                openai.api_key = OPENAI_API_KEY
                client = openai.OpenAI()
                response = client.chat.completions.create(
                    model=selected_model,
                    messages=[
                        {"role": "user", "content": full_prompt}
                    ],
                    temperature=temperature
                )
                ai_response = response.choices[0].message.content

            elif selected_api == "Google":
                genai.configure(api_key = GOOGLE_API_KEY)
                model = genai.GenerativeModel(selected_model)
                response = model.generate_content(
                    full_prompt,
                    generation_config=genai.types.GenerationConfig(temperature=temperature)
                )
                ai_response = response.text
            
            self.status_bar.config(text="Saving AI response...")
            with open(output_file_path, 'w', encoding='utf-8') as f:
                f.write(ai_response)

            self.status_bar.config(text=f"Success! Response saved to {output_file_path}")
            messagebox.showinfo("成功", f"AIからの応答が {output_file_path} に保存されました。")

            # 出力ファイルをエディタで開く
            self.view_file(output_file_path)

        except Exception as e:
            self.status_bar.config(text=f"API Error: {e}")
            messagebox.showerror("APIエラー", f"API呼び出し中にエラーが発生しました: {e}")
        finally:
            self.query_button.config(state="normal") # ボタンを有効に戻す
            if "Error" not in self.status_bar.cget("text"): # エラーがない場合のみReadyに戻す
                self.status_bar.config(text="Ready")

    def load_config(self):
        self.config = configparser.ConfigParser()
        if os.path.exists(self.config_file):
            self.config.read(self.config_file)
            if 'Settings' not in self.config:
                self.config['Settings'] = {}
        else:
            self.config['Settings'] = {}

    def save_config(self):
        self.config['Settings']['editor_executable'] = self.editor_executable_entry.get()
        self.config['Settings']['input_file'] = self.input_file_entry.get()
        self.config['Settings']['output_file'] = self.output_file_entry.get()
        self.config['Settings']['api_selection'] = self.api_selection.get()
        self.config['Settings']['model_selection'] = self.model_selection.get()
        self.config['Settings']['temperature'] = self.temperature_entry.get()
        self.config['Settings']['max_bytes'] = self.max_bytes_entry.get()
        self.config['Settings']['role_selection'] = self.role_combobox.get()
        self.config['Settings']['prompt_template_selection'] = self.prompt_template_combobox.get()
        self.config['Settings']['current_prompt_text'] = self.prompt_text_area.get(1.0, tk.END).strip()

        with open(self.config_file, 'w') as configfile:
            self.config.write(configfile)

    def load_initial_settings(self):
        settings = self.config['Settings']
        self.editor_executable_entry.insert(0, settings.get('editor_executable', editor_default))
        self.input_file_entry.insert(0, settings.get('input_file', ''))
        self.output_file_entry.insert(0, settings.get('output_file', ''))
        
        api_sel = settings.get('api_selection', 'OpenAI')
        self.api_selection.set(api_sel)
        self.update_model_list() # 読み込まれたAPIに基づいてモデルを更新
        self.model_selection.set(settings.get('model_selection', self.model_selection['values'][0] if self.model_selection['values'] else ''))

        self.temperature_entry.delete(0, tk.END)
        self.temperature_entry.insert(0, settings.get('temperature', '0.7'))
        self.max_bytes_entry.delete(0, tk.END)
        self.max_bytes_entry.insert(0, settings.get('max_bytes', '4096'))
        
        role_sel = settings.get('role_selection', '')
        self.role_combobox['values'] = [role["desc"] for role in self.role_list] # Comboboxの値を更新してから設定
        if role_sel in self.role_combobox['values']:
            self.role_combobox.set(role_sel)
        
        prompt_temp_sel = settings.get('prompt_template_selection', '')
        self.prompt_template_combobox['values'] = [prompt["desc"] for prompt in self.prompt_list] # Comboboxの値を更新してから設定
        if prompt_temp_sel in self.prompt_template_combobox['values']:
            self.prompt_template_combobox.set(prompt_temp_sel)

        current_prompt_text = settings.get('current_prompt_text', '')
        self.prompt_text_area.delete(1.0, tk.END)
        self.prompt_text_area.insert(tk.END, current_prompt_text)


    def on_closing(self):
        self.save_config()
        self.master.destroy()

if __name__ == "__main__":
    root = ThemedTk(theme="plastik") # "plastik", "breeze", "equilux" など、他のテーマも試せます
#    root = ThemedTk(theme="arc") # "plastik", "breeze", "equilux" など、他のテーマも試せます
    app = AIEditorApp(root)
    root.mainloop()    