"""A GUI app for using some of the features of `trouver`."""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/54_app.__init__.ipynb.

# %% auto 0
__all__ = ['Ui_MainWindow', 'ScrollableMessageBox', 'TrouverGUI', 'initUI', 'simple_instructional_message', 'DownloadThread',
           'choose_file_or_folder', 'choose_source_download_folder', 'get_folder', 'get_download_folder',
           'download_arxiv_source', 'open_downloaded_folder', 'choose_source_code_file',
           'choose_obsidian_subvault_location', 'CreateObsidianVaultThread', 'create_obsidian_subvault',
           'open_created_obsidian_subvault', 'get_prediction_folder', 'choose_prediction_folder', 'predict_on_notes',
           'show_confirmation_dialog', 'PredictionThread',
           'from_pretrained_fastai_without_checking_fastai_fastcore_version', 'open_prediction_folder',
           'get_obsidian_subvault_to_compile', 'choose_obsidian_subvault_to_compile_notes', 'get_original_tex_file',
           'choose_original_tex_file', 'get_compiled_tex_output_folder', 'choose_compiled_tex_output_folder',
           'compile_obsidian_subvault_as_tex_file', 'CompileObsidianSubvaultAsTexThread', 'main']

# %% ../../nbs/54_app.__init__.ipynb 2
# import gradio as gr

import glob
import os
from os import PathLike
import pathlib
from pathlib import Path
import platform
import sys
from typing import Callable, Optional
import warnings

from bs4 import MarkupResemblesLocatorWarning
import fastai
# from fastai.learner import Learner
from huggingface_hub import from_pretrained_fastai, snapshot_download
from huggingface_hub.fastai_utils import _check_fastai_fastcore_pyproject_versions, get_fastai_version
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import QThread, pyqtSignal
from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QPushButton, QVBoxLayout, QFileDialog, QMainWindow, QMessageBox, QScrollArea, QSpacerItem, QSizePolicy
from PyQt6.QtCore import Qt



from transformers import AutoModelForSeq2SeqLM, AutoModelForTokenClassification, AutoTokenizer, pipeline

from ..helper.arxiv import arxiv_id, arxiv_search, download_from_results, extract_last_names
from ..helper.files_and_folders import get_download_path, get_huggingface_cache_dir, text_from_file
from ..helper.html import remove_html_tags_in_text
from ..helper.latex.macros_and_commands import custom_commands
from ..helper.packages import ensure_package_installed, check_package_version_with_version_attribute, check_package_installed
from ..helper.regex import replace_string_by_indices
from ..latex import find_main_latex_file
from ..latex.convert import convert_notes_to_latex_code, setup_reference_from_latex_parts
from ..latex.divide import divide_latex_text
from ..latex.preamble import divide_preamble, replace_inclusion_of_style_file_with_code
from ..markdown.markdown.file import MarkdownFile
from ..markdown.obsidian.personal.machine_learning.information_note_types import automatically_add_note_type_tags
from ..markdown.obsidian.personal.machine_learning.tokenize.def_and_notat_token_classification import auto_mark_def_and_notats, convert_double_asterisks_to_html_tags
from ..markdown.obsidian.personal.machine_learning.notation_summarization import append_summary_to_notation_note
from ..markdown.obsidian.personal.note_processing import process_standard_information_note
from ..markdown.obsidian.personal.note_type import note_is_of_type, PersonalNoteTypeEnum
from ..markdown.obsidian.personal.notes import notes_linked_in_notes_linked_in_note
from ..markdown.obsidian.vault import all_paths_to_notes_in_vault, VaultNote

# MANUAL_FASTAI_VERSION = "2.7.18"

# %% ../../nbs/54_app.__init__.ipynb 3
# if __name__ == "__main__":
#     gradio_app.launch()

# %% ../../nbs/54_app.__init__.ipynb 7
# from pyqt6_UI.app import Ui_MainWindow

# Form implementation generated from reading ui file 'app.ui'
#
# Created by: PyQt6 UI code generator 6.4.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again.  Do not edit this file unless you know what you are doing.
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(593, 618)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
        MainWindow.setSizePolicy(sizePolicy)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
        self.centralwidget.setSizePolicy(sizePolicy)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.tabWidget = QtWidgets.QTabWidget(parent=self.centralwidget)
        self.tabWidget.setObjectName("tabWidget")
        self.tab = QtWidgets.QWidget()
        self.tab.setObjectName("tab")
        self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.tab)
        self.verticalLayout_6.setObjectName("verticalLayout_6")
        self.frame = QtWidgets.QFrame(parent=self.tab)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth())
        self.frame.setSizePolicy(sizePolicy)
        self.frame.setFrameShape(QtWidgets.QFrame.Shape.Box)
        self.frame.setFrameShadow(QtWidgets.QFrame.Shadow.Plain)
        self.frame.setLineWidth(2)
        self.frame.setMidLineWidth(1)
        self.frame.setObjectName("frame")
        self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.frame)
        self.verticalLayout_4.setObjectName("verticalLayout_4")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.label_3 = QtWidgets.QLabel(parent=self.frame)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
        self.label_3.setSizePolicy(sizePolicy)
        self.label_3.setTextFormat(QtCore.Qt.TextFormat.MarkdownText)
        self.label_3.setObjectName("label_3")
        self.verticalLayout.addWidget(self.label_3)
        self.label = QtWidgets.QLabel(parent=self.frame)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
        self.label.setSizePolicy(sizePolicy)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.url_input = QtWidgets.QLineEdit(parent=self.frame)
        self.url_input.setText("")
        self.url_input.setObjectName("url_input")
        self.verticalLayout.addWidget(self.url_input)
        self.download_folder_label = QtWidgets.QLabel(parent=self.frame)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.download_folder_label.sizePolicy().hasHeightForWidth())
        self.download_folder_label.setSizePolicy(sizePolicy)
        self.download_folder_label.setObjectName("download_folder_label")
        self.verticalLayout.addWidget(self.download_folder_label)
        self.folder_button = QtWidgets.QPushButton(parent=self.frame)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.folder_button.sizePolicy().hasHeightForWidth())
        self.folder_button.setSizePolicy(sizePolicy)
        self.folder_button.setObjectName("folder_button")
        self.verticalLayout.addWidget(self.folder_button)
        self.download_button = QtWidgets.QPushButton(parent=self.frame)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.download_button.sizePolicy().hasHeightForWidth())
        self.download_button.setSizePolicy(sizePolicy)
        self.download_button.setObjectName("download_button")
        self.verticalLayout.addWidget(self.download_button)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.download_progress_bar = QtWidgets.QProgressBar(parent=self.frame)
        self.download_progress_bar.setProperty("value", 0)
        self.download_progress_bar.setObjectName("download_progress_bar")
        self.horizontalLayout.addWidget(self.download_progress_bar)
        self.open_downloaded_folder_button = QtWidgets.QPushButton(parent=self.frame)
        self.open_downloaded_folder_button.setObjectName("open_downloaded_folder_button")
        self.horizontalLayout.addWidget(self.open_downloaded_folder_button)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.download_progress_label = QtWidgets.QLabel(parent=self.frame)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.download_progress_label.sizePolicy().hasHeightForWidth())
        self.download_progress_label.setSizePolicy(sizePolicy)
        self.download_progress_label.setText("")
        self.download_progress_label.setObjectName("download_progress_label")
        self.verticalLayout.addWidget(self.download_progress_label)
        self.verticalLayout_4.addLayout(self.verticalLayout)
        self.verticalLayout_6.addWidget(self.frame)
        self.tabWidget.addTab(self.tab, "")
        self.tab_2 = QtWidgets.QWidget()
        self.tab_2.setObjectName("tab_2")
        self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.tab_2)
        self.verticalLayout_7.setObjectName("verticalLayout_7")
        self.frame_2 = QtWidgets.QFrame(parent=self.tab_2)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.frame_2.sizePolicy().hasHeightForWidth())
        self.frame_2.setSizePolicy(sizePolicy)
        self.frame_2.setFrameShape(QtWidgets.QFrame.Shape.Box)
        self.frame_2.setFrameShadow(QtWidgets.QFrame.Shadow.Plain)
        self.frame_2.setLineWidth(2)
        self.frame_2.setMidLineWidth(1)
        self.frame_2.setObjectName("frame_2")
        self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.frame_2)
        self.verticalLayout_5.setObjectName("verticalLayout_5")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.label_2 = QtWidgets.QLabel(parent=self.frame_2)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
        self.label_2.setSizePolicy(sizePolicy)
        self.label_2.setTextFormat(QtCore.Qt.TextFormat.MarkdownText)
        self.label_2.setObjectName("label_2")
        self.verticalLayout_2.addWidget(self.label_2)
        self.selected_main_source_code_file_label = QtWidgets.QLabel(parent=self.frame_2)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.selected_main_source_code_file_label.sizePolicy().hasHeightForWidth())
        self.selected_main_source_code_file_label.setSizePolicy(sizePolicy)
        self.selected_main_source_code_file_label.setObjectName("selected_main_source_code_file_label")
        self.verticalLayout_2.addWidget(self.selected_main_source_code_file_label)
        self.choose_source_code_button = QtWidgets.QPushButton(parent=self.frame_2)
        self.choose_source_code_button.setObjectName("choose_source_code_button")
        self.verticalLayout_2.addWidget(self.choose_source_code_button)
        self.choose_obsidian_subvault_folder_label = QtWidgets.QLabel(parent=self.frame_2)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.choose_obsidian_subvault_folder_label.sizePolicy().hasHeightForWidth())
        self.choose_obsidian_subvault_folder_label.setSizePolicy(sizePolicy)
        self.choose_obsidian_subvault_folder_label.setObjectName("choose_obsidian_subvault_folder_label")
        self.verticalLayout_2.addWidget(self.choose_obsidian_subvault_folder_label)
        self.choose_obsidian_subvault_folder_button = QtWidgets.QPushButton(parent=self.frame_2)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.choose_obsidian_subvault_folder_button.sizePolicy().hasHeightForWidth())
        self.choose_obsidian_subvault_folder_button.setSizePolicy(sizePolicy)
        self.choose_obsidian_subvault_folder_button.setObjectName("choose_obsidian_subvault_folder_button")
        self.verticalLayout_2.addWidget(self.choose_obsidian_subvault_folder_button)
        self.label_4 = QtWidgets.QLabel(parent=self.frame_2)
        self.label_4.setObjectName("label_4")
        self.verticalLayout_2.addWidget(self.label_4)
        self.name_for_obsidian_sub_vault = QtWidgets.QLineEdit(parent=self.frame_2)
        self.name_for_obsidian_sub_vault.setObjectName("name_for_obsidian_sub_vault")
        self.verticalLayout_2.addWidget(self.name_for_obsidian_sub_vault)
        self.label_5 = QtWidgets.QLabel(parent=self.frame_2)
        self.label_5.setObjectName("label_5")
        self.verticalLayout_2.addWidget(self.label_5)
        self.author_names_line_edit = QtWidgets.QLineEdit(parent=self.frame_2)
        self.author_names_line_edit.setText("")
        self.author_names_line_edit.setObjectName("author_names_line_edit")
        self.verticalLayout_2.addWidget(self.author_names_line_edit)
        self.create_obsidian_subvault_button = QtWidgets.QPushButton(parent=self.frame_2)
        self.create_obsidian_subvault_button.setObjectName("create_obsidian_subvault_button")
        self.verticalLayout_2.addWidget(self.create_obsidian_subvault_button)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.create_obsidian_vault_progress_bar = QtWidgets.QProgressBar(parent=self.frame_2)
        self.create_obsidian_vault_progress_bar.setProperty("value", 0)
        self.create_obsidian_vault_progress_bar.setObjectName("create_obsidian_vault_progress_bar")
        self.horizontalLayout_2.addWidget(self.create_obsidian_vault_progress_bar)
        self.open_obsidian_subvault_button = QtWidgets.QPushButton(parent=self.frame_2)
        self.open_obsidian_subvault_button.setObjectName("open_obsidian_subvault_button")
        self.horizontalLayout_2.addWidget(self.open_obsidian_subvault_button)
        self.verticalLayout_2.addLayout(self.horizontalLayout_2)
        self.create_obsidian_vault_progress_message_label = QtWidgets.QLabel(parent=self.frame_2)
        self.create_obsidian_vault_progress_message_label.setText("")
        self.create_obsidian_vault_progress_message_label.setObjectName("create_obsidian_vault_progress_message_label")
        self.verticalLayout_2.addWidget(self.create_obsidian_vault_progress_message_label)
        self.verticalLayout_5.addLayout(self.verticalLayout_2)
        self.verticalLayout_7.addWidget(self.frame_2)
        self.tabWidget.addTab(self.tab_2, "")
        self.tab_3 = QtWidgets.QWidget()
        self.tab_3.setObjectName("tab_3")
        self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.tab_3)
        self.verticalLayout_8.setObjectName("verticalLayout_8")
        self.frame_3 = QtWidgets.QFrame(parent=self.tab_3)
        self.frame_3.setFrameShape(QtWidgets.QFrame.Shape.Box)
        self.frame_3.setFrameShadow(QtWidgets.QFrame.Shadow.Plain)
        self.frame_3.setLineWidth(2)
        self.frame_3.setObjectName("frame_3")
        self.verticalLayout_9 = QtWidgets.QVBoxLayout(self.frame_3)
        self.verticalLayout_9.setObjectName("verticalLayout_9")
        self.label_6 = QtWidgets.QLabel(parent=self.frame_3)
        self.label_6.setTextFormat(QtCore.Qt.TextFormat.MarkdownText)
        self.label_6.setObjectName("label_6")
        self.verticalLayout_9.addWidget(self.label_6)
        self.prediction_folder_label = QtWidgets.QLabel(parent=self.frame_3)
        self.prediction_folder_label.setObjectName("prediction_folder_label")
        self.verticalLayout_9.addWidget(self.prediction_folder_label)
        self.choose_prediction_folder_button = QtWidgets.QPushButton(parent=self.frame_3)
        self.choose_prediction_folder_button.setObjectName("choose_prediction_folder_button")
        self.verticalLayout_9.addWidget(self.choose_prediction_folder_button)
        self.predict_on_notes_button = QtWidgets.QPushButton(parent=self.frame_3)
        self.predict_on_notes_button.setObjectName("predict_on_notes_button")
        self.verticalLayout_9.addWidget(self.predict_on_notes_button)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.prediction_progress_bar = QtWidgets.QProgressBar(parent=self.frame_3)
        self.prediction_progress_bar.setProperty("value", 0)
        self.prediction_progress_bar.setObjectName("prediction_progress_bar")
        self.horizontalLayout_3.addWidget(self.prediction_progress_bar)
        self.open_prediction_folder_button = QtWidgets.QPushButton(parent=self.frame_3)
        self.open_prediction_folder_button.setObjectName("open_prediction_folder_button")
        self.horizontalLayout_3.addWidget(self.open_prediction_folder_button)
        self.verticalLayout_9.addLayout(self.horizontalLayout_3)
        self.prediction_progress_label = QtWidgets.QLabel(parent=self.frame_3)
        self.prediction_progress_label.setText("")
        self.prediction_progress_label.setObjectName("prediction_progress_label")
        self.verticalLayout_9.addWidget(self.prediction_progress_label)
        self.verticalLayout_8.addWidget(self.frame_3)
        self.tabWidget.addTab(self.tab_3, "")
        self.tab_4 = QtWidgets.QWidget()
        self.tab_4.setObjectName("tab_4")
        self.verticalLayout_10 = QtWidgets.QVBoxLayout(self.tab_4)
        self.verticalLayout_10.setObjectName("verticalLayout_10")
        self.frame_4 = QtWidgets.QFrame(parent=self.tab_4)
        self.frame_4.setFrameShape(QtWidgets.QFrame.Shape.Box)
        self.frame_4.setFrameShadow(QtWidgets.QFrame.Shadow.Plain)
        self.frame_4.setLineWidth(2)
        self.frame_4.setObjectName("frame_4")
        self.verticalLayout_11 = QtWidgets.QVBoxLayout(self.frame_4)
        self.verticalLayout_11.setObjectName("verticalLayout_11")
        self.label_7 = QtWidgets.QLabel(parent=self.frame_4)
        self.label_7.setTextFormat(QtCore.Qt.TextFormat.MarkdownText)
        self.label_7.setObjectName("label_7")
        self.verticalLayout_11.addWidget(self.label_7)
        self.obsidian_subvault_to_compile_label = QtWidgets.QLabel(parent=self.frame_4)
        self.obsidian_subvault_to_compile_label.setObjectName("obsidian_subvault_to_compile_label")
        self.verticalLayout_11.addWidget(self.obsidian_subvault_to_compile_label)
        self.obsidian_subvault_to_compile_button = QtWidgets.QPushButton(parent=self.frame_4)
        self.obsidian_subvault_to_compile_button.setObjectName("obsidian_subvault_to_compile_button")
        self.verticalLayout_11.addWidget(self.obsidian_subvault_to_compile_button)
        self.original_tex_file_select_label = QtWidgets.QLabel(parent=self.frame_4)
        self.original_tex_file_select_label.setObjectName("original_tex_file_select_label")
        self.verticalLayout_11.addWidget(self.original_tex_file_select_label)
        self.original_tex_file_select_button = QtWidgets.QPushButton(parent=self.frame_4)
        self.original_tex_file_select_button.setObjectName("original_tex_file_select_button")
        self.verticalLayout_11.addWidget(self.original_tex_file_select_button)
        self.compiled_tex_output_folder_label = QtWidgets.QLabel(parent=self.frame_4)
        self.compiled_tex_output_folder_label.setObjectName("compiled_tex_output_folder_label")
        self.verticalLayout_11.addWidget(self.compiled_tex_output_folder_label)
        self.compiled_tex_output_folder_button = QtWidgets.QPushButton(parent=self.frame_4)
        self.compiled_tex_output_folder_button.setObjectName("compiled_tex_output_folder_button")
        self.verticalLayout_11.addWidget(self.compiled_tex_output_folder_button)
        self.compile_tex_from_obsidian_subvault_button = QtWidgets.QPushButton(parent=self.frame_4)
        self.compile_tex_from_obsidian_subvault_button.setObjectName("compile_tex_from_obsidian_subvault_button")
        self.verticalLayout_11.addWidget(self.compile_tex_from_obsidian_subvault_button)
        self.compile_tex_from_obsidian_subvault_progress_bar = QtWidgets.QProgressBar(parent=self.frame_4)
        self.compile_tex_from_obsidian_subvault_progress_bar.setProperty("value", 0)
        self.compile_tex_from_obsidian_subvault_progress_bar.setObjectName("compile_tex_from_obsidian_subvault_progress_bar")
        self.verticalLayout_11.addWidget(self.compile_tex_from_obsidian_subvault_progress_bar)
        self.compile_tex_from_obsidian_subvault_progress_message_label = QtWidgets.QLabel(parent=self.frame_4)
        self.compile_tex_from_obsidian_subvault_progress_message_label.setText("")
        self.compile_tex_from_obsidian_subvault_progress_message_label.setObjectName("compile_tex_from_obsidian_subvault_progress_message_label")
        self.verticalLayout_11.addWidget(self.compile_tex_from_obsidian_subvault_progress_message_label)
        self.verticalLayout_10.addWidget(self.frame_4)
        self.tabWidget.addTab(self.tab_4, "")
        self.verticalLayout_3.addWidget(self.tabWidget)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 593, 22))
        self.menubar.setObjectName("menubar")
        self.menuHelp = QtWidgets.QMenu(parent=self.menubar)
        self.menuHelp.setObjectName("menuHelp")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.action_instruction = QtGui.QAction(parent=MainWindow)
        self.action_instruction.setObjectName("action_instruction")
        self.action_about_trouver = QtGui.QAction(parent=MainWindow)
        self.action_about_trouver.setObjectName("action_about_trouver")
        self.action_disclaimer = QtGui.QAction(parent=MainWindow)
        self.action_disclaimer.setObjectName("action_disclaimer")
        self.menuHelp.addAction(self.action_instruction)
        self.menuHelp.addSeparator()
        self.menuHelp.addAction(self.action_about_trouver)
        self.menuHelp.addAction(self.action_disclaimer)
        self.menubar.addAction(self.menuHelp.menuAction())

        self.retranslateUi(MainWindow)
        self.tabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label_3.setText(_translate("MainWindow", "**Step 1: download the arXiv source**"))
        self.label.setText(_translate("MainWindow", "Enter arXiv URL or ID:"))
        self.url_input.setPlaceholderText(_translate("MainWindow", "https://arxiv.org/abs/xxxx.xxxx"))
        self.download_folder_label.setText(_translate("MainWindow", "Selected Folder: "))
        self.folder_button.setText(_translate("MainWindow", "Choose Folder"))
        self.download_button.setText(_translate("MainWindow", "Download"))
        self.open_downloaded_folder_button.setText(_translate("MainWindow", "Open Downloaded Source Folder"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Step 1"))
        self.label_2.setText(_translate("MainWindow", "**Step 2: setup an Obsidian.md (sub)vault from the arXiv source**"))
        self.selected_main_source_code_file_label.setText(_translate("MainWindow", "Selected Main Source Code File: "))
        self.choose_source_code_button.setText(_translate("MainWindow", "Choose Main Source Code File"))
        self.choose_obsidian_subvault_folder_label.setText(_translate("MainWindow", "Selected Folder to Create Obsidian.md (sub)vault in: "))
        self.choose_obsidian_subvault_folder_button.setText(_translate("MainWindow", "Choose Folder to Create Obsidian.md (sub)vault in"))
        self.label_4.setText(_translate("MainWindow", "Name for Obsidian.md (sub)vault:"))
        self.label_5.setText(_translate("MainWindow", "Author Names (perferably comma and space separated and ordered by alphabetized family names):"))
        self.author_names_line_edit.setPlaceholderText(_translate("MainWindow", "Dedekind, Euler, Gauss, Riemann"))
        self.create_obsidian_subvault_button.setText(_translate("MainWindow", "Create Obsidian.md (sub)vault"))
        self.open_obsidian_subvault_button.setText(_translate("MainWindow", "Open Created Obsidian.md (sub)vault"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Step 2"))
        self.label_6.setText(_translate("MainWindow", "**Step 3: Predict note types and mark definitions and notations using ML models**"))
        self.prediction_folder_label.setText(_translate("MainWindow", "Selected folder in which to make predictions: "))
        self.choose_prediction_folder_button.setText(_translate("MainWindow", "Choose folder to make predictions in"))
        self.predict_on_notes_button.setText(_translate("MainWindow", "Predict"))
        self.open_prediction_folder_button.setText(_translate("MainWindow", "Open Folder"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "Step 3"))
        self.label_7.setText(_translate("MainWindow", "**Step 4: Compile the labeled notes from the Obsidian.md vault back into a single LaTeX file**"))
        self.obsidian_subvault_to_compile_label.setText(_translate("MainWindow", "Selected Obsidian.md (sub)vault from which to compile notes: "))
        self.obsidian_subvault_to_compile_button.setText(_translate("MainWindow", "Choose Obsidian.md (sub)vault from which to compile notes"))
        self.original_tex_file_select_label.setText(_translate("MainWindow", "Selected original .tex file (from which to get the preamble from): "))
        self.original_tex_file_select_button.setText(_translate("MainWindow", "Choose .tex file from which to get the preamble"))
        self.compiled_tex_output_folder_label.setText(_translate("MainWindow", "Folder in which to output compiled .tex file: "))
        self.compiled_tex_output_folder_button.setText(_translate("MainWindow", "Choose folder in which to output the compiled .tex file"))
        self.compile_tex_from_obsidian_subvault_button.setText(_translate("MainWindow", "Compile"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), _translate("MainWindow", "Step 4"))
        self.menuHelp.setTitle(_translate("MainWindow", "Help"))
        self.action_instruction.setText(_translate("MainWindow", "Instruction"))
        self.action_about_trouver.setText(_translate("MainWindow", "About `trouver` GUI"))
        self.action_disclaimer.setText(_translate("MainWindow", "Disclaimer"))


# %% ../../nbs/54_app.__init__.ipynb 8
class ScrollableMessageBox(QMessageBox):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        scroll = QScrollArea(self)
        self.content = QWidget()
        self.content_layout = QVBoxLayout(self.content)
        
        scroll.setWidget(self.content)
        scroll.setWidgetResizable(True)
        scroll.setFixedHeight(400)  # Adjust the height as needed
        scroll.setFixedWidth(600)   # Adjust the width as needed
        
        layout = self.layout()
        layout.addWidget(scroll, 0, 0, 1, layout.columnCount())
        self.setStyleSheet("QScrollArea{min-width: 600px;}")

    def setText(self, text):
        label = QLabel(text)
        label.setWordWrap(True)
        label.setOpenExternalLinks(True)
        self.content_layout.addWidget(label)

# %% ../../nbs/54_app.__init__.ipynb 11
class TrouverGUI(QMainWindow):
    """
    **Attributes**

    - download_thread: DownloadThread
        - A QThread that manages the source-file-download.
    - downloaded_folder: Path
        - The folder in which the source file is downloaded.
    - authors - str
        - Roughly, the family names of the authors. This is determined during the
          invocation of `self.download_thread.run`.
    - create_obsidian_thread: CreateObsidianVaultThread
        - A QThread that manages the Obsidian.md (sub)vault creation.
    - created_obsidian_vault: Path
        - The folder in which the Obsidian.md (sub)vault is created.
    - prediction_thread: PredictionThread
        - A QThread that manages ML predictions
    - current_predicted_note: VaultNote
        - The VaultNote that is currently being predicted.
    - compile_to_tex_thread: CompileObsidianSubvaultAsTexThread
        - A QThead that manages compiling an Obisidian.md (sub)vault into a single .tex file.
    """
    def __init__(self):
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.initUI()
        self.download_thread = None
        self.downloaded_folder = None
        self.authors = None
        self._set_download_folder(get_download_path())
        self.create_obsidian_thread = None
        self.created_obsidian_vault = None
        self.prediction_thread = None
        self.current_predicted_note = None
        self.compile_to_tex_thread = None
        self.disclaimer_message()
        self.instruction_message()
        self.info_message()
        # self.menuBar()


    def info_message(self):
        msg = ScrollableMessageBox(self)
        msg.setWindowTitle("About `trouver` GUI")
        html_content = f"""<p>This application, which is a part of <a href='https://github.com/hyunjongkimmath/trouver'>`trouver`</a>,
        is a prototype GUI for using some of `trouver`'s functionalities.</p>
        <p> This application essentially modifies the contents of the source `.tex` file
        of an arXiv article and outputs a modified `.tex` file which attempts to mark the
        definitions and notations introduced in the article. Compiling the outputted `.tex`
        file with an appropriate LaTeX editor outputs a rendered document (pdf, ps, or dvi)
        which highlights such definitions and notations.</p>

<p>`trouver` is created by <a href='https://sites.google.com/view/hyunjongkimmath/'>Hyun Jong Kim</a>.
See the <a href='https://github.com/hyunjongkimmath/trouver'>GitHub repository for `trouver`</a> for copyright information.
</p>

<p>Please consider also using `trouver` as a Python library/<a href='https://pypi.org/project/trouver/'>pip package</a> as it has more features, is better supported, and may be more stable than this application. </p>

"""
        msg.setInformativeText(html_content)
        # layout = msg.layout()
        # spacer = QSpacerItem(300, 0, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
        # layout.addItem(spacer, layout.rowCount(), 0, 1, layout.columnCount())
        msg.setIcon(QMessageBox.Icon.Information)
        msg.setTextFormat(Qt.TextFormat.RichText)
        msg.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
        msg.show()


    def instruction_message(self):
        msg = ScrollableMessageBox(self)
        msg.setWindowTitle("How to use `trouver` GUI")
        html_content = f"""<p>The following are the steps for using this application:</p>
<ol>
<li>Download the arXiv source file</li>
<li>Setup an Obsidian.md (sub)vault from the arXiv source</li>
<li>Predict note types and mark definitions and notations using Machine Learning (ML)/AI models.</li>
<li>Compile the labeled notes from the Obsidian.md vault back into a single LaTeX file.</li>
</ol>

<p>Once these steps are completed, a `.tex`` file should be outputted.
This file should be compiled with a LaTeX compiler. In most cases, it should suffice to</p>

<ul>
    <li>Copy-paste an arXiv URL into the line edit in Step 1.</li>
    <li>Press the "Download" button in Step 1.</li>
    <li>Press the "Create Obsidian.md (sub)vault" button in Step 2.</li>
    <li>Press the "Predict" button in Step 3.</li>
    <li>Press the "Compile" button in Step 4.</li>
</ul>

<p>Step 3 of the above requires one-time downloads (or updates) of some
ML/AI models from `huggingface`.  It is recommended to have at least several
GB's of free disk space before running step 3. Step 3 automatically
downloads/updates these models as necessary.  In your machine, it is likely
that these models will be downloaded in {get_huggingface_cache_dir()}.
Remember this directory in case you need to delete the models later.
Once these models are downloaded, all computations are performed
on your local machine. This application does not collect your data.</p>"""
        msg.setInformativeText(html_content)
        # layout = msg.layout()
        # spacer = QSpacerItem(300, 0, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
        # layout.addItem(spacer, layout.rowCount(), 0, 1, layout.columnCount())
        msg.setIcon(QMessageBox.Icon.Information)
        msg.setTextFormat(Qt.TextFormat.RichText)
        msg.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
        msg.show()


    def disclaimer_message(self):
        msg = ScrollableMessageBox(self)
        msg.setWindowTitle("How to use `trouver` GUI")
        html_content = """<p>Disclaimer:</p>
<p>This application may not process many arXiv source files properly, as it is
difficult to account for the diverse ways that many authors format their LaTeX
documents. Please consider reporting issues with this application at
<a href='https://github.com/hyunjongkimmath/trouver/issues'>https://github.com/hyunjongkimmath/trouver/issues</a>
and please consider filling out the following <a href='https://docs.google.com/forms/d/e/1FAIpQLScA7XJthYUgt1lK9RbNXvwWKe7Xo1G23Tgivq2o0K3YRFagxw/viewform'>survey</a>: 
so that we can better understand which features you would especially like to see developed.  
However, since `trouver` is currently developed and maintained by a single
mathematician, issues may take significant time to be addressed. </p>
"""

        msg.setInformativeText(html_content)
        # layout = msg.layout()
        # spacer = QSpacerItem(300, 0, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
        # layout.addItem(spacer, layout.rowCount(), 0, 1, layout.columnCount())
        msg.setIcon(QMessageBox.Icon.Information)
        msg.setTextFormat(Qt.TextFormat.RichText)
        msg.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
        msg.show()


# %% ../../nbs/54_app.__init__.ipynb 13
def initUI(self):
    self.setWindowTitle('Arxiv Downloader')
    # For downloading source
    self.ui.folder_button.clicked.connect(self.choose_source_download_folder)
    self.ui.download_button.clicked.connect(self.download_arxiv_source)
    self.ui.open_downloaded_folder_button.clicked.connect(self.open_downloaded_folder)
    # For selecting source
    self.ui.choose_source_code_button.clicked.connect(self.choose_source_code_file)
    self.ui.choose_obsidian_subvault_folder_button.clicked.connect(self.choose_obsidian_subvault_location)
    # For creating Obsidian.md vault
    self.ui.create_obsidian_subvault_button.clicked.connect(self.create_obsidian_subvault)
    self.ui.open_obsidian_subvault_button.clicked.connect(self.open_created_obsidian_subvault)
    # For predicting on Obsidian.md vault notes
    self.ui.choose_prediction_folder_button.clicked.connect(self.choose_prediction_folder)
    self.ui.predict_on_notes_button.clicked.connect(self.predict_on_notes)
    self.ui.open_prediction_folder_button.clicked.connect(self.open_prediction_folder)
    # For compiling an Obsidian.md vault into a single tex file
    self.ui.obsidian_subvault_to_compile_button.clicked.connect(self.choose_obsidian_subvault_to_compile_notes)
    self.ui.original_tex_file_select_button.clicked.connect(self.choose_original_tex_file)
    self.ui.compiled_tex_output_folder_button.clicked.connect(self.choose_compiled_tex_output_folder)
    self.ui.compile_tex_from_obsidian_subvault_button.clicked.connect(self.compile_obsidian_subvault_as_tex_file)
    # Menu
    # self.menuBar().menuHelp.actionInstruction.triggered.connect(self.startup_message)
    self.ui.action_instruction.triggered.connect(self.instruction_message)
    self.ui.action_about_trouver.triggered.connect(self.info_message)
    self.ui.action_disclaimer.triggered.connect(self.disclaimer_message)


TrouverGUI.initUI = initUI

# %% ../../nbs/54_app.__init__.ipynb 15
def simple_instructional_message(self, window_title: str, window_text: str):
    """
    Display a simple error message for when specifications by the user do not
    fulfill requirements.
    """
    msg_box = QMessageBox(self)
    msg_box.setWindowTitle(window_title)
    msg_box.setText(window_text)
    msg_box.setIcon(QMessageBox.Icon.Information)
    msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
    msg_box.exec()

TrouverGUI.simple_instructional_message = simple_instructional_message

# %% ../../nbs/54_app.__init__.ipynb 17
class DownloadThread(QThread):
    progress_update = pyqtSignal(int)
    error_occurred = pyqtSignal(str)  # Add this line
    finished = pyqtSignal()
    
    def __init__(self, url: str, folder: PathLike, trouver_gui):
        super().__init__()
        self.url = url
        self.folder = folder
        self.trouver_gui = trouver_gui
    
    def run(self):
        try:
            self.progress_update.emit(0)
            id = arxiv_id(self.url)
            result = list(arxiv_search(id))[0]

            self.trouver_gui.authors = extract_last_names([author.name for author in result.authors])
            self.trouver_gui.downloaded_folder = download_from_results(result, self.folder)[0]
            self.progress_update.emit(100)
            self.finished.emit()
        except Exception as e:
            self.error_occurred.emit(str(e))

# %% ../../nbs/54_app.__init__.ipynb 18
def choose_file_or_folder(
        self,
        set_text_method: Callable[[PathLike], None], # The method of `self` that is supposed to be used after the folder is chosen to change the text of a label.
        default_folder: PathLike|None, # The default folder to open in the QFileDialog, if available. This should be an absolute path.
        for_folder: bool, # If `True`, then the dialog is for choosing a folder. Otherwise, the dialog is for choosing a file.
        ) -> None:
    if for_folder:
        if default_folder and os.path.exists(default_folder):
            folder = QFileDialog.getExistingDirectory(
                self, "Select Folder", str(default_folder))
        else:
            folder = QFileDialog.getExistingDirectory(
                self, "Select Folder")
        if folder:
            set_text_method(folder)
    else:
        file = QFileDialog.getOpenFileName(self, "Select File")[0]
        if file:
            set_text_method(file)


def choose_source_download_folder(self):
    self.choose_file_or_folder(
        self._set_download_folder,
        self._default_dir_for_source_download(),
        for_folder=True)


def _set_download_folder(self, folder: PathLike|str):
    self.ui.download_folder_label.setText(f'Selected Folder: {folder}')

TrouverGUI.choose_file_or_folder = choose_file_or_folder
TrouverGUI.choose_source_download_folder = choose_source_download_folder
TrouverGUI._set_download_folder = _set_download_folder

# %% ../../nbs/54_app.__init__.ipynb 19
def get_folder(text) -> Path|None:
    """
    Get a folder path from some text.
    
    For example, "Selected Folder: <folder_path>" 
    """
    folder = text.split(": ")[1]
    if folder.strip():
        return Path(folder.strip())
    else:
        return None


def get_download_folder(self) -> Path|None:
    """
    Get the indicated folder in which to download the source code.
    """
    return get_folder(self.ui.download_folder_label.text())


def _default_dir_for_source_download(self) -> Path:
    """
    Helper function for `choose_source_download_folder`
    """
    indicated_folder = self.get_download_folder()
    if indicated_folder:
        return Path(indicated_folder)
    else:
        return Path(get_download_path())

TrouverGUI.get_download_folder = get_download_folder
TrouverGUI._default_dir_for_source_download = _default_dir_for_source_download


# %% ../../nbs/54_app.__init__.ipynb 20
def download_arxiv_source(self):
    url = self.ui.url_input.text()
    folder = self.get_download_folder()
    if not url:
        self._no_arxiv_url_or_id_selected_message()
        return
    elif not folder:
        self._no_download_folder_selected_message()
        return
    self.download_thread = DownloadThread(url, folder, self)
    self.download_thread.progress_update.connect(self._update_download_source_progress)
    self.download_thread.error_occurred.connect(self._handle_download_source_error)
    self.download_thread.finished.connect(self._after_download_finish)
    self.download_thread.start()
    self.ui.download_button.setEnabled(False)

TrouverGUI.download_arxiv_source = download_arxiv_source

# %% ../../nbs/54_app.__init__.ipynb 21
def _no_arxiv_url_or_id_selected_message(self):
    """
    Helper function to `download_arxiv_source`.
    """
    self.simple_instructional_message(
        "URL or ID needed for downloading arXiv source file.",
        'Please enter a URL or ID for an arXiv article in the box beneath "Enter arXiv URL or ID".'
    )

def _no_download_folder_selected_message(self):
    """
    Helper function to `download_arxiv_source`.
    """
    self.simple_instructional_message(
        "Download folder needed for downloading arXiv source file.",
        'Please select a folder in which to download the source file for an arXiv article by clicking the "Choose Folder" button.'
    )

TrouverGUI._no_arxiv_url_or_id_selected_message = _no_arxiv_url_or_id_selected_message
TrouverGUI._no_download_folder_selected_message = _no_download_folder_selected_message

# %% ../../nbs/54_app.__init__.ipynb 22
def _update_download_source_progress(self, value):
    """
    Update the progress bar for source code download task.
    """
    self.ui.download_progress_bar.setValue(value)
    self.ui.download_progress_label.setText(f"Downloading... {value}%")
    if value == 100:
        self.ui.download_progress_label.setText(f"Downloaded source at '{self.downloaded_folder}'")
        self.ui.download_button.setEnabled(True)


def _handle_download_source_error(self, error_message):
    self.ui.download_progress_label.setText(f"Error: {error_message}")
    self.ui.download_button.setEnabled(True)


def _after_download_finish(self):
    if self.downloaded_folder:
        try:
            main_file = find_main_latex_file(self.downloaded_folder)
            self._change_main_source_code_file_label(main_file)
            # TODO
            self._change_choose_obsidian_subvault_folder_label(self.downloaded_folder)
            self._change_obsidian_sub_vault_name_text(os.path.basename(self.downloaded_folder))
            self._change_author_names_line_edit_text(self.authors)
            self._change_compiled_tex_output_folder_label_text(self.downloaded_folder)
        except FileNotFoundError:
            pass


TrouverGUI._update_download_source_progress = _update_download_source_progress
TrouverGUI._handle_download_source_error = _handle_download_source_error
TrouverGUI._after_download_finish = _after_download_finish

# %% ../../nbs/54_app.__init__.ipynb 23
def open_downloaded_folder(self):
    if self.downloaded_folder and os.path.exists(self.downloaded_folder):
        self.ui.download_progress_label.setText(
            f"Opening '{self.downloaded_folder}'.")
        os.startfile(self.downloaded_folder)
    elif os.path.exists(self.get_download_folder()):
        self.ui.download_progress_label.setText(
            f"Opening '{self.get_download_folder()}'.")
        os.startfile(self.get_download_folder())
    else:
        self.ui.download_progress_label.setText(
            f"Failed to open the directories '{self.downloaded_folder}' and '{self.get_download_folder()}' because they do not exist.")

TrouverGUI.open_downloaded_folder = open_downloaded_folder



# %% ../../nbs/54_app.__init__.ipynb 25
def _change_main_source_code_file_label(self, file: PathLike):
    file = str(file)
    self.ui.selected_main_source_code_file_label.setText(f'Selected Main Source Code File: {file}')

def _change_obsidian_sub_vault_name_text(self, name: str):
    self.ui.name_for_obsidian_sub_vault.setText(name)

def _change_author_names_line_edit_text(self, authors: list[str]):
    self.ui.author_names_line_edit.setText(', '.join(authors))

TrouverGUI._change_main_source_code_file_label = _change_main_source_code_file_label
TrouverGUI._change_obsidian_sub_vault_name_text = _change_obsidian_sub_vault_name_text
TrouverGUI._change_author_names_line_edit_text = _change_author_names_line_edit_text


# %% ../../nbs/54_app.__init__.ipynb 26
def choose_source_code_file(self):
    self.choose_file_or_folder(
        self._change_main_source_code_file_label,
        default_folder=None,
        for_folder=False)

def choose_obsidian_subvault_location(self):
    self.choose_file_or_folder(
        self._change_choose_obsidian_subvault_folder_label,
        default_folder=self.downloaded_folder,
        for_folder=True
    )

def _change_choose_obsidian_subvault_folder_label(
        self, folder: PathLike):
    self.ui.choose_obsidian_subvault_folder_label.setText(
        f'Selected Folder to Create Obsidian.md (sub)vault in: {folder}')



TrouverGUI.choose_source_code_file = choose_source_code_file
TrouverGUI.choose_obsidian_subvault_location = choose_obsidian_subvault_location
TrouverGUI._change_choose_obsidian_subvault_folder_label = _change_choose_obsidian_subvault_folder_label


# %% ../../nbs/54_app.__init__.ipynb 27
def _no_latex_source_file_selected_message(self):
    """
    Helper function to `create_obsidian_subvault`.
    """
    self.simple_instructional_message(
        "Source tex file needed for Obsidian.md (sub)vault creation.",
        'Please choose a source tex file from which to create an Obsidian.md (sub)vault (whose contents are excerpts from the tex file) by clicking the button that says "Choose Main Source Code File". You may need to download such a source file first.'
    )

def _latex_source_file_does_not_exist(self, source_file: PathLike):
    """
    Helper function to `create_obsidian_subvault`.
    """
    self.simple_instructional_message(
        "Specified source tex file does not exist.",
        f'The specified source tex file at {source_file} does not exist.'
    )

def _no_subvault_folder_selected_message(self):
    """
    Helper function to `create_obsidian_subvault`.
    """
    self.simple_instructional_message(
        "Folder needed for Obsidian.md (sub)vault creation.",
        'Please choose a folder in which to create an Obsidian.md (sub)vault by clicking the button that says "Choose Folder to Create Obsidian.md (sub)vault in".'
    )

def _no_subvault_name_selected_message(self):
    """
    Helper function to `create_obsidian_subvault`.
    """
    self.simple_instructional_message(
        "Name needed for Obsidian.md (sub)vault creation.",
        'Please specify a name for the Obsidian.md (sub)vault to be created by typing in text into the box beneath "Name for Obisidian.md (sub)vault".'
    )

TrouverGUI._no_latex_source_file_selected_message = _no_latex_source_file_selected_message
TrouverGUI._latex_source_file_does_not_exist = _latex_source_file_does_not_exist
TrouverGUI._no_subvault_folder_selected_message = _no_subvault_folder_selected_message
TrouverGUI._no_subvault_name_selected_message = _no_subvault_name_selected_message

# %% ../../nbs/54_app.__init__.ipynb 28
class CreateObsidianVaultThread(QThread):
    progress_update = pyqtSignal(int)
    error_occurred = pyqtSignal(str)  # Add this line
    finished = pyqtSignal()
    
    def __init__(
            self,
            source_file: PathLike,
            subvault_folder: PathLike,
            subvault_name: str,
            authors: list[str]):
        super().__init__()
        self.source_file = source_file
        self.subvault_folder = subvault_folder
        self.subvault_name = subvault_name
        self.authors = authors

    def run(self):
        try:
            self.progress_update.emit(0)
            source_file, subvault_folder, subvault_name, authors = self.source_file, self.subvault_folder, self.subvault_name, self.authors
            source_dir = Path(source_file).parent
            latex_text = text_from_file(source_file)
            preamble, _ = divide_preamble(latex_text)
            parts = divide_latex_text(latex_text, source_dir)
            preamble = replace_inclusion_of_style_file_with_code(preamble, source_dir)
            cust_comms = custom_commands(preamble)
            self.progress_update.emit(50)
            setup_reference_from_latex_parts(
                parts, cust_comms, subvault_folder, '.',
                subvault_name,
                authors,
                create_reference_file_in_references_folder=False,
                create_template_file_in_templates_folder=False,
                adjust_common_latex_syntax_to_markdown=True,
                repeat_replacing_custom_commands=-1,
                copy_obsidian_configs=None
                )
            self.progress_update.emit(100)
            self.finished.emit()
        except Exception as e:
            self.error_occurred.emit(str(e))
            # raise(e)

# %% ../../nbs/54_app.__init__.ipynb 29
def create_obsidian_subvault(self):
    source_file = get_folder(self.ui.selected_main_source_code_file_label.text())
    # self.ui.selected_main_source_code_file_label.text().split(': ')[1]
    subvault_folder = get_folder(self.ui.choose_obsidian_subvault_folder_label.text())
    # subvault_folder = self.ui.choose_obsidian_subvault_folder_label.text().split(': ')[1]
    authors = self.ui.author_names_line_edit.text().split(', ')
    subvault_name = self.ui.name_for_obsidian_sub_vault.text()
    # Check that everything that needs to be present is present.
    # Otherwise, make a message window
    if not source_file:
        self._no_latex_source_file_selected_message()
        return
    elif not os.path.exists(source_file):
        self._latex_source_file_does_not_exist(source_file)
        return
    elif not subvault_folder:
        self._no_subvault_folder_selected_message()
        return
    elif not subvault_name.strip():
        self._no_subvault_name_selected_message()
        return
    # Setup and run thread
    if not authors:
        authors = []
    self.create_obsidian_thread = CreateObsidianVaultThread(
        source_file, subvault_folder, subvault_name, authors)
    self.create_obsidian_thread.progress_update.connect(self._update_create_obsidian_vault_progress)
    self.create_obsidian_thread.error_occurred.connect(self._handle_create_obsidian_vault_error)
    self.create_obsidian_thread.finished.connect(self._after_create_obsidian_vault_finish)
    self.create_obsidian_thread.start()
    self.ui.create_obsidian_subvault_button.setEnabled(False)
    return

TrouverGUI.create_obsidian_subvault = create_obsidian_subvault

# %% ../../nbs/54_app.__init__.ipynb 30
def _update_create_obsidian_vault_progress(self, value):
    self.ui.create_obsidian_vault_progress_bar.setValue(value)
    if value == 0:
        self.ui.create_obsidian_vault_progress_message_label.setText(
            "Dividing selected source code. This may take a few minutes..."
        )
    elif value == 50:
        self.ui.create_obsidian_vault_progress_message_label.setText(
            "Setting up Obsidian.md (sub)vault. This may take a few minutes..."
        )
    elif value == 100:
        self.ui.create_obsidian_vault_progress_message_label.setText(
            "Obsidian.md (sub)vault has been created!"
        )
        self.ui.create_obsidian_subvault_button.setEnabled(True)
    return

def _handle_create_obsidian_vault_error(self, error_message):
    self.ui.create_obsidian_vault_progress_message_label.setText(f"Error: {error_message}")
    self.ui.create_obsidian_vault_progress_bar.setValue(0)
    self.ui.create_obsidian_subvault_button.setEnabled(True)
    # print(error_message)

def _after_create_obsidian_vault_finish(self):
    self.created_obsidian_vault = Path(self.create_obsidian_thread.subvault_folder) / self.create_obsidian_thread.subvault_name
    self.ui.prediction_folder_label.setText(
        f'Selected folder in which to make predictions: {self.created_obsidian_vault}')
    self._change_original_tex_file_label_text(
        str(self.create_obsidian_thread.source_file)
    )


TrouverGUI._update_create_obsidian_vault_progress = _update_create_obsidian_vault_progress
TrouverGUI._handle_create_obsidian_vault_error = _handle_create_obsidian_vault_error
TrouverGUI._after_create_obsidian_vault_finish = _after_create_obsidian_vault_finish


# %% ../../nbs/54_app.__init__.ipynb 31
def open_created_obsidian_subvault(self):
    if self.created_obsidian_vault and os.path.exists(self.created_obsidian_vault):
        self.ui.create_obsidian_vault_progress_message_label.setText(
            f'Opening "{self.created_obsidian_vault}"'
        )
    return    
TrouverGUI.open_created_obsidian_subvault = open_created_obsidian_subvault

# %% ../../nbs/54_app.__init__.ipynb 33
def get_prediction_folder(self) -> Path|None:
    return get_folder(self.ui.prediction_folder_label.text())

def choose_prediction_folder(self):
    self.choose_file_or_folder(
        self._change_prediction_folder_label,
        self.get_prediction_folder(),
        for_folder=True
    )
    # current_prediction_folder = str(self.get_prediction_folder())
    # folder = QFileDialog.getExistingDirectory(self, "Select Folder", current_prediction_folder)
    # if folder:
    #     self.ui.prediction_folder_label.setText(
    #         f'Selected folder in which to make predictions: {folder}')

def _change_prediction_folder_label(
        self,
        folder: PathLike):
    self.ui.prediction_folder_label.setText(
        f'Selected folder in which to make predictions: {folder}')
    
def predict_on_notes(self):
    folder = self.get_prediction_folder()
    if not folder:
        self.simple_instructional_message(
            "Folder needed in which to make ML predictions.",
            'Please select a folder in which to make ML predictions by clicking the "Choose folder to make predictions in" button.')
        return 
    models_exist = self._model_exists_in_cache('hyunjongkimmath/information_note_type') and self._model_exists_in_cache('hyunjongkimmath/def_and_notat_token_classification_model')
    packages_installed = check_package_installed('fastai') and check_package_installed('fastcore')
    if (not models_exist or not packages_installed):
        choice = self.show_confirmation_dialog(models_exist, packages_installed)
        if not choice:
            return
        # download_models = True
    # else:
        # download_models = False
    self.prediction_thread = PredictionThread(self, folder)
    self.prediction_thread.progress_update.connect(self._update_prediction_progress)
    self.prediction_thread.error_occurred.connect(self._handle_prediction_error)
    self.prediction_thread.finished.connect(self._after_prediction_finish)
    self.prediction_thread.start()
    self.ui.predict_on_notes_button.setEnabled(False)
    
def _model_exists_in_cache(self, model_name: str):
    cache_dir = get_huggingface_cache_dir()
    model_path = cache_dir / f"models--{model_name.replace('/', '--')}"
    # Check if model exists in cache
    # print(model_path)
    model_exists = model_path.exists() and any(model_path.iterdir())  # Check if directory exists and is not empty
    return model_exists

def show_confirmation_dialog(
        self,
        models_exist: bool,
        packages_installed: bool) -> bool:
    """
    Confirm whether packages should be installed and/or models should be downloaded
    before making predictions.
    """
    msg_box = QMessageBox(self)
    msg_box.setIcon(QMessageBox.Icon.Question)
    message = ''
    if not models_exist:
        message = ('Due to complications that arose when this application was built, '
            'some of the necessary Python packages do not seem to be installed. '
            'These packages (which may include fastai and fastcore) are necessary to load the ML/AI models. '
            'Pressing "Yes" will attempt to install them automatically (from PyPI via pip) and '
            'automatically begin the prediction process. '
            'These installations will likely be temporary and be deleted once the application exits.'
            'Stable Internet connection is neecessary for this process.\n\n')
    if not packages_installed:
        mesage += (
        f'The necessary ML models do not seem to be downloaded to this computer. '
        f'It is recommended to have at least several GBs of free space for this download. '
        f'Pressing "Yes" will download the models and automatically begin the prediction process. '
        f'They will probably be downloaded within {get_huggingface_cache_dir()}. '
        f'Stable Internet connection is necessary for this process\n\n')
    message += 'Proceed?'
    msg_box.setText(message)
        
    msg_box.setWindowTitle("Confirmation needed.")
    msg_box.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
    msg_box.setDefaultButton(QMessageBox.StandardButton.No)
    
    result = msg_box.exec()
    
    if result == QMessageBox.StandardButton.Yes:
        # print("User chose to proceed")
        return True
    else:
        # print("User chose to cancel")
        return False    


TrouverGUI.get_prediction_folder = get_prediction_folder
TrouverGUI.choose_prediction_folder = choose_prediction_folder
TrouverGUI._change_prediction_folder_label = _change_prediction_folder_label
TrouverGUI.predict_on_notes = predict_on_notes
TrouverGUI._model_exists_in_cache = _model_exists_in_cache
TrouverGUI.show_confirmation_dialog = show_confirmation_dialog

# %% ../../nbs/54_app.__init__.ipynb 34
def _update_prediction_progress(self, value):
    """
    Update the progress bar for predictions on notes.
    """
    self.ui.prediction_progress_bar.setValue(value)
    if value == 0:
        self.ui.prediction_progress_label.setText(
            f"Loading ML models. "
            f"If there was a message prompting for the necessary Python packages to be installed (from PyPI via pip), then they are being installed first."
            f"and the ML models may be downloaded. "
            f"These processes may take some time, depending on the Internet connection.")
    elif value == 100:
        self.ui.prediction_progress_label.setText(
            f"Predictions complete!")
        self.ui.predict_on_notes_button.setEnabled(True)
    else:
        if self.current_predicted_note:
            self.ui.prediction_progress_label.setText(
                f"Predicting type and definition/notation locations in file {self.current_predicted_note.path(relative=True)}.")


def _handle_prediction_error(self, error_message):
    self.ui.prediction_progress_label.setText(f"Error: {error_message}")
    self.ui.predict_on_notes_button.setEnabled(True)


def _after_prediction_finish(self):
    folder = self.get_prediction_folder()
    if folder:
        self._change_obsidian_subvault_to_compile_label_text(folder)


TrouverGUI._update_prediction_progress = _update_prediction_progress
TrouverGUI._handle_prediction_error = _handle_prediction_error
TrouverGUI._after_prediction_finish = _after_prediction_finish

# %% ../../nbs/54_app.__init__.ipynb 35
class PredictionThread(QThread):
    progress_update = pyqtSignal(int)
    error_occurred = pyqtSignal(str)  # Add this line
    finished = pyqtSignal()
    
    """
    """
    def __init__(self, trouver_gui, vault: PathLike):
        super().__init__()
        self.trouver_gui = trouver_gui
        self.vault = vault
    
    def run(self):
        try:
            self.progress_update.emit(0)
            ensure_package_installed('fastai')
            ensure_package_installed('fastcore')
            try:
                information_note_type_model = self.load_information_note_type_categorization_model()
            except ImportError:
                information_note_type_model = self.load_information_note_type_categorization_model(
                    load_method=from_pretrained_fastai_without_checking_fastai_fastcore_version)
            def_notat_classifier = self.load_def_and_notat_token_classification_pipeline()
            warnings.filterwarnings("ignore", category=MarkupResemblesLocatorWarning)
            # vault = 
            # index_note = VaultNote(VAULT, name=f'_index_{reference}')
            # notes = notes_linked_in_notes_linked_in_note(index_note, as_dict=False)
            vault = self.vault
            all_note_paths = all_paths_to_notes_in_vault(vault, as_dict=True)
            all_note_paths = [note_path for _, notes in all_note_paths.items() for note_path in notes]
            self.progress_update.emit(1)
            for i, note_path in enumerate(all_note_paths):
                vn = VaultNote(vault, rel_path=note_path)
                if not note_is_of_type(vn, PersonalNoteTypeEnum.STANDARD_INFORMATION_NOTE):
                    continue
                self.trouver_gui.current_predicted_note = vn
                progress = max(1, int(100 * i / len(all_note_paths)))
                # print(f"Emitting progress: {progress}")
                self.progress_update.emit(progress)
                # self.progress_update.emit(max(1, 100 * i / len(all_note_paths)))
                automatically_add_note_type_tags(information_note_type_model, vault, [vn])
                mf = MarkdownFile.from_vault_note(vn)
                if mf.has_tag('_auto/_meta/definition') or mf.has_tag('_auto/_meta/notation'):
                    auto_mark_def_and_notats(vn, def_notat_classifier, excessive_space_threshold=2)
            self.progress_update.emit(100)
            self.finished.emit()
        except Exception as e:
            self.error_occurred.emit(str(e))
            # raise e

    def load_information_note_type_categorization_model(
            self,
            load_method: Callable[[str], fastai.learner.Learner] = from_pretrained_fastai
            ) -> fastai.learner.Learner:
        repo_id = 'hyunjongkimmath/information_note_type'

        if platform.system() == 'Windows':
            temp = pathlib.PosixPath # See https://stackoverflow.com/questions/57286486/i-cant-load-my-model-because-i-cant-put-a-posixpath
            pathlib.PosixPath = pathlib.WindowsPath
            information_note_type_model = load_method(repo_id)
            pathlib.PosixPath = temp
        else:
            information_note_type_model = load_method(repo_id)
        return information_note_type_model

    def load_def_and_notat_token_classification_pipeline(
            self):
        model = AutoModelForTokenClassification.from_pretrained('hyunjongkimmath/def_and_notat_token_classification_model')
        tokenizer = AutoTokenizer.from_pretrained('hyunjongkimmath/def_and_notat_token_classification_model')
        def_notat_classifier = pipeline('ner', model=model, tokenizer=tokenizer)
        return def_notat_classifier


# %% ../../nbs/54_app.__init__.ipynb 36
def from_pretrained_fastai_without_checking_fastai_fastcore_version(
    repo_id: str,
    revision: Optional[str] = None,
):
    """
    This is a copy of the `from_pretrained_fastai` funtion from the `huggingface_hub.fastai_utils`
    module, except that it does not check the fastai and fastcore versions as that function does.

    It may be the case that the fastai/fastcore version checking functions do not work as
    intended when the app is built.

    Args:
        repo_id (`str`):
            The location where the pickled fastai.Learner is. It can be either of the two:
                - Hosted on the Hugging Face Hub. E.g.: 'espejelomar/fatai-pet-breeds-classification' or 'distilgpt2'.
                  You can add a `revision` by appending `@` at the end of `repo_id`. E.g.: `dbmdz/bert-base-german-cased@main`.
                  Revision is the specific model version to use. Since we use a git-based system for storing models and other
                  artifacts on the Hugging Face Hub, it can be a branch name, a tag name, or a commit id.
                - Hosted locally. `repo_id` would be a directory containing the pickle and a pyproject.toml
                  indicating the fastai and fastcore versions used to build the `fastai.Learner`. E.g.: `./my_model_directory/`.
        revision (`str`, *optional*):
            Revision at which the repo's files are downloaded. See documentation of `snapshot_download`.

    Returns:
        The `fastai.Learner` model in the `repo_id` repo.
    """
    # _check_fastai_fastcore_versions()

    # Load the `repo_id` repo.
    # `snapshot_download` returns the folder where the model was stored.
    # `cache_dir` will be the default '/root/.cache/huggingface/hub'
    if not os.path.isdir(repo_id):
        storage_folder = snapshot_download(
            repo_id=repo_id,
            revision=revision,
            library_name="fastai",
            library_version= check_package_version_with_version_attribute('fastai') # MANUAL_FASTAI_VERSION # get_fastai_version(),
        )
    else:
        storage_folder = repo_id

    # _check_fastai_fastcore_pyproject_versions(storage_folder)

    from fastai.learner import load_learner  # type: ignore

    return load_learner(os.path.join(storage_folder, "model.pkl"))

# %% ../../nbs/54_app.__init__.ipynb 37
def open_prediction_folder(self):
    folder = self.get_prediction_folder()
    if folder:
        if os.path.exists(folder):
            self.ui.prediction_progress_label.setText(
                f"Opening '{folder}'."
            )
            os.startfile(folder)
        else:
            self.ui.prediction_progress_label.setText(
                f"Failed to open the directory '{folder}' because it does not exist."
            )
    else:
        self.ui.prediction_progress_label.setText(
            f'Folder not set. Use the "Choose folder to make predictions in" button first.'
        )

TrouverGUI.open_prediction_folder = open_prediction_folder

# %% ../../nbs/54_app.__init__.ipynb 39
def _change_obsidian_subvault_to_compile_label_text(
        self, vault: PathLike):
    self.ui.obsidian_subvault_to_compile_label.setText(
        f'Selected Obsidian.md (sub)vault from which to compile notes: {vault}')


def _change_original_tex_file_label_text(
        self, file: PathLike):
    self.ui.original_tex_file_select_label.setText(
        f'Selected original .tex file (from which to get the preamble from): {file}')

def _change_compiled_tex_output_folder_label_text(
        self, folder: PathLike):
    self.ui.compiled_tex_output_folder_label.setText(
        f'Folder in which to output compiled .tex file: {folder}')


TrouverGUI._change_obsidian_subvault_to_compile_label_text = _change_obsidian_subvault_to_compile_label_text
TrouverGUI._change_original_tex_file_label_text = _change_original_tex_file_label_text
TrouverGUI._change_compiled_tex_output_folder_label_text = _change_compiled_tex_output_folder_label_text

# %% ../../nbs/54_app.__init__.ipynb 40
def get_obsidian_subvault_to_compile(self) -> Path|None:
    return get_folder(self.ui.obsidian_subvault_to_compile_label.text())

def choose_obsidian_subvault_to_compile_notes(self):
    # folder = QFileDialog.getExistingDirectory()
    self.choose_file_or_folder(
        self._change_obsidian_subvault_to_compile_label_text,
        self.get_obsidian_subvault_to_compile(),
        for_folder=True
    )

def get_original_tex_file(self) -> Path|None:
    return get_folder(self.ui.original_tex_file_select_label.text())

def choose_original_tex_file(self):
    self.choose_file_or_folder(
        self._change_original_tex_file_label_text,
        self.get_original_tex_file(),
        for_folder=False
    )

def get_compiled_tex_output_folder(self) -> Path|None:
    return get_folder(self.ui.compiled_tex_output_folder_label.text())

def choose_compiled_tex_output_folder(self):
    self.choose_file_or_folder(
        self._change_compiled_tex_output_folder_label_text,
        self.get_compiled_tex_output_folder(),
        for_folder=True
    )

TrouverGUI.get_obsidian_subvault_to_compile = get_obsidian_subvault_to_compile
TrouverGUI.choose_obsidian_subvault_to_compile_notes = choose_obsidian_subvault_to_compile_notes
TrouverGUI.get_original_tex_file = get_original_tex_file
TrouverGUI.choose_original_tex_file = choose_original_tex_file
TrouverGUI.get_compiled_tex_output_folder = get_compiled_tex_output_folder
TrouverGUI.choose_compiled_tex_output_folder = choose_compiled_tex_output_folder

# %% ../../nbs/54_app.__init__.ipynb 41
def compile_obsidian_subvault_as_tex_file(self):
    subvault = self.get_obsidian_subvault_to_compile()
    if not subvault:
        self.simple_instructional_message(
            "Subvault folder needed from which to compile notes.",
            'No folder is selected as the Obisidian.md (sub)vault. Please select one by clicking the "Choose Obsidian.md (sub)vault from which to compile notes" button.')
        return
    elif not os.path.exists(subvault):
        self.simple_instructional_message(
            "Subvault folder needed from which to compile notes.",
            f'The selected subvault folder at {subvault} does not exist. Please select an existing folder by clicking the "Choose Obsidian.md (sub)vault from which to compile notes" button.'
        )
        return
    original_tex_file = self.get_original_tex_file()
    if not original_tex_file:
        self.simple_instructional_message(
            "Original tex file needed from which to obtain preamble.",
            'No file is selected as the original .tex file. Such a file is needed to obtain the appropriate code for the LaTeX preamble. Please select one by clicking the "Choose .tex file from which to get the preamble" button.')
        return
    elif not os.path.exists(original_tex_file):
        self.simple_instructional_message(
            "Original tex file needed from which to obtain preamble.",
            'The selected .tex file at {original_tex_file} does not exist. Please select an existing .tex file by clicking the "Choose .tex file from which to get the preamble" button.')
        return
    output_folder = self.get_compiled_tex_output_folder()
    if not output_folder:
        self.simple_instructional_message(
            "Output folder needed.",
            'No folder is selected as the output folder for the `.tex` file to be compiled. Please select one by clicking the "Choose folder in which to output the compiled .tex file" button.')
        return
    elif not os.path.exists(output_folder):
        self.simple_instructional_message(
            "Output folder needed.",
            'The selected subvault folder at {output_folder} does not exist. Please select an existing folder by clicking the "Choose folder in which to output the compiled .tex file" button.')
        return
    files = list(subvault.glob(f"_index_*.md"))
    if len(files) != 1:
        self.simple_instructional_message(
            "One index file expected.",
            f'There must be exactly one file whose name is of the form "_index_*.md" in the directory "{subvault}", but {len(files)} were found. Please either ensure that only one such file is in the directory.')
        return
    index_file = files[0]
    # print(subvault)
    # print(index_file)

    self.compile_to_tex_thread = CompileObsidianSubvaultAsTexThread(
        subvault, index_file, original_tex_file, output_folder)
    self.compile_to_tex_thread.progress_update.connect(self._update_compile_obsidian_subvault_progress)
    self.compile_to_tex_thread.error_occurred.connect(self._handle_compile_obsidian_subvault_error)
    self.compile_to_tex_thread.finished.connect(self._after_compile_obsidian_subvault_finish)
    self.compile_to_tex_thread.start()
    self.ui.compile_tex_from_obsidian_subvault_button.setEnabled(False)


TrouverGUI.compile_obsidian_subvault_as_tex_file = compile_obsidian_subvault_as_tex_file

# %% ../../nbs/54_app.__init__.ipynb 42
class CompileObsidianSubvaultAsTexThread(QThread):
    progress_update = pyqtSignal(int)
    error_occurred = pyqtSignal(str)  # Add this line
    finished = pyqtSignal()
    def __init__(
            self,
            subvault: PathLike,
            index_file: PathLike,
            original_tex_file: PathLike,
            output_folder: PathLike):
        super().__init__()
        self.subvault = Path(subvault)
        self.index_file = index_file
        # print(self.index_file)
        self.original_tex_file = Path(original_tex_file)
        self.output_folder = Path(output_folder)
        self.output_file = None # Path
    
    def run(self):
        try:
            self.progress_update.emit(0)
            VaultNote.clear_cache()
            # print(self.index_file)
            index_note = VaultNote(
                self.subvault,
                name=os.path.basename(self.index_file)[:-3],
                update_cache=True)
            print(f'Index note name: {index_note.name}')
            notes = notes_linked_in_notes_linked_in_note(index_note, as_dict=False)
            for note in notes:
                print(note.name)
            with open(self.original_tex_file, mode='r', encoding='utf-8') as file:
                original_text = file.read()
            preamble, _ = divide_preamble(original_text)
            output_code = convert_notes_to_latex_code(notes, self.subvault, preamble)
            file_name = 'trouver_output'
            while os.path.exists(self.output_folder / f'{file_name}.tex'):
                file_name += '_dupl'
            self.output_file = self.output_folder / f'{file_name}.tex'
            with open(self.output_file, 'w', encoding='utf-8') as file:
                file.write(output_code)
            self.progress_update.emit(100)
            self.finished.emit()
        except Exception as e:
            self.error_occurred.emit(str(e))
            raise(e)

# %% ../../nbs/54_app.__init__.ipynb 43
def _update_compile_obsidian_subvault_progress(self, value):
    self.ui.compile_tex_from_obsidian_subvault_progress_bar.setValue(value)
    if value == 0:
        self.ui.compile_tex_from_obsidian_subvault_progress_message_label.setText(
            "Compiling notes from `Obsidian.md` subvault into a .tex file."
        )
    elif value == 100:
        self.ui.compile_tex_from_obsidian_subvault_progress_message_label.setText(
            ".tex file compiled!"
        )


def _handle_compile_obsidian_subvault_error(self, error_message):
    self.ui.compile_tex_from_obsidian_subvault_progress_message_label.setText(f"Error: {error_message}")
    self.ui.compile_tex_from_obsidian_subvault_progress_bar.setValue(0)
    self.ui.compile_tex_from_obsidian_subvault_button.setEnabled(True)


def _after_compile_obsidian_subvault_finish(self):
    self.simple_instructional_message(
        "`.tex` file outputed!",
        f"The `.tex` file has been outputed as {self.compile_to_tex_thread.output_file}. "
        "You may compile this file just as you would compile other LaTeX documents. Please "
        " read the comments at the start of this file, as they may help you to "
        " troubleshoot compilation issues. In particular, it may be necesary to manually fix "
        " syntax issues caused by the definition/notation markings predicted by the ML models."
    )
    self.ui.compile_tex_from_obsidian_subvault_button.setEnabled(True)
    return

TrouverGUI._update_compile_obsidian_subvault_progress = _update_compile_obsidian_subvault_progress
TrouverGUI._handle_compile_obsidian_subvault_error = _handle_compile_obsidian_subvault_error
TrouverGUI._after_compile_obsidian_subvault_finish = _after_compile_obsidian_subvault_finish


# %% ../../nbs/54_app.__init__.ipynb 44
#|eval: false
def main():
    app = QApplication(sys.argv)
    downloader = TrouverGUI()
    downloader.show()
    sys.exit(app.exec())

if __name__ == '__main__':
    main()
