import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog, scrolledtext
import os
import argparse
import chardet # Character encoding detection library
from tkinterdnd2 import DND_FILES, TkinterDnD # TkinterDnD2 library for drag-and-drop
import re # Regular expression module
import subprocess # For running external commands
from PIL import Image, ImageTk # Pillow for image handling
# Removed configparser import, as it will no longer be used for read_ini/write_ini directly.

# --- Constants ---
CONFIG_FILE = "iniedit.ini" # Configuration file for editor settings
TEXT_INI_FILE = "text.ini" # File for insertion data

# --- INI File Reading/Writing Functions (Custom for flat key=value files) ---
def read_ini(filepath):
    """
    Reads settings from a simple INI file (key=val format, no sections required).
    Attempts to auto-detect encoding, falls back to utf-8.
    """
    settings = {}
    if not os.path.exists(filepath):
        return settings

    # First, try to detect encoding
    detected_encoding = 'utf-8' # Default fallback
    try:
        with open(filepath, 'rb') as f_raw:
            raw_data = f_raw.read()
            result = chardet.detect(raw_data)
            if result['encoding'] and result['confidence'] > 0.8:
                detected_encoding = result['encoding']
    except Exception as e:
        print(f"Warning: Could not detect encoding for {filepath}: {e}")

    try:
        with open(filepath, 'r', encoding=detected_encoding, errors='ignore') as f:
            for line in f:
                line = line.strip()
                if not line or line.startswith('#'):  # Skip empty lines or comments
                    continue
                if '=' in line:
                    key, value = line.split('=', 1)
                    settings[key.strip()] = value.strip()
    except Exception as e:
        print(f"Error reading INI file {filepath} with encoding {detected_encoding}: {e}")
        messagebox.showerror("INIファイル読み込みエラー", f"'{os.path.basename(filepath)}' の読み込み中にエラーが発生しました:\n{e}")
    return settings

def write_ini(filepath, settings):
    """Writes settings to a simple INI file (key=val format)."""
    try:
        with open(filepath, 'w', encoding='utf-8') as f: # Always write out as UTF-8 for config
            for key, value in settings.items():
                f.write(f"{key}={value}\n")
    except Exception as e:
        print(f"Error writing INI file {filepath}: {e}")
        messagebox.showerror("INIファイル書き込みエラー", f"'{os.path.basename(filepath)}' の書き込み中にエラーが発生しました:\n{e}")

# --- Simple TOML/INI Parser Class (for main editor content) ---
class SimpleTomlParser:
    """
    A simplified TOML/INI parser.
    Does not strictly adhere to TOML specifications.
    Handles sections ([section]) and key=value pairs.
    Treats [section].key-like lines as sections for extraction.
    """
    def __init__(self):
        self.data = {} # Stores parsed data structured by section
        self.sections = []  # Stores (extracted_section_name, line_number, original_line_content)

    def parse(self, content):
        self.data = {}
        self.sections = []
        current_section = "" # Represents the current section key (e.g., "my_section", or "" for top-level)
        lines = content.splitlines()
        section_start_pattern = re.compile(r"^\s*\[")

        for i, line in enumerate(lines):
            stripped_line = line.strip()
            if stripped_line.startswith("#") or not stripped_line:
                continue

            # Detect section-like lines (starting with [ and no '=')
            if section_start_pattern.match(stripped_line) and "=" not in stripped_line:
                extracted_section_name = stripped_line # Full line for display in section dialog
                self.sections.append((extracted_section_name, i + 1, line))
                
                # For associating key-value pairs with a section, extract the inner part
                match_inner_section = re.match(r"^\[([^\]]+)\].*", stripped_line)
                parsed_section_key = match_inner_section.group(1).strip() if match_inner_section else ""
                
                # Initialize section in data structure for key-value pairs
                if parsed_section_key not in self.data:
                    self.data[parsed_section_key] = {}
                current_section = parsed_section_key # Set current section for subsequent keys
            
            # Detect key=value pairs
            elif "=" in stripped_line:
                try:
                    key, value = stripped_line.split("=", 1)
                    key = key.strip()
                    value = value.strip()
                    
                    # If no section is active (current_section is ""), put it under a default top-level section ''
                    target_section_data = self.data.setdefault(current_section, {})
                    target_section_data[key] = value

                except ValueError:
                    pass # Ignore malformed key-value lines
        return self.data, self.sections

# --- ToolTip Class ---
class ToolTip:
    """
    Provides a tooltip for a given widget.
    """
    def __init__(self, widget, text):
        self.widget = widget
        self.text = text
        self.tip_window = None
        self.id = None
        self.x = 0
        self.y = 0
        self.widget.bind("<Enter>", self.enter)
        self.widget.bind("<Leave>", self.leave)
        self.widget.bind("<ButtonPress>", self.leave)
        
    def enter(self, event=None):
        self.schedule()

    def leave(self, event=None):
        self.unschedule()
        self.hide()

    def schedule(self):
        self.unschedule()
        self.id = self.widget.after(500, self.show)

    def unschedule(self):
        if self.id:
            self.widget.after_cancel(self.id)
            self.id = None

    def show(self):
        if self.tip_window or not self.text:
            return
        x, y, cx, cy = self.widget.bbox("insert")
        x = x + self.widget.winfo_rootx() + 25
        y = y + cy + self.widget.winfo_rooty() + 20

        self.tip_window = tk.Toplevel(self.widget)
        self.tip_window.wm_overrideredirect(True)
        self.tip_window.wm_geometry(f"+{x}+{y}")

        label = tk.Label(self.tip_window, text=self.text, background="#ffffe0", relief=tk.SOLID, borderwidth=1,
                         font=("tahoma", "8", "normal"))
        label.pack(ipadx=1)

    def hide(self):
        if self.tip_window:
            self.tip_window.destroy()
            self.tip_window = None

# --- Main Editor Class ---
class TomlEditor:
    # --- Toolbar/Menu Helper Methods (Defined before __init__ for proper referencing) ---
    def _add_toolbar_buttons(self):
        self.icons = {}
        icon_size = (18, 18)

        icon_paths = {
            "new": "icons/new.png",
            "open": "icons/open.png",
            "save": "icons/save.png",
            "find": "icons/find.png",
            "replace": "icons/replace.png",
            "undo": "icons/undo.png",
            "redo": "icons/redo.png",
            "cut": "icons/cut.png",
            "copy": "icons/copy.png",
            "paste": "icons/paste.png",
            "sections": "icons/sections.png",
        }

        for name, path in icon_paths.items():
            path = os.path.join(self.script_dir, path)
            try:
                img = Image.open(path)
                img = img.resize(icon_size, Image.Resampling.LANCZOS)
                self.icons[name] = ImageTk.PhotoImage(img)
            except FileNotFoundError:
                self.icons[name] = None
            except Exception as e:
                print(f"Error loading icon {path}: {e}")
                self.icons[name] = None

        # 各ボタンにツールチップを設定
        # 新規ファイルボタン
        new_button = tk.Button(self.toolbar, command=self.new_file)
        if self.icons.get("new"):
            new_button.config(image=self.icons["new"]) # アイコンのみ
        else:
            new_button.config(text="新規", compound=tk.LEFT) # テキストとアイコン（左）
        new_button.pack(side=tk.LEFT, padx=2, pady=2)
        ToolTip(new_button, "新規ファイル (Ctrl+N)")


        # ファイルを開くボタン
        open_button = tk.Button(self.toolbar, command=self.open_file)
        if self.icons.get("open"):
            open_button.config(image=self.icons["open"])
        else:
            open_button.config(text="開く", compound=tk.LEFT)
        open_button.pack(side=tk.LEFT, padx=2, pady=2)
        ToolTip(open_button, "ファイルを開く (Ctrl+O)")

        # 保存ボタン
        save_button = tk.Button(self.toolbar, command=self.save_file)
        if self.icons.get("save"):
            save_button.config(image=self.icons["save"])
        else:
            save_button.config(text="保存", compound=tk.LEFT)
        save_button.pack(side=tk.LEFT, padx=2, pady=2)
        ToolTip(save_button, "ファイルを保存 (Ctrl+S)")
        
        # セパレータ
        tk.Frame(self.toolbar, width=1, bg="gray").pack(side=tk.LEFT, fill=tk.Y, padx=5)

        # 検索ボタン
        find_button = tk.Button(self.toolbar, command=self.find_text)
        if self.icons.get("find"):
            find_button.config(image=self.icons["find"])
        else:
            find_button.config(text="検索", compound=tk.LEFT)
        find_button.pack(side=tk.LEFT, padx=2, pady=2)
        ToolTip(find_button, "テキストを検索 (Ctrl+F)")

        # 置換ボタン
        replace_button = tk.Button(self.toolbar, command=self.replace_text)
        if self.icons.get("replace"):
            replace_button.config(image=self.icons["replace"])
        else:
            replace_button.config(text="置換", compound=tk.LEFT)
        replace_button.pack(side=tk.LEFT, padx=2, pady=2)
        ToolTip(replace_button, "テキストを置換 (Ctrl+H)")

        # セパレータ
        tk.Frame(self.toolbar, width=1, bg="gray").pack(side=tk.LEFT, fill=tk.Y, padx=5)
        
        # 元に戻すボタン
        undo_button = tk.Button(self.toolbar, command=self.text_area.edit_undo)
        if self.icons.get("undo"):
            undo_button.config(image=self.icons["undo"])
        else:
            undo_button.config(text="元に戻す", compound=tk.LEFT)
        undo_button.pack(side=tk.LEFT, padx=2, pady=2)
        ToolTip(undo_button, "元に戻す (Ctrl+Z)")

        # やり直しボタン
        redo_button = tk.Button(self.toolbar, command=self.text_area.edit_redo)
        if self.icons.get("redo"):
            redo_button.config(image=self.icons["redo"])
        else:
            redo_button.config(text="やり直し", compound=tk.LEFT)
        redo_button.pack(side=tk.LEFT, padx=2, pady=2)
        ToolTip(redo_button, "やり直し (Ctrl+Y)")
        
        tk.Frame(self.toolbar, width=1, bg="gray").pack(side=tk.LEFT, fill=tk.Y, padx=5)

        # 切り取りボタン
        cut_button = tk.Button(self.toolbar, command=lambda: self.text_area.event_generate("<<Cut>>"))
        if self.icons.get("cut"):
            cut_button.config(image=self.icons["cut"])
        else:
            cut_button.config(text="切り取り", compound=tk.LEFT)
        cut_button.pack(side=tk.LEFT, padx=2, pady=2)
        ToolTip(cut_button, "切り取り (Ctrl+X)")

        # コピーボタン
        copy_button = tk.Button(self.toolbar, command=lambda: self.text_area.event_generate("<<Copy>>"))
        if self.icons.get("copy"):
            copy_button.config(image=self.icons["copy"])
        else:
            copy_button.config(text="コピー", compound=tk.LEFT)
        copy_button.pack(side=tk.LEFT, padx=2, pady=2)
        ToolTip(copy_button, "コピー (Ctrl+C)")

        # 貼り付けボタン
        paste_button = tk.Button(self.toolbar, command=lambda: self.text_area.event_generate("<<Paste>>"))
        if self.icons.get("paste"):
            paste_button.config(image=self.icons["paste"])
        else:
            paste_button.config(text="貼り付け", compound=tk.LEFT)
        paste_button.pack(side=tk.LEFT, padx=2, pady=2)
        ToolTip(paste_button, "貼り付け (Ctrl+V)")

        # セパレータ
        tk.Frame(self.toolbar, width=1, bg="gray").pack(side=tk.LEFT, fill=tk.Y, padx=5)
        
        # セクションダイアログボタン
        section_button = tk.Button(self.toolbar, command=self.show_section_dialog)
        if self.icons.get("sections"):
            section_button.config(image=self.icons["sections"])
        else:
            section_button.config(text="セクション", compound=tk.LEFT)
        section_button.pack(side=tk.LEFT, padx=2, pady=2)
        ToolTip(section_button, "セクションダイアログを表示")

    def _bind_shortcuts(self):
        self.master.bind("<Control-n>", lambda event: self.new_file())
        self.master.bind("<Control-o>", lambda event: self.open_file())
        self.master.bind("<Control-s>", lambda event: self.save_file())
        self.master.bind("<Control-Shift-S>", lambda event: self.save_file_as())
        self.master.bind("<Control-q>", lambda event: self.master.quit())

        self.master.bind("<Control-f>",    lambda event: self.find_text())
        self.master.bind("<F3>",           lambda event: self.find_next_menu())
        self.master.bind("<Control-Down>", lambda event: self.find_next_menu())
        self.master.bind("<Shift-F3>",     lambda event: self.find_previous_menu())
        self.master.bind("<Control-Up>",   lambda event: self.find_previous_menu())
        self.master.bind("<Control-h>", lambda event: self.replace_text())
        self.master.bind("<Control-z>", lambda event: self.text_area.edit_undo())
        self.master.bind("<Control-y>", lambda event: self.text_area.edit_redo())

    def __init__(self, master, initial_file=None, initial_line=None, initial_col=None):
        self.master = master
        master.title("Inifile Editor")

        self.last_search_term = ""
        self.last_search_is_regex = False

        self.script_dir = os.path.dirname(os.path.abspath(__file__))
        self.config_filepath = os.path.join(self.script_dir, CONFIG_FILE)
        self.text_ini_filepath = os.path.join(self.script_dir, TEXT_INI_FILE)

        # Load editor settings (using the custom read_ini)
        self.settings = read_ini(self.config_filepath)
        self._apply_saved_settings() # Apply main window geometry from settings

        font_family = self.settings.get('font_family', 'Courier New')
        try:
            font_size = int(self.settings.get('font_size', 10))
        except ValueError:
            font_size = 10 # 数値でない場合はデフォルト値を使用

        editor_font = (font_family, font_size)
    
        self.current_file = initial_file
        self.file_encoding = "auto" 

        # SimpleTomlParser instance for main editor content
        self.parser = SimpleTomlParser()
        # Data parsed from the main editor content
        self.parsed_data = {} 
        # Data loaded from text.ini for insertion menu
        self.text_ini_data = {} 

        # ---------------------------------------------
        # Create all widgets first, then pack them later.
        # This prevents AttributeError due to premature referencing.
        # ---------------------------------------------

        # --- Toolbar Frame ---
        self.toolbar = tk.Frame(master, bd=1, relief=tk.RAISED)

        # --- Text Area ---
        self.text_area = scrolledtext.ScrolledText(master, wrap=tk.WORD, undo=True, font=editor_font)
        self.text_area.bind("<<Modified>>", self.on_text_modified)
        self.text_area.bind("<KeyRelease>", self.update_status_bar)
        self.text_area.bind("<ButtonRelease-1>", self.update_status_bar)

        # Drag & Drop Setup
        self.text_area.drop_target_register(DND_FILES)
        self.text_area.dnd_bind("<<Drop>>", self.handle_drop)
        
        # --- Context Menu Setup (Right-click menu) ---
        self.context_menu = tk.Menu(self.text_area, tearoff=0)
        self.text_area.bind("<Button-3>", self._show_context_menu) # Bind right-click

        # --- Status Bar ---
        self.status_bar = tk.Label(master, text="行: 1, 桁: 1 | 文字コード: 自動判別", bd=1, relief=tk.SUNKEN, anchor=tk.W)
        
        # Add toolbar buttons (text_area is created now, so safe to reference)
        self._add_toolbar_buttons() # <= This call is now safe because _add_toolbar_buttons is above __init__

        # --- Menu Bar ---
        self.menubar = tk.Menu(master)
        master.config(menu=self.menubar)

        # File Menu
        file_menu = tk.Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label="ファイル", menu=file_menu)
        file_menu.add_command(label="新規", command=self.new_file, accelerator="Ctrl+N")
        file_menu.add_command(label="開く", command=self.open_file, accelerator="Ctrl+O")
        file_menu.add_command(label="保存", command=self.save_file, accelerator="Ctrl+S")
        file_menu.add_command(label="名前を付けて保存...", command=self.save_file_as, accelerator="Ctrl+Shift+S")
        file_menu.add_separator()
        file_menu.add_command(label="ファイル場所を開く", command=self._open_file_location)
        file_menu.add_command(label="ターミナルを開く", command=self._open_terminal)
        file_menu.add_separator()

        # Encoding Submenu
        self.encoding_menu = tk.Menu(file_menu, tearoff=0)
        file_menu.add_cascade(label="文字コード", menu=self.encoding_menu)
        self.selected_encoding = tk.StringVar(value="auto") # Default to auto-detect
        self._add_encoding_options()

        file_menu.add_separator()
        file_menu.add_command(label="終了", command=master.quit, accelerator="Ctrl+Q")

        # Edit Menu
        edit_menu = tk.Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label="編集", menu=edit_menu)
        edit_menu.add_command(label="検索", command=self.find_text, accelerator="Ctrl+F") 
        edit_menu.add_command(label="次を検索", command=self.find_next_menu, accelerator="F3")
        edit_menu.add_command(label="前を検索", command=self.find_previous_menu, accelerator="Shift+F3")
        edit_menu.add_command(label="置換", command=self.replace_text, accelerator="Ctrl+H")

        edit_menu.add_separator()
        edit_menu.add_command(label="元に戻す", command=self.text_area.edit_undo, accelerator="Ctrl+Z")
        edit_menu.add_command(label="やり直し", command=self.text_area.edit_redo, accelerator="Ctrl+Y")
        edit_menu.add_separator()
        edit_menu.add_command(label="文書先頭へジャンプ", command=self._jump_to_start)
        edit_menu.add_command(label="文書末へジャンプ", command=self._jump_to_end)
        edit_menu.add_command(label="行番号を指定してジャンプ", command=self._jump_to_line)
        edit_menu.add_separator()
        edit_menu.add_command(label="切り取り", command=lambda: self.text_area.event_generate("<<Cut>>"), accelerator="Ctrl+X")
        edit_menu.add_command(label="コピー", command=lambda: self.text_area.event_generate("<<Copy>>"), accelerator="Ctrl+C")
        edit_menu.add_command(label="貼り付け", command=lambda: self.text_area.event_generate("<<Paste>>"), accelerator="Ctrl+V")

        # View Menu
        view_menu = tk.Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label="表示", menu=view_menu)
        self.word_wrap_var = tk.BooleanVar(value=True)
        view_menu.add_checkbutton(label="折り返し", onvalue=True, offvalue=False, variable=self.word_wrap_var, command=self.toggle_word_wrap)

        # Tools Menu
        tools_menu = tk.Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label="ツール", menu=tools_menu)
        tools_menu.add_command(label="セクションダイアログ", command=self.show_section_dialog)
        self._add_external_commands_to_menu(tools_menu) # Add external commands

        # ---------------------------------------------
        # Pack all widgets in the correct order
        # ---------------------------------------------
        self.toolbar.pack(side=tk.TOP, fill=tk.X) # Toolbar at the very top
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X) # Status bar at the very bottom
        self.text_area.pack(expand=True, fill="both") # Text area fills remaining space

        # Shortcode key bindings
        self._bind_shortcuts()

        # Update status bar initially
        self.update_status_bar() 

        # Initial file load and cursor position setup
        if self.current_file:
            self._load_initial_file(self.current_file)
            if initial_line:
                self.master.after(100, lambda: self._jump_to_line_col(initial_line, initial_col))
        else:
            self.master.title("Inifile Editor - 無題")
            self.status_bar.config(text=f"行: 1, 桁: 1 | 文字コード: 自動判別")
            # For new empty file, initialize parsed_data (empty)
            self.parsed_data, _ = self.parser.parse(self.text_area.get(1.0, tk.END))

        # Load text.ini data for context menu after all components are ready
        self._load_text_ini_data() 

        # Register handler for window closing
        self.master.protocol("WM_DELETE_WINDOW", self._on_closing)

        # Variable for section dialog geometry
        self.section_dialog_geometry = None

    def find_next_menu(self):
        """Menu command for Find Next."""
        if self.last_search_term:
            self._perform_find_next(self.last_search_term, self.last_search_is_regex)
        else:
            self.find_text() # 検索履歴がなければダイアログを開く

    def find_previous_menu(self):
        """Menu command for Find Previous."""
        if self.last_search_term:
            self._perform_find_previous(self.last_search_term, self.last_search_is_regex)
        else:
            self.find_text() # 検索履歴がなければダイアログを開く
 
    # --- Utility Methods ---
    def _add_external_commands_to_menu(self, menu):
        """Loads external commands from iniedit.ini and adds them to the Tools menu."""
        added_any = False
        for i in range(1, 10): # Check for external_cmd1 to external_cmd9
            cmd_name_key = f'external_cmd{i}_name'
            cmd_path_key = f'external_cmd{i}_path'
            
            cmd_name = self.settings.get(cmd_name_key)
            cmd_path = self.settings.get(cmd_path_key)

            if cmd_name and cmd_path:
                if not added_any:
                    menu.add_separator() # Add a separator before the first external command
                    added_any = True
                menu.add_command(label=cmd_name, command=lambda cmd=cmd_path: self._run_external_command(cmd))
        if added_any:
            menu.add_separator() # Add a separator after the last external command

    def _run_external_command(self, command_template):
        """Executes an external command, replacing {{file_path}} with the current file."""
        if not self.current_file or not os.path.exists(self.current_file):
            messagebox.showwarning("コマンド実行エラー", "ファイルが保存されていないか、存在しません。")
            return
        
        # Replace the placeholder with the current file path, enclosed in quotes
        command = command_template.replace("{{file_path}}", f'"{self.current_file}"')
        
        try:
            subprocess.Popen(command, shell=True, cwd=os.path.dirname(self.current_file))
        except Exception as e:
            messagebox.showerror("コマンド実行エラー", f"コマンドの実行に失敗しました:\n{command}\nエラー: {e}")

    def _open_file_location(self):
        """Opens the directory of the current file in the system file manager."""
        if not self.current_file or not os.path.exists(self.current_file):
            messagebox.showwarning("ファイル場所を開く", "ファイルが保存されていないか、存在しません。")
            return

        directory = os.path.dirname(self.current_file)
        file_manager_path = self.settings.get('file_manager_path')

        if not file_manager_path:
            messagebox.showwarning("設定不足", "iniedit.iniに 'file_manager_path' が設定されていません。")
            return
        
        try:
            if os.name == 'nt':  # Windows
                subprocess.Popen([file_manager_path, directory])
            elif os.uname().sysname == 'Darwin':  # macOS
                subprocess.Popen([file_manager_path, directory])
            else:  # Linux/Unix-like
                subprocess.Popen([file_manager_path, directory])
        except Exception as e:
            messagebox.showerror("エラー", f"ファイラーの起動に失敗しました:\n{file_manager_path} {directory}\nエラー: {e}")

    def _open_terminal(self):
        """Opens a terminal in the directory of the current file."""
        if not self.current_file or not os.path.exists(self.current_file):
            messagebox.showwarning("ターミナルを開く", "ファイルが保存されていないか、存在しません。")
            return

        directory = os.path.dirname(self.current_file)
        terminal_path = self.settings.get('terminal_path')

        if not terminal_path:
            messagebox.showwarning("設定不足", "iniedit.iniに 'terminal_path' が設定されていません。")
            return

        try:
            subprocess.Popen(terminal_path, shell=True, cwd=directory)
        except Exception as e:
            messagebox.showerror("エラー", f"ターミナルの起動に失敗しました:\n{terminal_path} (cwd: {directory})\nエラー: {e}")

    def _load_text_ini_data(self):
        """Loads data from text.ini into self.text_ini_data for context menu insertion."""
        self.text_ini_data = read_ini(self.text_ini_filepath)
#        messagebox.showinfo("情報", f"{TEXT_INI_FILE} を再読み込みしました。", parent=self.master)

    def _apply_saved_settings(self):
        """Applies saved window settings (position and size) from iniedit.ini."""
        try:
            width = int(self.settings.get('main_width', 800))
            height = int(self.settings.get('main_height', 600))
            pos_x = int(self.settings.get('main_pos_x', 100))
            pos_y = int(self.settings.get('main_pos_y', 100))
            self.master.geometry(f"{width}x{height}+{pos_x}+{pos_y}")
        except ValueError:
            self.master.geometry("800x600+100+100")

    def _on_closing(self):
        """Handles application closing, saving settings and prompting to save modified files."""
        if self.text_area.edit_modified():
            if messagebox.askyesno("未保存の変更", "ファイルを保存しますか？"):
                self.save_file()
        
        # Record main window's current position and size
        self.settings['main_width'] = self.master.winfo_width()
        self.settings['main_height'] = self.master.winfo_height()
        self.settings['main_pos_x'] = self.master.winfo_x()
        self.settings['main_pos_y'] = self.master.winfo_y()

        write_ini(self.config_filepath, self.settings)
        self.master.destroy()

    def on_text_modified(self, event=None):
        """Updates the window title to indicate unsaved changes (*)."""
        if self.text_area.edit_modified():
            if self.current_file:
                self.master.title(f"Inifile Editor - {os.path.basename(self.current_file)}*")
            else:
                self.master.title("Inifile Editor - 無題*")
        else:
            if self.current_file:
                self.master.title(f"Inifile Editor - {os.path.basename(self.current_file)}")
            else:
                self.master.title("Inifile Editor - 無題")
        self.text_area.edit_modified(False) # Reset the modified flag after checking

    def update_status_bar(self, event=None):
        """Updates the status bar with current cursor position and file encoding."""
        line, char = self.text_area.index(tk.INSERT).split('.')
        encoding_display = self.file_encoding.upper() if self.file_encoding != "auto" else "自動判別"
        self.status_bar.config(text=f"行: {line}, 桁: {int(char) + 1} | 文字コード: {encoding_display}")

    # --- Context Menu Logic ---
    def _show_context_menu(self, event):
        """Displays the right-click context menu dynamically."""
        self.context_menu.delete(0, tk.END) # Clear existing items from main context menu

        # Standard Edit Menu Items
        self.context_menu.add_command(label="切り取り", command=lambda: self.text_area.event_generate("<<Cut>>"))
        self.context_menu.add_command(label="コピー", command=lambda: self.text_area.event_generate("<<Copy>>"))
        self.context_menu.add_command(label="貼り付け", command=lambda: self.text_area.event_generate("<<Paste>>"))
        self.context_menu.add_separator()

        # Dynamic "text.iniから挿入" Submenu
        insert_ini_menu = tk.Menu(self.context_menu, tearoff=0)
        self.context_menu.add_cascade(label=f"{TEXT_INI_FILE}から挿入", menu=insert_ini_menu)

        if self.text_ini_data:
            # Sort keys alphabetically for cleaner display
            for key in self.text_ini_data.keys():
#            for key in sorted(self.text_ini_data.keys()):
                value = self.text_ini_data[key]
                insert_ini_menu.add_command(label=f"{key}", command=lambda v=value: self._insert_value_from_ini(v))
#                insert_ini_menu.add_command(label=f"{key}={value}", command=lambda v=value: self._insert_value_from_ini(v))
        else:
            insert_ini_menu.add_command(label=f"({TEXT_INI_FILE}が空か見つかりません)", state=tk.DISABLED)
        
        self.context_menu.add_command(label=f"{TEXT_INI_FILE}再読み込み", command=self._load_text_ini_data) # Reload command

        try:
            self.context_menu.tk_popup(event.x_root, event.y_root)
        finally:
            self.context_menu.grab_release()

    def _insert_value_from_ini(self, value):
        """Inserts a value from text.ini at the current cursor position."""
        value = value.replace(r'\n', "\n")
        self.text_area.insert(tk.INSERT, value)
        self.text_area.edit_modified(True) # Mark document as modified
        self.on_text_modified() # Update title bar

    # --- Jump Functions ---
    def _jump_to_start(self):
        """Jumps the cursor to the beginning of the document."""
        self.text_area.mark_set(tk.INSERT, "1.0")
        self.text_area.see(tk.INSERT)
        self.update_status_bar()

    def _jump_to_end(self):
        """Jumps the cursor to the end of the document."""
        self.text_area.mark_set(tk.INSERT, tk.END)
        self.text_area.see(tk.INSERT)
        self.update_status_bar()

    def _jump_to_line(self):
        """Prompts for a line number and jumps to it."""
        line_str = simpledialog.askstring("行へジャンプ", "ジャンプする行番号を入力してください:", parent=self.master)
        if line_str:
            try:
                line_number = int(line_str)
                self._jump_to_line_col(line_number, 0) # Jump to column 0 (start of line)
            except ValueError:
                messagebox.showerror("入力エラー", "有効な行番号を入力してください。")

    def _jump_to_line_col(self, line, col):
        """Jumps to a specific line and column number (internal use)."""
        if line is None:
            return

        if line < 1:
            line = 1
        
        last_line = int(self.text_area.index("end-1c").split('.')[0])
        if line > last_line:
            line = last_line

        self.text_area.mark_set(tk.INSERT, f"{line}.{col}")
        self.text_area.see(tk.INSERT)
        self.update_status_bar()

    # --- Encoding Options ---
    def _add_encoding_options(self):
        """Populates the encoding submenu with common options."""
        encodings = ["utf-8", "shift_jis", "cp932", "euc_jp", "iso2022_jp"]
        self.encoding_menu.add_radiobutton(label="自動判別", value="auto",
                                            variable=self.selected_encoding,
                                            command=lambda: self._on_encoding_selected(is_auto=True))
        self.encoding_menu.add_separator()
        for enc in encodings:
            self.encoding_menu.add_radiobutton(label=enc.upper(), value=enc,
                                                variable=self.selected_encoding,
                                                command=self._on_encoding_selected)
    
    def _on_encoding_selected(self, is_auto=False):
        """Handles encoding selection from the menu."""
        if not is_auto:
            self.file_encoding = self.selected_encoding.get()
        else:
            self.file_encoding = "auto"
        self.update_status_bar()

    def _load_initial_file(self, file_path):
        """Loads the specified file initially, setting up title and status."""
        self.current_file = file_path
        self.selected_encoding.set("auto") # Force auto-detect for initial load
        self._read_file_with_encoding_detection(file_path)
        self.master.title(f"Inifile Editor - {os.path.basename(file_path)}")
        self.text_area.edit_reset() 
        self.update_status_bar()

    def _read_file_with_encoding_detection(self, file_path):
        """Reads a file, attempting to detect encoding if 'auto' is selected."""
        try:
            with open(file_path, "rb") as f:
                raw_data = f.read()

            if self.selected_encoding.get() == "auto":
                result = chardet.detect(raw_data)
                detected_encoding = result['encoding']
                confidence = result['confidence']

                if detected_encoding and confidence > 0.8:
                    self.file_encoding = detected_encoding
                else:
                    self.file_encoding = "utf-8"
                    messagebox.showwarning("文字コード判別",
                                           f"文字コードを自動判別できませんでした。UTF-8で開きます。\n"
                                           f"検出結果: {detected_encoding} (確信度: {confidence:.2f})")
            else:
                self.file_encoding = self.selected_encoding.get()

            content = raw_data.decode(self.file_encoding)
            self.text_area.delete(1.0, tk.END)
            self.text_area.insert(tk.END, content)
            self.selected_encoding.set(self.file_encoding) # Update menu selection to actual encoding
            
            # Re-parse editor content after loading
            self.parsed_data, _ = self.parser.parse(content)

        except UnicodeDecodeError:
            messagebox.showerror("文字コードエラー",
                                   f"選択された文字コード '{self.file_encoding}' でファイルをデコードできませんでした。\n"
                                   "別の文字コードを試してください。")
            self.text_area.delete(1.0, tk.END)
            self.file_encoding = "utf-8" # Reset on error
            self.selected_encoding.set(self.file_encoding)
            self.parsed_data = {} # Clear parsed data on error
        except Exception as e:
            messagebox.showerror("エラー", f"ファイルの読み込み中にエラーが発生しました:\n{e}")
            self.text_area.delete(1.0, tk.END)
            self.file_encoding = "utf-8"
            self.selected_encoding.set(self.file_encoding)
            self.parsed_data = {} # Clear parsed data on error


    def handle_drop(self, event):
        """Handles file drag-and-drop events."""
        file_path = event.data.strip('{}')
        if ' ' in file_path and os.name == 'nt':
             file_paths = file_path.split('} {')
             file_path = file_paths[0].strip('{}')
        
        if os.path.isfile(file_path):
            if self.text_area.edit_modified():
                if messagebox.askyesno("未保存の変更", "現在のファイルを保存しますか？"):
                    self.save_file()
            self._load_initial_file(file_path) # Use common loading method
        else:
            messagebox.showwarning("D&Dエラー", "ドロップされたファイルが無効です。")


    def new_file(self):
        """Creates a new empty document."""
        if self.text_area.edit_modified():
            if not messagebox.askyesno("未保存の変更", "現在のファイルを保存しますか？"):
                self.text_area.delete(1.0, tk.END)
                self.current_file = None
                self.master.title("Inifile Editor - 無題")
                self.text_area.edit_reset()
                self.file_encoding = "auto" 
                self.selected_encoding.set(self.file_encoding)
                self.update_status_bar()
                self.parsed_data = {} # Clear parsed data
                return

        self.text_area.delete(1.0, tk.END)
        self.current_file = None
        self.master.title("Inifile Editor - 無題")
        self.text_area.edit_reset()
        self.file_encoding = "auto" 
        self.selected_encoding.set(self.file_encoding)
        self.update_status_bar()
        self.parsed_data = {} # Clear parsed data


    def open_file(self):
        """Opens an existing file from a dialog."""
        if self.text_area.edit_modified():
            if messagebox.askyesno("未保存の変更", "現在のファイルを保存しますか？"):
                self.save_file()

        file_types = [
            ("INI files", "*.ini"), 
            ("TOML files", "*.toml"),
            ("Text files", "*.txt"),
            ("All files", "*.*")
        ]
        file_path = filedialog.askopenfilename(filetypes=file_types)
        if file_path:
            self._load_initial_file(file_path) # Use common loading method
            self.current_file = file_path
            self.master.title(f"Inifile Editor - {os.path.basename(file_path)}")
            self.text_area.edit_reset()
        self.update_status_bar()

    def save_file(self):
        """Saves the current file, or calls save_file_as if no file is open."""
        if self.current_file:
            try:
                encoding_to_save = self.file_encoding if self.file_encoding != "auto" else "utf-8"
#                with open(self.current_file, "w", encoding=encoding_to_save) as file:
#                    file.write(self.text_area.get(1.0, tk.END))
                with open(self.current_file, "w", encoding=encoding_to_save, newline='') as file:
# TkinterのTextウィジェットが返す末尾の余分な改行を削除
                    content = self.text_area.get(1.0, tk.END).rstrip('\n')
                    file.write(content)

                self.master.title(f"Inifile Editor - {os.path.basename(self.current_file)}")
                self.text_area.edit_reset()
                # Re-parse editor content after saving
                self.parsed_data, _ = self.parser.parse(self.text_area.get(1.0, tk.END))
            except Exception as e:
                messagebox.showerror("エラー", f"ファイルの保存中にエラーが発生しました:\n{e}")
        else:
            self.save_file_as()
        self.update_status_bar()

    def save_file_as(self):
        """Saves the current file with a new name/location."""
        file_types = [
            ("INI files", "*.ini"),
            ("TOML files", "*.toml"), # Default extension for save dialog
            ("Text files", "*.txt"),
            ("All files", "*.*")
        ]
        file_path = filedialog.asksaveasfilename(defaultextension=".toml", filetypes=file_types)
        if file_path:
            try:
                encoding_to_save = self.file_encoding if self.file_encoding != "auto" else "utf-8"
#                with open(file_path, "w", encoding=encoding_to_save) as file:
#                    file.write(self.text_area.get(1.0, tk.END))
                with open(file_path, "w", encoding=encoding_to_save, newline='') as file:
    # TkinterのTextウィジェットが返す末尾の余分な改行を削除
                    content = self.text_area.get(1.0, tk.END).rstrip('\n')
                    file.write(content)
    
                self.current_file = file_path
                self.master.title(f"Inifile Editor - {os.path.basename(file_path)}")
                self.text_area.edit_reset()
                # Re-parse editor content after saving
                self.parsed_data, _ = self.parser.parse(self.text_area.get(1.0, tk.END))
            except Exception as e:
                messagebox.showerror("エラー", f"ファイルの保存中にエラーが発生しました:\n{e}")
        self.update_status_bar()

    # --- Find/Replace Functions ---
    def find_text(self):
        """Displays the Find dialog."""
    # 既にダイアログが存在し、表示されている場合は新しく作らずに手前に表示する
        if hasattr(self, 'find_dialog') and self.find_dialog.winfo_exists():
            self.find_dialog.lift()
            self.find_dialog.focus_set()
            return

        self.find_dialog = tk.Toplevel(self.master)
        self.find_dialog.title("検索")
#        self.find_dialog.resizable(False, False)

        self.find_dialog.grid_columnconfigure(1, weight=1)
        tk.Label(self.find_dialog, text="検索:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.find_entry = tk.Entry(self.find_dialog)
        self.find_entry.grid(row=0, column=1, columnspan=2, padx=5, pady=5, sticky="we") # we = west-east
        self.find_entry.focus_set()

    # 正規表現チェックボックス
        self.regex_var_find = tk.BooleanVar(value=False)
        tk.Checkbutton(self.find_dialog, text="正規表現", variable=self.regex_var_find).grid(row=1, column=0, columnspan=3, sticky=tk.W, padx=5)

    # ボタンを配置するフレーム
        button_frame = tk.Frame(self.find_dialog)
        button_frame.grid(row=2, column=0, columnspan=3, pady=5)

    # 各ボタン
        tk.Button(button_frame, text="前を検索 (Enter)", command=self._find_previous).pack(side=tk.LEFT, padx=5)
        tk.Button(button_frame, text="次を検索 (Shift+Enter)", command=self._find_next).pack(side=tk.LEFT, padx=5)
        tk.Button(button_frame, text="閉じる (ESC)", command=self.find_dialog.destroy).pack(side=tk.LEFT, padx=5)

        self.find_entry.bind("<Return>", lambda event: self._find_next())
        self.find_entry.bind("<Shift-Return>", lambda event: self._find_previous())
        self.find_entry.bind("<Escape>", lambda event: self.find_dialog.destroy())

    def _find_next(self):
        """Finds and highlights the next occurrence from the dialog."""
    # ダイアログから条件を取得
        self.last_search_term = self.find_entry.get()
        self.last_search_is_regex = self.regex_var_find.get()
    
    # 共通メソッドを呼び出し
        self._perform_find_next(self.last_search_term, self.last_search_is_regex)

    def _find_previous(self):
        """Finds and highlights the previous occurrence from the dialog."""
    # ダイアログから条件を取得
        self.last_search_term = self.find_entry.get()
        self.last_search_is_regex = self.regex_var_find.get()
    
    # 共通メソッドを呼び出し
        self._perform_find_previous(self.last_search_term, self.last_search_is_regex)

    def _perform_find_next(self, search_text, is_regex):
        """指定された条件で前方検索を実行する共通メソッド"""
        if not search_text:
            return

        start_index = self.text_area.index(tk.INSERT)
        self.text_area.tag_remove("found", "1.0", tk.END)

        try:
            pos = self.text_area.search(search_text, start_index, stopindex=tk.END, regexp=is_regex, nocase=True)
            if not pos:
                pos = self.text_area.search(search_text, "1.0", stopindex=tk.END, regexp=is_regex, nocase=True)
                if not pos:
                    messagebox.showinfo("検索", "テキストが見つかりませんでした。")
                    return

            if is_regex:
                line_content_after_pos = self.text_area.get(pos, f"{pos} lineend")
                match_obj = re.match(search_text, line_content_after_pos, re.IGNORECASE)
                match_length = match_obj.end() - match_obj.start() if match_obj else len(search_text)
            else:
                match_length = len(search_text)
        
            end_pos = f"{pos}+{match_length}c"
            self.text_area.tag_add("found", pos, end_pos)
            self.text_area.tag_config("found", background="yellow")
            self.text_area.mark_set(tk.INSERT, end_pos)
            self.text_area.see(tk.INSERT)
        except re.error as e:
            messagebox.showerror("正規表現エラー", f"無効な正規表現です:\n{e}")

    def _perform_find_previous(self, search_text, is_regex):
        """指定された条件で後方検索を実行する共通メソッド"""
        if not search_text:
            return

        start_index = self.text_area.index(tk.INSERT)
        self.text_area.tag_remove("found", "1.0", tk.END)

        try:
            pos = self.text_area.search(search_text, start_index, stopindex="1.0", backwards=True, regexp=is_regex, nocase=True)
            if not pos:
                pos = self.text_area.search(search_text, tk.END, stopindex="1.0", backwards=True, regexp=is_regex, nocase=True)
                if not pos:
                    messagebox.showinfo("検索", "テキストが見つかりませんでした。")
                    return
        
            if is_regex:
                line_content_after_pos = self.text_area.get(pos, f"{pos} lineend")
                match_obj = re.match(search_text, line_content_after_pos, re.IGNORECASE)
                match_length = match_obj.end() - match_obj.start() if match_obj else len(search_text)
            else:
                match_length = len(search_text)

            end_pos = f"{pos}+{match_length}c"
            self.text_area.tag_add("found", pos, end_pos)
            self.text_area.tag_config("found", background="yellow")
            self.text_area.mark_set(tk.INSERT, pos)
            self.text_area.see(tk.INSERT)
        except re.error as e:
            messagebox.showerror("正規表現エラー", f"無効な正規表現です:\n{e}")

    def replace_text(self):
        """Displays the Replace dialog."""
        self.replace_dialog = tk.Toplevel(self.master)
        self.replace_dialog.title("置換")
        self.replace_dialog.transient(self.master)
        self.replace_dialog.grab_set()
#        self.replace_dialog.resizable(False, False)

        self.replace_dialog.grid_columnconfigure(1, weight=1)
        tk.Label(self.replace_dialog, text="検索:").grid(row=0, column=0, padx=5, pady=5)
        self.replace_find_entry = tk.Entry(self.replace_dialog)
        self.replace_find_entry.grid(row=0, column=1, padx=5, pady=5, sticky="we")
        self.replace_find_entry.focus_set()

        tk.Label(self.replace_dialog, text="置換:").grid(row=1, column=0, padx=5, pady=5)
        self.replace_entry = tk.Entry(self.replace_dialog)
        self.replace_entry.grid(row=1, column=1, padx=5, pady=5, sticky="we")

        self.regex_var_replace = tk.BooleanVar(value=False)
        tk.Checkbutton(self.replace_dialog, text="正規表現", variable=self.regex_var_replace).grid(row=2, column=1, sticky=tk.W, padx=5)

        tk.Button(self.replace_dialog, text="次を検索", command=self._find_next_for_replace).grid(row=0, column=2, padx=5, pady=5)
        tk.Button(self.replace_dialog, text="置換", command=self._do_replace).grid(row=1, column=2, padx=5, pady=5)
        tk.Button(self.replace_dialog, text="すべて置換", command=self._do_replace_all).grid(row=2, column=2, padx=5, pady=5)
        tk.Button(self.replace_dialog, text="閉じる", command=self.replace_dialog.destroy).grid(row=3, column=2, padx=5, pady=5)

    def _find_next_for_replace(self):
        """Finds and highlights the next occurrence for replacement."""
        search_text = self.replace_find_entry.get()
        if not search_text:
            return

        is_regex = self.regex_var_replace.get()

        start_index = self.text_area.index(tk.INSERT)
        self.text_area.tag_remove("found", "1.0", tk.END)
        
        try:
            if is_regex:
                pos = self.text_area.search(search_text, start_index, stopindex=tk.END, regexp=True, nocase=True)
            else:
                pos = self.text_area.search(search_text, start_index, stopindex=tk.END, nocase=True)

            if pos:
                if is_regex:
                    line_content_after_pos = self.text_area.get(pos, f"{pos} lineend")
                    match_obj = re.match(search_text, line_content_after_pos, re.IGNORECASE)
                    if match_obj:
                        match_length = match_obj.end() - match_obj.start()
                    else:
                        match_length = len(search_text)
                else:
                    match_length = len(search_text)

                end_pos = f"{pos}+{match_length}c"
                self.text_area.tag_add("found", pos, end_pos)
                self.text_area.tag_config("found", background="yellow")
                self.text_area.mark_set(tk.INSERT, end_pos)
                self.text_area.see(tk.INSERT)
            else:
                if is_regex:
                    pos = self.text_area.search(search_text, "1.0", stopindex=tk.END, regexp=True, nocase=True)
                else:
                    pos = self.text_area.search(search_text, "1.0", stopindex=tk.END, nocase=True)

                if pos:
                    if is_regex:
                        line_content_after_pos = self.text_area.get(pos, f"{pos} lineend")
                        match_obj = re.match(search_text, line_content_after_pos, re.IGNORECASE)
                        if match_obj:
                            match_length = match_obj.end() - match_obj.start()
                        else:
                            match_length = len(search_text)
                    else:
                        match_length = len(search_text)

                    end_pos = f"{pos}+{match_length}c"
                    self.text_area.tag_add("found", pos, end_pos)
                    self.text_area.tag_config("found", background="yellow")
                    self.text_area.mark_set(tk.INSERT, end_pos)
                    self.text_area.see(tk.INSERT)
                else:
                    messagebox.showinfo("検索", "テキストが見つかりませんでした。", parent=self.replace_dialog)
        except re.error as e:
            messagebox.showerror("正規表現エラー", f"無効な正規表現です:\n{e}", parent=self.replace_dialog)

    def _do_replace(self):
        """Replaces the currently selected and matched text."""
        search_text = self.replace_find_entry.get()
        replace_with = self.replace_entry.get()
        if not search_text:
            return

        is_regex = self.regex_var_replace.get()

        current_selection_start = self.text_area.index(tk.SEL_FIRST) if self.text_area.tag_ranges(tk.SEL) else None
        current_selection_end = self.text_area.index(tk.SEL_LAST) if self.text_area.tag_ranges(tk.SEL) else None

        try:
            if current_selection_start:
                selected_text = self.text_area.get(current_selection_start, current_selection_end)
                
                match = False
                if is_regex:
                    match_obj = re.fullmatch(search_text, selected_text, re.IGNORECASE)
                    if match_obj:
                        match = True
                else:
                    if selected_text.lower() == search_text.lower():
                        match = True

                if match:
                    self.text_area.replace(current_selection_start, current_selection_end, replace_with)
                    self.text_area.tag_remove("found", "1.0", tk.END)
                    self.text_area.mark_set(tk.INSERT, f"{current_selection_start}+{len(replace_with)}c")
                    self.text_area.see(tk.INSERT)
                    self.text_area.edit_modified(True)
                    self.on_text_modified()
                    self._find_next_for_replace()
                    return
            
            self._find_next_for_replace()

        except re.error as e:
            messagebox.showerror("正規表現エラー", f"無効な正規表現です:\n{e}", parent=self.replace_dialog)

    def _do_replace_all(self):
        """Replaces all occurrences of the search text."""
        search_text = self.replace_find_entry.get()
        replace_with = self.replace_entry.get()
        if not search_text:
            return

        is_regex = self.regex_var_replace.get()
        
        self.text_area.edit_separator()

        count = 0
        start_pos = "1.0"
        
        try:
            while True:
                if is_regex:
                    pos = self.text_area.search(search_text, start_pos, stopindex=tk.END, regexp=True, nocase=True)
                else:
                    pos = self.text_area.search(search_text, start_pos, stopindex=tk.END, nocase=True)
                    
                if not pos:
                    break
                
                if is_regex:
                    line_content_after_pos = self.text_area.get(pos, f"{pos} lineend")
                    match_obj = re.match(search_text, line_content_after_pos, re.IGNORECASE)
                    if match_obj:
                        match_length = match_obj.end() - match_obj.start()
                    else:
                        match_length = len(search_text)
                else:
                    match_length = len(search_text)
                    
                end_pos = f"{pos}+{match_length}c"
                
                self.text_area.replace(pos, end_pos, replace_with)
                start_pos = f"{pos}+{len(replace_with)}c"
                count += 1
            
            self.text_area.tag_remove("found", "1.0", tk.END)
            if count > 0:
                self.text_area.edit_modified(True)
                self.on_text_modified()
            messagebox.showinfo("置換", f"{count} 件を置換しました。", parent=self.replace_dialog)
        except re.error as e:
            messagebox.showerror("正規表現エラー", f"無効な正規表現です:\n{e}", parent=self.replace_dialog)

    def toggle_word_wrap(self):
        """Toggles word wrapping on/off in the text area."""
        if self.word_wrap_var.get():
            self.text_area.config(wrap=tk.WORD)
        else:
            self.text_area.config(wrap=tk.NONE)

    # --- Section Dialog Functions ---
    def show_section_dialog(self):
        """Displays the Section dialog, extracting sections from the current document."""
        # Prevent multiple dialogs
        if hasattr(self, 'section_dialog') and self.section_dialog.winfo_exists():
            self.section_dialog.lift()
            return

        self.section_dialog = tk.Toplevel(self.master)
        self.section_dialog.title("セクション")

        # Apply saved dimensions for the section dialog
        try:
            width = int(self.settings.get('section_dialog_width', 300))
            height = int(self.settings.get('section_dialog_height', 400))
            self.section_dialog.geometry(f"{width}x{height}")
        except ValueError:
            self.section_dialog.geometry("300x400")

        self.section_listbox = tk.Listbox(self.section_dialog)
        self.section_listbox.pack(expand=True, fill="both", padx=10, pady=10)
        self.section_listbox.bind("<<ListboxSelect>>", self._on_section_select)

        self._populate_sections() # Populate the listbox with sections

        close_button = tk.Button(self.section_dialog, text="閉じる", command=self._on_section_dialog_closing)
        close_button.pack(pady=5)

        # Register handler for closing the dialog
        self.section_dialog.protocol("WM_DELETE_WINDOW", self._on_section_dialog_closing)

    def _on_section_dialog_closing(self):
        """Handles closing the section dialog, saving its size."""
        self.settings['section_dialog_width'] = self.section_dialog.winfo_width()
        self.settings['section_dialog_height'] = self.section_dialog.winfo_height()
        write_ini(self.config_filepath, self.settings)
        self.section_dialog.destroy()

    def _populate_sections(self):
        """Fills the section listbox with detected sections from the document."""
        self.section_listbox.delete(0, tk.END) # Clear previous items
        content = self.text_area.get(1.0, tk.END)
        # Use self.parser (which handles main editor content parsing)
        _, sections = self.parser.parse(content)
        self.parsed_sections = sections # Store parsed sections

        for extracted_section_name, _, _ in self.parsed_sections:
            self.section_listbox.insert(tk.END, extracted_section_name.strip())

    def _on_section_select(self, event):
        """Handles selection in the section listbox, jumping to the corresponding line."""
        selected_indices = self.section_listbox.curselection()
        if selected_indices:
            index = selected_indices[0]
            # Retrieve the original line content for accurate search
            extracted_section_name, line_number, original_line_content = self.parsed_sections[index]

            search_text = original_line_content.strip()

            self.text_area.tag_remove("found_section", "1.0", tk.END) # Clear previous section highlight

            # Define search range as the specific line
            start_index = f"{line_number}.0"
            end_index = f"{line_number}.end"

            # Search within that line for the exact text (case-insensitive)
            pos = self.text_area.search(search_text, start_index, stopindex=end_index, nocase=True)

            if pos:
                end_pos = f"{pos}+{len(search_text)}c"
                self.text_area.tag_add("found_section", pos, end_pos)
                self.text_area.tag_config("found_section", background="lightblue") # Highlight section
                self.text_area.mark_set(tk.INSERT, pos) # Move cursor to start of section
                self.text_area.see(tk.INSERT) # Scroll to cursor
            else:
                # Fallback: if search fails (e.g., text changed), just jump by line number
                self.text_area.mark_set(tk.INSERT, f"{line_number}.0")
                self.text_area.see(tk.INSERT)
                messagebox.showwarning("検索失敗", "セクションの元の行内容が見つかりませんでした。", parent=self.section_dialog)

            self.update_status_bar() # Update status bar after jumping

# --- Main Application Entry Point ---
def main():
    parser = argparse.ArgumentParser(description="簡易Iniファイルエディタ")
    parser.add_argument("input_file", nargs="?", default=None,
                        help="編集するINI/TOMLファイルのパス (オプション)")
    parser.add_argument("--line", type=int, default=None,
                        help="開始行番号 (オプション)")
    parser.add_argument("--col", type=int, default=0,
                        help="開始桁番号 (オプション, デフォルトは0)")
    args = parser.parse_args()

    # Use TkinterDnD.Tk() for drag-and-drop functionality
    root = TkinterDnD.Tk()
    editor = TomlEditor(root, initial_file=args.input_file, initial_line=args.line, initial_col=args.col)
    root.mainloop()

if __name__ == "__main__":
    main()