Fix and debug some errors.

```
#!/usr/bin/env python3
"""
Enhanced SRVK Algorithm Implementation with Optimized and Feature-Rich GUI in Python

This script implements the Super RSA Versatile Key (SRVK) algorithm with an enhanced and optimized GUI.
The GUI allows users to interactively perform key generation, encryption, and decryption
with additional features such as batch processing, compression, detailed progress tracking, and logging.

Author: [Your Name]
Date: [Date]
"""

import os
import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog
from tkinter import ttk
import threading
import json
import gzip
import base64
import logging
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives import ciphers
from cryptography.hazmat.primitives.ciphers import algorithms, modes, Cipher
from cryptography.hazmat.backends import default_backend
from cryptography.exceptions import InvalidTag

# ==================== Logging Configuration ====================
logging.basicConfig(
    filename='srvk_app.log',
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

# ==================== Encryption and Key Management Functions ====================

def generate_keys(rsa_key_size=2048, symmetric_key_size=256):
    """
    Generates RSA key pair and symmetric key.

    Parameters:
    - rsa_key_size (int): Size of the RSA key in bits (2048, 4096, or 8192)
    - symmetric_key_size (int): Size of the symmetric key in bits (128 or 256)

    Returns:
    - rsa_private_key: RSA private key object
    - rsa_public_key: RSA public key object
    - symmetric_key (bytes): Symmetric key bytes
    """
    logging.debug(f"Generating RSA keys with size {rsa_key_size} bits.")
    # Generate RSA key pair
    rsa_private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=rsa_key_size,
        backend=default_backend()
    )
    rsa_public_key = rsa_private_key.public_key()

    # Generate symmetric key
    if symmetric_key_size not in [128, 256]:
        raise ValueError("Symmetric key size must be 128 or 256 bits.")
    symmetric_key = os.urandom(symmetric_key_size // 8)  # Convert bits to bytes
    logging.debug("Symmetric key generated.")

    return rsa_private_key, rsa_public_key, symmetric_key

def srvk_encrypt(plaintext, rsa_public_key, symmetric_key=None, block_size=1024*1024, key_rotation_interval=0, compress=False):
    """
    Encrypts the plaintext using the SRVK algorithm.

    Parameters:
    - plaintext (bytes): Data to encrypt
    - rsa_public_key: RSA public key object
    - symmetric_key (bytes): Optional; symmetric key to use (if None, a new key is generated)
    - block_size (int): Size of data blocks to split plaintext into
    - key_rotation_interval (int): Number of blocks after which to rotate symmetric key (0 for no rotation)
    - compress (bool): Whether to compress the data before encryption

    Returns:
    - A dictionary containing:
        - 'encrypted_symmetric_keys': List of encrypted symmetric keys (base64 encoded)
        - 'cipher_blocks': List of cipher blocks (each block is a dictionary with 'ciphertext', 'nonce', 'tag', 'key_index')
        - 'compression': Boolean indicating if compression was applied
    """
    logging.debug("Starting encryption process.")
    # Compress data if required
    if compress:
        logging.debug("Compressing plaintext.")
        plaintext = gzip.compress(plaintext)

    # If no symmetric key is provided, generate one
    if symmetric_key is None:
        symmetric_key = os.urandom(32)  # 256-bit key
        logging.debug("Symmetric key not provided. Generated new symmetric key.")

    encrypted_symmetric_keys = []
    cipher_blocks = []

    # Split plaintext into blocks
    blocks = [plaintext[i:i+block_size] for i in range(0, len(plaintext), block_size)]
    num_blocks = len(blocks)
    logging.debug(f"Plaintext split into {num_blocks} blocks.")

    current_symmetric_key = symmetric_key
    encrypted_symmetric_key = rsa_public_key.encrypt(
        current_symmetric_key,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )
    encrypted_symmetric_keys.append(base64.b64encode(encrypted_symmetric_key).decode())
    key_index = 0
    logging.debug("Initial symmetric key encrypted and stored.")

    for i, block in enumerate(blocks):
        # Key rotation
        if key_rotation_interval > 0 and i > 0 and i % key_rotation_interval == 0:
            logging.debug(f"Rotating symmetric key at block {i}.")
            # Rotate symmetric key
            current_symmetric_key = os.urandom(32)
            encrypted_symmetric_key = rsa_public_key.encrypt(
                current_symmetric_key,
                padding.OAEP(
                    mgf=padding.MGF1(algorithm=hashes.SHA256()),
                    algorithm=hashes.SHA256(),
                    label=None
                )
            )
            encrypted_symmetric_keys.append(base64.b64encode(encrypted_symmetric_key).decode())
            key_index += 1

        # Encrypt the block with AES-GCM
        nonce = os.urandom(12)  # 96-bit nonce for GCM
        aes_cipher = Cipher(
            algorithms.AES(current_symmetric_key),
            modes.GCM(nonce),
            backend=default_backend()
        ).encryptor()

        ciphertext = aes_cipher.update(block) + aes_cipher.finalize()
        tag = aes_cipher.tag

        cipher_blocks.append({
            'ciphertext': base64.b64encode(ciphertext).decode(),
            'nonce': base64.b64encode(nonce).decode(),
            'tag': base64.b64encode(tag).decode(),
            'key_index': key_index  # Indicates which encrypted symmetric key to use
        })

        if (i+1) % max(1, num_blocks // 10) == 0:
            logging.debug(f"Encrypted {i+1}/{num_blocks} blocks.")

    logging.debug("Encryption process completed.")
    return {
        'encrypted_symmetric_keys': encrypted_symmetric_keys,
        'cipher_blocks': cipher_blocks,
        'compression': compress
    }

def srvk_decrypt(encrypted_data, rsa_private_key):
    """
    Decrypts the encrypted data using the SRVK algorithm.

    Parameters:
    - encrypted_data (dict): The encrypted data as returned by srvk_encrypt
    - rsa_private_key: RSA private key object

    Returns:
    - plaintext (bytes)
    """
    logging.debug("Starting decryption process.")
    encrypted_symmetric_keys = encrypted_data['encrypted_symmetric_keys']
    cipher_blocks = encrypted_data['cipher_blocks']
    compression = encrypted_data.get('compression', False)

    # Decrypt symmetric keys
    symmetric_keys = []
    for enc_key_b64 in encrypted_symmetric_keys:
        encrypted_symmetric_key = base64.b64decode(enc_key_b64)
        symmetric_key = rsa_private_key.decrypt(
            encrypted_symmetric_key,
            padding.OAEP(
                mgf=padding.MGF1(algorithm=hashes.SHA256()),
                algorithm=hashes.SHA256(),
                label=None
            )
        )
        symmetric_keys.append(symmetric_key)
    logging.debug("All symmetric keys decrypted.")

    # Decrypt cipher blocks
    plaintext_blocks = []
    total_blocks = len(cipher_blocks)

    for idx, block in enumerate(cipher_blocks):
        key_index = block['key_index']
        symmetric_key = symmetric_keys[key_index]

        nonce = base64.b64decode(block['nonce'])
        tag = base64.b64decode(block['tag'])
        ciphertext = base64.b64decode(block['ciphertext'])

        # Decrypt the block with AES-GCM
        try:
            aes_cipher = Cipher(
                algorithms.AES(symmetric_key),
                modes.GCM(nonce, tag),
                backend=default_backend()
            ).decryptor()

            plaintext_block = aes_cipher.update(ciphertext) + aes_cipher.finalize()
            plaintext_blocks.append(plaintext_block)

            if (idx+1) % max(1, total_blocks // 10) == 0:
                logging.debug(f"Decrypted {idx+1}/{total_blocks} blocks.")
        except InvalidTag:
            logging.error(f"Decryption failed at block {idx}. Invalid authentication tag.")
            raise ValueError(f"Decryption failed at block {idx}. Invalid authentication tag.")

    # Reassemble plaintext
    plaintext = b''.join(plaintext_blocks)

    # Decompress if needed
    if compression:
        logging.debug("Decompressing plaintext.")
        plaintext = gzip.decompress(plaintext)

    logging.debug("Decryption process completed.")
    return plaintext

def save_rsa_private_key(private_key, filepath, password=None):
    """
    Saves RSA private key to a file in PEM format.

    Parameters:
    - private_key: RSA private key object
    - filepath (str): Path to save the private key file
    - password (bytes): Optional; password to encrypt the private key
    """
    pem = private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.BestAvailableEncryption(password) if password else serialization.NoEncryption()
    )

    with open(filepath, 'wb') as f:
        f.write(pem)
    logging.debug(f"Private key saved to {filepath}.")

def load_rsa_private_key(filepath, password=None):
    """
    Loads RSA private key from a PEM file.

    Parameters:
    - filepath (str): Path to the private key PEM file
    - password (bytes): Optional; password to decrypt the private key

    Returns:
    - private_key: RSA private key object
    """
    with open(filepath, 'rb') as f:
        pem_data = f.read()

    private_key = serialization.load_pem_private_key(
        pem_data,
        password=password,
        backend=default_backend()
    )
    logging.debug(f"Private key loaded from {filepath}.")
    return private_key

def save_rsa_public_key(public_key, filepath):
    """
    Saves RSA public key to a file in PEM format.

    Parameters:
    - public_key: RSA public key object
    - filepath (str): Path to save the public key file
    """
    pem = public_key.public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PublicFormat.SubjectPublicKeyInfo
    )

    with open(filepath, 'wb') as f:
        f.write(pem)
    logging.debug(f"Public key saved to {filepath}.")

def load_rsa_public_key(filepath):
    """
    Loads RSA public key from a PEM file.

    Parameters:
    - filepath (str): Path to the public key PEM file

    Returns:
    - public_key: RSA public key object
    """
    with open(filepath, 'rb') as f:
        pem_data = f.read()

    public_key = serialization.load_pem_public_key(
        pem_data,
        backend=default_backend()
    )
    logging.debug(f"Public key loaded from {filepath}.")
    return public_key

# ==================== GUI Class ====================

class SRVKApp:
    def __init__(self, root):
        self.root = root
        self.root.title("SRVK Encryption/Decryption")
        self.root.geometry("800x700")
        self.root.configure(bg="#2e2e2e")

        # Apply a style
        self.style = ttk.Style()
        self.style.theme_use('clam')  # Options: 'clam', 'alt', 'default', 'classic'

        # Existing styles
        self.style.configure("TNotebook", background="#2e2e2e", borderwidth=0)
        self.style.configure("TNotebook.Tab", background="#444", foreground="white", padding=[10, 5])
        self.style.map("TNotebook.Tab", background=[("selected", "#2e2e2e")], foreground=[("selected", "white")])
        self.style.configure("TButton", padding=6, relief="flat", background="#444", foreground="white")
        self.style.map("TButton", background=[("active", "#555")])

        # Define custom styles
        self.style.configure("Custom.TCheckbutton", foreground="white", background="#2e2e2e")
        self.style.configure("Custom.TLabel", foreground="white", background="#2e2e2e")
        self.style.configure("Custom.TEntry", foreground="black", background="#ffffff")

        # Create a Notebook (tabbed interface)
        self.notebook = ttk.Notebook(root)
        self.notebook.pack(expand=True, fill='both', padx=10, pady=10)

        # Create Frames for each tab
        self.create_key_generation_tab()
        self.create_encryption_tab()
        self.create_decryption_tab()
        self.create_settings_tab()
        self.create_log_tab()
        self.create_status_bar()

    def create_key_generation_tab(self):
        self.key_gen_frame = ttk.Frame(self.notebook, padding=20)
        self.notebook.add(self.key_gen_frame, text='Key Generation')

        # RSA Key Size
        rsa_label = ttk.Label(self.key_gen_frame, text="RSA Key Size (bits):", style="Custom.TLabel")
        rsa_label.grid(row=0, column=0, sticky='W', pady=10)
        self.rsa_key_size_var = tk.IntVar(value=2048)
        self.rsa_key_size_combo = ttk.Combobox(self.key_gen_frame, textvariable=self.rsa_key_size_var, state='readonly', width=20)
        self.rsa_key_size_combo['values'] = (2048, 4096, 8192)
        self.rsa_key_size_combo.grid(row=0, column=1, pady=10, sticky='W')

        # Symmetric Key Size
        sym_label = ttk.Label(self.key_gen_frame, text="Symmetric Key Size (bits):", style="Custom.TLabel")
        sym_label.grid(row=1, column=0, sticky='W', pady=10)
        self.sym_key_size_var = tk.IntVar(value=256)
        self.sym_key_size_combo = ttk.Combobox(self.key_gen_frame, textvariable=self.sym_key_size_var, state='readonly', width=20)
        self.sym_key_size_combo['values'] = (128, 256)
        self.sym_key_size_combo.grid(row=1, column=1, pady=10, sticky='W')

        # Generate Keys Button
        self.generate_keys_button = ttk.Button(self.key_gen_frame, text="Generate Keys", command=self.generate_keys_thread)
        self.generate_keys_button.grid(row=2, column=0, columnspan=2, pady=20)

        # Progress Bar
        self.key_gen_progress = ttk.Progressbar(self.key_gen_frame, mode='determinate')
        self.key_gen_progress.grid(row=3, column=0, columnspan=2, sticky='EW', pady=10)

    def create_encryption_tab(self):
        self.encrypt_frame = ttk.Frame(self.notebook, padding=20)
        self.notebook.add(self.encrypt_frame, text='Encryption')

        # Public Key Selection
        pub_key_label = ttk.Label(self.encrypt_frame, text="RSA Public Key:", style="Custom.TLabel")
        pub_key_label.grid(row=0, column=0, sticky='W', pady=5)
        self.pub_key_path = tk.StringVar()
        self.pub_key_entry = ttk.Entry(self.encrypt_frame, textvariable=self.pub_key_path, width=50, state='readonly', style="Custom.TEntry")
        self.pub_key_entry.grid(row=0, column=1, pady=5, sticky='W')
        browse_pub_button = ttk.Button(self.encrypt_frame, text="Browse", command=self.browse_pub_key)
        browse_pub_button.grid(row=0, column=2, pady=5, padx=5)

        # Files to Encrypt (Allow multiple)
        file_enc_label = ttk.Label(self.encrypt_frame, text="File(s) to Encrypt:", style="Custom.TLabel")
        file_enc_label.grid(row=1, column=0, sticky='NW', pady=5)
        self.file_enc_paths = tk.StringVar()
        self.file_enc_listbox = tk.Listbox(
            self.encrypt_frame,
            listvariable=self.file_enc_paths,
            selectmode='multiple',
            width=50,
            height=5,
            bg="#1e1e1e",
            fg="#dcdcdc"
        )
        self.file_enc_listbox.grid(row=1, column=1, pady=5, sticky='W')
        browse_file_enc_button = ttk.Button(self.encrypt_frame, text="Browse", command=self.browse_files_to_encrypt)
        browse_file_enc_button.grid(row=1, column=2, pady=5, padx=5)

        # Compression Option
        self.compress_var = tk.BooleanVar(value=False)
        compress_check = ttk.Checkbutton(
            self.encrypt_frame,
            text="Compress before Encryption",
            variable=self.compress_var,
            style="Custom.TCheckbutton"
        )
        compress_check.grid(row=2, column=0, columnspan=2, sticky='W', pady=5)

        # Block Size
        block_size_label = ttk.Label(self.encrypt_frame, text="Block Size (bytes):", style="Custom.TLabel")
        block_size_label.grid(row=3, column=0, sticky='W', pady=5)
        self.block_size_var = tk.IntVar(value=1048576)
        self.block_size_entry = ttk.Entry(self.encrypt_frame, textvariable=self.block_size_var, width=20, style="Custom.TEntry")
        self.block_size_entry.grid(row=3, column=1, pady=5, sticky='W')

        # Key Rotation Interval
        key_rot_label = ttk.Label(self.encrypt_frame, text="Key Rotation Interval (blocks):", style="Custom.TLabel")
        key_rot_label.grid(row=4, column=0, sticky='W', pady=5)
        self.key_rot_var = tk.IntVar(value=0)
        self.key_rot_entry = ttk.Entry(self.encrypt_frame, textvariable=self.key_rot_var, width=20, style="Custom.TEntry")
        self.key_rot_entry.grid(row=4, column=1, pady=5, sticky='W')

        # Encrypt Button
        self.encrypt_button = ttk.Button(self.encrypt_frame, text="Encrypt", command=self.encrypt_thread)
        self.encrypt_button.grid(row=5, column=0, columnspan=3, pady=20)

        # Progress Bar with Percentage
        self.encrypt_progress = ttk.Progressbar(self.encrypt_frame, mode='determinate')
        self.encrypt_progress.grid(row=6, column=0, columnspan=3, sticky='EW', pady=10)

        self.encrypt_progress_label = ttk.Label(self.encrypt_frame, text="Progress: 0%", style="Custom.TLabel")
        self.encrypt_progress_label.grid(row=7, column=0, columnspan=3, sticky='E', pady=5)

    def create_decryption_tab(self):
        self.decrypt_frame = ttk.Frame(self.notebook, padding=20)
        self.notebook.add(self.decrypt_frame, text='Decryption')

        # Private Key Selection
        priv_key_label = ttk.Label(self.decrypt_frame, text="RSA Private Key:", style="Custom.TLabel")
        priv_key_label.grid(row=0, column=0, sticky='W', pady=5)
        self.priv_key_path = tk.StringVar()
        self.priv_key_entry = ttk.Entry(self.decrypt_frame, textvariable=self.priv_key_path, width=50, state='readonly', style="Custom.TEntry")
        self.priv_key_entry.grid(row=0, column=1, pady=5, sticky='W')
        browse_priv_button = ttk.Button(self.decrypt_frame, text="Browse", command=self.browse_priv_key)
        browse_priv_button.grid(row=0, column=2, pady=5, padx=5)

        # Encrypted Files to Decrypt (Allow multiple)
        file_dec_label = ttk.Label(self.decrypt_frame, text="Encrypted File(s):", style="Custom.TLabel")
        file_dec_label.grid(row=1, column=0, sticky='NW', pady=5)
        self.file_dec_paths = tk.StringVar()
        self.file_dec_listbox = tk.Listbox(
            self.decrypt_frame,
            listvariable=self.file_dec_paths,
            selectmode='multiple',
            width=50,
            height=5,
            bg="#1e1e1e",
            fg="#dcdcdc"
        )
        self.file_dec_listbox.grid(row=1, column=1, pady=5, sticky='W')
        browse_file_dec_button = ttk.Button(self.decrypt_frame, text="Browse", command=self.browse_files_to_decrypt)
        browse_file_dec_button.grid(row=1, column=2, pady=5, padx=5)

        # Decrypt Button
        self.decrypt_button = ttk.Button(self.decrypt_frame, text="Decrypt", command=self.decrypt_thread)
        self.decrypt_button.grid(row=2, column=0, columnspan=3, pady=20)

        # Progress Bar with Percentage
        self.decrypt_progress = ttk.Progressbar(self.decrypt_frame, mode='determinate')
        self.decrypt_progress.grid(row=3, column=0, columnspan=3, sticky='EW', pady=10)

        self.decrypt_progress_label = ttk.Label(self.decrypt_frame, text="Progress: 0%", style="Custom.TLabel")
        self.decrypt_progress_label.grid(row=4, column=0, columnspan=3, sticky='E', pady=5)

    def create_settings_tab(self):
        self.settings_frame = ttk.Frame(self.notebook, padding=20)
        self.notebook.add(self.settings_frame, text='Settings')

        # Default Save Directory
        save_dir_label = ttk.Label(self.settings_frame, text="Default Save Directory:", style="Custom.TLabel")
        save_dir_label.grid(row=0, column=0, sticky='W', pady=10)
        self.save_dir_var = tk.StringVar(value=os.getcwd())
        self.save_dir_entry = ttk.Entry(self.settings_frame, textvariable=self.save_dir_var, width=50, state='readonly', style="Custom.TEntry")
        self.save_dir_entry.grid(row=0, column=1, pady=10, sticky='W')
        browse_save_dir_button = ttk.Button(self.settings_frame, text="Browse", command=self.browse_save_directory)
        browse_save_dir_button.grid(row=0, column=2, pady=10, padx=5)

        # Log Level
        log_level_label = ttk.Label(self.settings_frame, text="Log Level:", style="Custom.TLabel")
        log_level_label.grid(row=1, column=0, sticky='W', pady=10)
        self.log_level_var = tk.StringVar(value='INFO')
        self.log_level_combo = ttk.Combobox(self.settings_frame, textvariable=self.log_level_var, state='readonly', width=20)
        self.log_level_combo['values'] = ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
        self.log_level_combo.grid(row=1, column=1, pady=10, sticky='W')

        # Apply Settings Button
        apply_settings_button = ttk.Button(self.settings_frame, text="Apply Settings", command=self.apply_settings)
        apply_settings_button.grid(row=2, column=0, columnspan=3, pady=20)

    def create_log_tab(self):
        self.log_frame = ttk.Frame(self.notebook, padding=20)
        self.notebook.add(self.log_frame, text='Logs')

        # Log Display
        self.log_text = tk.Text(self.log_frame, state='disabled', wrap='word', bg="#1e1e1e", fg="#dcdcdc")
        self.log_text.pack(expand=True, fill='both')

        # Load existing logs
        self.load_logs()

    def create_status_bar(self):
        self.status_var = tk.StringVar()
        self.status_var.set("Welcome to the SRVK Encryption/Decryption Tool.")
        self.status_bar = ttk.Label(self.root, textvariable=self.status_var, relief='sunken', anchor='w', background="#2e2e2e", foreground="white")
        self.status_bar.pack(side='bottom', fill='x')

    # ==================== Key Generation Handlers ====================

    def generate_keys_thread(self):
        threading.Thread(target=self.generate_keys_gui, daemon=True).start()

    def generate_keys_gui(self):
        rsa_key_size = self.rsa_key_size_var.get()
        symmetric_key_size = self.sym_key_size_var.get()

        if rsa_key_size not in [2048, 4096, 8192]:
            messagebox.showerror("Error", "Invalid RSA key size. Choose 2048, 4096, or 8192.")
            logging.error("Invalid RSA key size selected.")
            return
        if symmetric_key_size not in [128, 256]:
            messagebox.showerror("Error", "Invalid symmetric key size. Choose 128 or 256.")
            logging.error("Invalid symmetric key size selected.")
            return

        self.status_var.set("Generating keys...")
        self.key_gen_progress['value'] = 0
        self.key_gen_progress.update()

        try:
            rsa_private_key, rsa_public_key, symmetric_key = generate_keys(rsa_key_size=rsa_key_size, symmetric_key_size=symmetric_key_size)
        except Exception as e:
            messagebox.showerror("Error", f"Key generation failed: {e}")
            logging.exception("Key generation failed.")
            self.key_gen_progress['value'] = 0
            self.status_var.set("Key generation failed.")
            return

        # Saving keys
        try:
            # Save Private Key
            save_path = filedialog.asksaveasfilename(
                title="Save Private Key As",
                defaultextension=".pem",
                initialdir=self.save_dir_var.get(),
                filetypes=[("PEM files", "*.pem")]
            )
            if save_path:
                password = simpledialog.askstring(
                    "Private Key Encryption",
                    "Enter password for private key encryption (leave blank for no password):",
                    show='*'
                )
                if password:
                    password = password.encode()
                else:
                    password = None
                save_rsa_private_key(rsa_private_key, save_path, password=password)
                self.status_var.set(f"Private key saved to {save_path}")
                logging.info(f"Private key saved to {save_path}.")
                self.key_gen_progress['value'] = 50
                self.key_gen_progress.update()
            else:
                logging.warning("Private key save operation was canceled by the user.")
                self.status_var.set("Private key save canceled.")
                self.key_gen_progress['value'] = 0
                return

            # Save Public Key
            save_public_path = filedialog.asksaveasfilename(
                title="Save Public Key As",
                defaultextension=".pem",
                initialdir=self.save_dir_var.get(),
                filetypes=[("PEM files", "*.pem")]
            )
            if save_public_path:
                save_rsa_public_key(rsa_public_key, save_public_path)
                self.status_var.set(f"Public key saved to {save_public_path}")
                logging.info(f"Public key saved to {save_public_path}.")
                self.key_gen_progress['value'] = 100
                self.key_gen_progress.update()
            else:
                logging.warning("Public key save operation was canceled by the user.")
                self.status_var.set("Public key save canceled.")
                self.key_gen_progress['value'] = 50
                return

        except Exception as e:
            messagebox.showerror("Error", f"Failed to save keys: {e}")
            logging.exception("Failed to save keys.")
            self.key_gen_progress['value'] = 0
            self.status_var.set("Key saving failed.")
            return

        self.key_gen_progress['value'] = 100
        self.status_var.set("Key generation completed successfully.")
        messagebox.showinfo("Success", "Keys generated and saved successfully.")

    # ==================== Encryption Handlers ====================

    def browse_pub_key(self):
        path = filedialog.askopenfilename(
            title="Select RSA Public Key",
            filetypes=[("PEM files", "*.pem")],
            initialdir=self.save_dir_var.get()
        )
        if path:
            self.pub_key_path.set(path)
            logging.info(f"Public key selected: {path}")

    def browse_files_to_encrypt(self):
        paths = filedialog.askopenfilenames(
            title="Select File(s) to Encrypt",
            initialdir=self.save_dir_var.get()
        )
        if paths:
            self.file_enc_listbox.delete(0, tk.END)
            for path in paths:
                self.file_enc_listbox.insert(tk.END, path)
            logging.info(f"Files selected for encryption: {paths}")

    def encrypt_thread(self):
        threading.Thread(target=self.encrypt_gui, daemon=True).start()

    def encrypt_gui(self):
        pub_key_path = self.pub_key_path.get()
        if not pub_key_path:
            messagebox.showerror("Error", "Please select an RSA public key.")
            logging.error("No RSA public key selected for encryption.")
            return
        try:
            rsa_public_key = load_rsa_public_key(pub_key_path)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to load public key: {e}")
            logging.exception("Failed to load RSA public key.")
            return

        file_indices = self.file_enc_listbox.curselection()
        if not file_indices:
            messagebox.showerror("Error", "Please select at least one file to encrypt.")
            logging.error("No files selected for encryption.")
            return

        file_paths = [self.file_enc_listbox.get(i) for i in file_indices]
        logging.info(f"Files to encrypt: {file_paths}")

        compression = self.compress_var.get()

        try:
            block_size = self.block_size_var.get()
            if block_size <= 0:
                raise ValueError
        except:
            messagebox.showerror("Error", "Invalid block size. Please enter a positive integer.")
            logging.error("Invalid block size entered.")
            return

        try:
            key_rotation_interval = self.key_rot_var.get()
            if key_rotation_interval < 0:
                raise ValueError
        except:
            messagebox.showerror("Error", "Invalid key rotation interval. Please enter a non-negative integer.")
            logging.error("Invalid key rotation interval entered.")
            return

        self.status_var.set("Starting encryption...")
        self.encrypt_progress['value'] = 0
        self.encrypt_progress.update()
        total_files = len(file_paths)
        for idx, file_path in enumerate(file_paths, start=1):
            try:
                with open(file_path, 'rb') as f:
                    plaintext = f.read()
                logging.info(f"Reading file for encryption: {file_path}")

                encrypted_data = srvk_encrypt(
                    plaintext=plaintext,
                    rsa_public_key=rsa_public_key,
                    block_size=block_size,
                    key_rotation_interval=key_rotation_interval,
                    compress=compression
                )

                # Save encrypted data
                file_name = os.path.basename(file_path)
                encrypted_file_name = f"{file_name}.enc"
                save_encrypted_path = os.path.join(self.save_dir_var.get(), encrypted_file_name)

                with open(save_encrypted_path, 'w') as f:
                    json.dump(encrypted_data, f)
                logging.info(f"Encrypted data saved to {save_encrypted_path}.")

                # Update Progress
                progress = (idx / total_files) * 100
                self.encrypt_progress['value'] = progress
                self.encrypt_progress_label.config(text=f"Progress: {int(progress)}%")
                self.encrypt_progress.update()

            except Exception as e:
                messagebox.showerror("Error", f"Encryption failed for {file_path}: {e}")
                logging.exception(f"Encryption failed for {file_path}.")
                self.encrypt_progress['value'] = 0
                self.status_var.set("Encryption failed.")
                return

        self.encrypt_progress['value'] = 100
        self.encrypt_progress_label.config(text="Progress: 100%")
        self.status_var.set("Encryption completed successfully.")
        messagebox.showinfo("Success", "Encryption of all files completed successfully.")

    # ==================== Decryption Handlers ====================

    def browse_priv_key(self):
        path = filedialog.askopenfilename(
            title="Select RSA Private Key",
            filetypes=[("PEM files", "*.pem")],
            initialdir=self.save_dir_var.get()
        )
        if path:
            self.priv_key_path.set(path)
            logging.info(f"Private key selected: {path}")

    def browse_files_to_decrypt(self):
        paths = filedialog.askopenfilenames(
            title="Select Encrypted File(s)",
            filetypes=[("Encrypted files", "*.enc"), ("All files", "*.*")],
            initialdir=self.save_dir_var.get()
        )
        if paths:
            self.file_dec_listbox.delete(0, tk.END)
            for path in paths:
                self.file_dec_listbox.insert(tk.END, path)
            logging.info(f"Encrypted files selected for decryption: {paths}")

    def decrypt_thread(self):
        threading.Thread(target=self.decrypt_gui, daemon=True).start()

    def decrypt_gui(self):
        priv_key_path = self.priv_key_path.get()
        if not priv_key_path:
            messagebox.showerror("Error", "Please select an RSA private key.")
            logging.error("No RSA private key selected for decryption.")
            return
        password = simpledialog.askstring("Private Key Password", "Enter password for private key (leave blank if none):", show='*')
        if password:
            password = password.encode()
        else:
            password = None
        try:
            rsa_private_key = load_rsa_private_key(priv_key_path, password=password)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to load private key: {e}")
            logging.exception("Failed to load RSA private key.")
            return

        file_indices = self.file_dec_listbox.curselection()
        if not file_indices:
            messagebox.showerror("Error", "Please select at least one encrypted file to decrypt.")
            logging.error("No encrypted files selected for decryption.")
            return

        file_paths = [self.file_dec_listbox.get(i) for i in file_indices]
        logging.info(f"Files to decrypt: {file_paths}")

        self.status_var.set("Starting decryption...")
        self.decrypt_progress['value'] = 0
        self.decrypt_progress.update()
        total_files = len(file_paths)
        for idx, file_path in enumerate(file_paths, start=1):
            try:
                with open(file_path, 'r') as f:
                    encrypted_data = json.load(f)
                logging.info(f"Reading encrypted file for decryption: {file_path}")

                plaintext = srvk_decrypt(
                    encrypted_data=encrypted_data,
                    rsa_private_key=rsa_private_key
                )

                # Determine if the original file was compressed
                original_file_name = os.path.basename(file_path)
                if original_file_name.endswith('.enc'):
                    original_file_name = original_file_name[:-4]

                save_decrypted_path = os.path.join(self.save_dir_var.get(), original_file_name)

                with open(save_decrypted_path, 'wb') as f:
                    f.write(plaintext)
                logging.info(f"Decrypted data saved to {save_decrypted_path}.")

                # Update Progress
                progress = (idx / total_files) * 100
                self.decrypt_progress['value'] = progress
                self.decrypt_progress_label.config(text=f"Progress: {int(progress)}%")
                self.decrypt_progress.update()

            except Exception as e:
                messagebox.showerror("Error", f"Decryption failed for {file_path}: {e}")
                logging.exception(f"Decryption failed for {file_path}.")
                self.decrypt_progress['value'] = 0
                self.status_var.set("Decryption failed.")
                return

        self.decrypt_progress['value'] = 100
        self.decrypt_progress_label.config(text="Progress: 100%")
        self.status_var.set("Decryption completed successfully.")
        messagebox.showinfo("Success", "Decryption of all files completed successfully.")

    # ==================== Settings Handlers ====================

    def browse_save_directory(self):
        path = filedialog.askdirectory(
            title="Select Default Save Directory",
            initialdir=self.save_dir_var.get()
        )
        if path:
            self.save_dir_var.set(path)
            logging.info(f"Default save directory set to: {path}")

    def apply_settings(self):
        log_level = self.log_level_var.get()
        level = getattr(logging, log_level, logging.INFO)
        logging.getLogger().setLevel(level)
        self.status_var.set(f"Settings updated. Log level set to {log_level}.")
        logging.info(f"Log level changed to {log_level}.")
        messagebox.showinfo("Success", "Settings applied successfully.")

    # ==================== Logs Handlers ====================

    def load_logs(self):
        try:
            with open('srvk_app.log', 'r') as f:
                log_content = f.read()
            self.log_text.config(state='normal')
            self.log_text.delete(1.0, tk.END)
            self.log_text.insert(tk.END, log_content)
            self.log_text.config(state='disabled')
            logging.debug("Logs loaded into the log tab.")
        except FileNotFoundError:
            self.log_text.config(state='normal')
            self.log_text.insert(tk.END, "Log file not found.")
            self.log_text.config(state='disabled')
            logging.warning("Log file not found when loading logs into GUI.")

    # ==================== Logs Update Handler ====================

    def update_log_tab(self):
        """
        Periodically update the log tab with new log entries.
        """
        try:
            with open('srvk_app.log', 'r') as f:
                log_content = f.read()
            self.log_text.config(state='normal')
            self.log_text.delete(1.0, tk.END)
            self.log_text.insert(tk.END, log_content)
            self.log_text.config(state='disabled')
        except FileNotFoundError:
            pass
        self.root.after(5000, self.update_log_tab)  # Update every 5 seconds

    # ==================== Utility Methods ====================

    def run(self):
        self.update_log_tab()
        self.root.mainloop()

# ==================== Main Function ====================

def main_gui():
    root = tk.Tk()
    app = SRVKApp(root)
    app.run()

if __name__ == "__main__":
    main_gui()