# encoding: utf-8
import atexit
import os

import sys
import tempfile
import webbrowser
from pathlib import Path

from natsort import natsorted
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import QThread
from PyQt6.QtGui import QRegularExpressionValidator

from src import Icon, logger, settings
from src.backend import Database, BookGrabber, AvailChecker, DocumentationThread
from src.backend.semester import Semester
from src.backend.create_file import recreateFile
from src.backend.delete_temp_contents import delete_temp_contents as tempdelete
from src.ui import Ui_Semesterapparat
from src.logic import (
    APP_NRS,
    # PROF_TITLES,
    ApparatData,
    BookData,
    csv_to_list,
    word_docx_to_csv,
    word_to_semap,
    Prof,
    Apparat,
)
from src.ui.dialogs import (
    popus_confirm,
    medienadder_ui,
    About,
    ApparatExtendDialog,
    Mail_Dialog,
    Settings,
    edit_bookdata_ui,
    login_ui,
    parsed_titles_ui,
    reminder_ui,
)
from src.ui.widgets import (
    ElsaDialog,
    MessageCalendar,
    FilePicker,
    CalendarEntry,
    UserCreate,
    SearchStatisticPage,
    EditUser,
    EditProf,
)
from src.utils import SemesterDocument


valid_input = (0, 0, 0, 0, 0, 0)


class Ui(Ui_Semesterapparat):
    # use the Ui_MainWindow class from mainwindow.py
    def __init__(self, MainWindow, username: str) -> None:
        logger.info("Starting Semesterapparatsmanagement")
        super().__init__()
        self.active_user = username
        self.setupUi(MainWindow)
        self.MainWindow = MainWindow
        # set the window title
        MainWindow.setWindowTitle("Semesterapparatsmanagement")
        MainWindow.setWindowIcon(Icon("logo").icon)

        self.db = Database()
        self.btn_add_document.clicked.connect(self.add_document)
        self.check_file.clicked.connect(
            self.btn_check_file_threaded
        )  # default: self.add_media_from_file
        self.create_new_app.clicked.connect(self.btn_create_new_apparat)
        self.btn_apparat_save.clicked.connect(lambda: self.btn_save_apparat(True))
        self.btn_apparat_apply.clicked.connect(self.update_apparat)
        self.btn_open_document.clicked.connect(self.open_document)
        self.add_medium.clicked.connect(self.btn_add_medium)
        self.btn_copy_adis_command.clicked.connect(self.text_to_clipboard)
        self.btn_reserve.clicked.connect(self.check_availability)
        self.create_document.clicked.connect(self.create_doc)
        self.calendarWidget = MessageCalendar(self.calendar_frame)
        self.calendarWidget.setGridVisible(True)
        self.calendarWidget.setVerticalHeaderFormat(
            QtWidgets.QCalendarWidget.VerticalHeaderFormat.NoVerticalHeader
        )
        self.calendarWidget.setObjectName("MessageCalendar")
        self.calendarWidget.clicked.connect(self.open_reminder)
        # assign a context menu to the calendar
        self.calendarlayout.addWidget(self.calendarWidget)
        self.tableWidget_apparat_media.horizontalHeader().setSectionResizeMode(
            QtWidgets.QHeaderView.ResizeMode.Stretch
        )
        self.tableWidget_apparate.horizontalHeader().setSectionResizeMode(
            QtWidgets.QHeaderView.ResizeMode.Stretch
        )
        self.tableWidget_apparate.setSortingEnabled(True)
        self.saveandcreate.hide()

        # Actions
        self.actionEinstellungen.triggered.connect(self.open_settings)
        Icon("settings", self.actionEinstellungen)
        self.actionDokumentation_lokal.triggered.connect(self.open_documentation)
        Icon("offAction", self.actionBeenden)
        self.actionBeenden.triggered.connect(self.quit)
        self.actionAbout.triggered.connect(self.open_about)

        # set validators
        self.sem_sommer.clicked.connect(lambda: self.toggleButton(self.sem_winter))
        self.sem_winter.clicked.connect(lambda: self.toggleButton(self.sem_sommer))
        self.sem_year.setText(str(QtCore.QDate.currentDate().year()))
        self.prof_mail.setValidator(
            QRegularExpressionValidator(
                QtCore.QRegularExpression(
                    r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}"
                )
            )
        )
        self.prof_tel_nr.setValidator(QtGui.QIntValidator())
        self.prof_tel_nr.setValidator(
            QtGui.QRegularExpressionValidator(QtCore.QRegularExpression(r"^\d{3,14}"))
        )
        # #print(self.prof_tel_nr.maxLength())
        self.app_fach.setValidator(  # validator to allow typing in the app_fach field
            QtGui.QRegularExpressionValidator(
                QtCore.QRegularExpression(r"[a-zA-Z0-9\s\W]+")
            )
        )
        # allow only letters, numbers, whitespaces, symbols for the apparat name
        self.app_name.setValidator(
            QtGui.QRegularExpressionValidator(
                QtCore.QRegularExpression(r"[a-zA-Z0-9\s\W]+")
            )
        )

        self.tableWidget_apparate.addScrollBarWidget(
            QtWidgets.QScrollBar(), QtCore.Qt.AlignmentFlag.AlignRight
        )
        self.tableWidget_apparate.doubleClicked.connect(self.load_app_data)

        # #print(f"user:{self.active_user}")
        userrole = self.db.getRole(self.active_user)
        # hide admin interface when non-admin is logged in
        if userrole == "admin":
            self.tabWidget.setTabVisible(3, True)
        else:
            self.tabWidget.setTabVisible(3, False)
        # self.update_app_media_list()
        self.populate_prof_dropdown()
        self.populate_appfach_dropdown()
        # if the focus is changed from the prof name dropdown, set the prof data if the prof exists in the database, otherwise show a message
        self.drpdwn_prof_name.currentIndexChanged.connect(self.set_prof_data)
        self.cancel_active_selection.clicked.connect(self.btn_cancel_active_selection)
        self.check_eternal_app.stateChanged.connect(self.set_state)
        # validate inputs
        self.prof_mail.textChanged.connect(self.validate_prof_mail)
        self.drpdwn_prof_name.editTextChanged.connect(self.validate_prof_name)
        self.prof_tel_nr.textChanged.connect(self.validate_prof_tel)
        self.app_name.textChanged.connect(self.validate_app_name)
        self.app_fach.currentTextChanged.connect(self.validate_app_fach)
        self.sem_year.textChanged.connect(self.validate_semester)
        self.check_eternal_app.stateChanged.connect(self.validate_semester)
        self.chkbx_show_del_media.stateChanged.connect(self.update_app_media_list)
        self.progress_label.setText("Bitte warten...")

        # Set visibility/enabled state of certain entries
        self.chkbx_show_del_media.setEnabled(False)
        self.label_info.hide()
        self.app_group_box.setEnabled(False)
        self.line_2.hide()
        self.progress_label.hide()
        self.btn_reserve.hide()
        self.label_20.hide()
        self.line_3.hide()
        self.avail_status.hide()
        self.chkbx_show_del_media.hide()
        self.automation_add_selected_books.hide()
        # self.btn_del_select_apparats.setEnabled(False)

        self.tabWidget.currentChanged.connect(self.tabW1_changed)

        # create a thread, that continually checks the validity of the inputs
        self.validate_thread = QThread()
        self.validate_thread.started.connect(self.thread_check)
        self.validate_thread.start()
        self.add_medium.setEnabled(False)
        self.docu = DocumentationThread()
        self.docu.start()
        self.actionDokumentation_lokal.triggered.connect(self.open_documentation)

        # get all current apparats and cache them in a list
        self.apparats = self.get_apparats()

        self.old_apparats = self.apparats  # create a clone to compare against later
        # if length of apparats changes, update box_apparats
        # if tab is changed, gather data needed

        # Context Menus
        self.tableWidget_apparate.setContextMenuPolicy(
            QtCore.Qt.ContextMenuPolicy.CustomContextMenu
        )
        self.tableWidget_apparat_media.setContextMenuPolicy(
            QtCore.Qt.ContextMenuPolicy.CustomContextMenu
        )
        self.tableWidget_apparate.customContextMenuRequested.connect(
            self.open_context_menu
        )
        self.tableWidget_apparat_media.customContextMenuRequested.connect(
            self.media_context_menu
        )

        # admin buttons
        self.select_action_box.currentTextChanged.connect(self.adminActions)
        self.select_action_box.addItem("")
        self.select_action_box.setCurrentText("")
        self.admin_action.setLayout(QtWidgets.QVBoxLayout())
        self.admin_action.setTitle("")

        # Create instances to be used by the threads in the application
        self.bookGrabber = []
        self.availChecker = None
        self.mail_thread = None
        self.autoGrabber = None

        self.elsatab.setLayout(QtWidgets.QVBoxLayout())
        self.search_statistics.setLayout(QtWidgets.QVBoxLayout())

        # add splitter
        self.splitter = QtWidgets.QSplitter(QtCore.Qt.Orientation.Vertical)
        self.splitter.addWidget(self.calendar_frame)
        self.splitter.addWidget(self.frame_creation_progress)
        self.verticalLayout.removeWidget(self.calendar_frame)
        self.verticalLayout.removeWidget(self.frame_creation_progress)
        self.verticalLayout.addWidget(self.splitter)

        self.steps.hide()

    def create_doc(self):
        result = self.confirm_popup(
            "Mit dem Klick auf Okay wird eine Übersicht aller aktiven Semesterapparate erstellt und an den FollowME Drucker gesendet. Es kann bis zu 10 Minuten dauern, bis das document im Drucker angezeigt wird",
            "document erstellen?",
        )
        logger.debug(f"Result: {result}")
        if result == 1:
            # print("Creating document")
            apparats = self.apparats
            apps = []
            for apparat in apparats:
                prof = self.db.getProf(apparat[2])
                data = (apparat[4], f"{prof.lastname} ({apparat[1]})")
                apps.append(data)
            # print(apps)
            logger.info("Using apparats: {}", apps)
            doc = SemesterDocument(
                semester=Semester().value,
                filename="Semesterapparate",
                apparats=apps,
                full=True,
                config=settings,
            )
            # doc.make_document()
            # doc.create_pdf()
            # doc.print_document()
            # doc.cleanup()
            # logger.info("Document created and sent to printer")

            # kill thread after execution done

    def checkValidInput(self):
        if valid_input == (1, 1, 1, 1, 1, 1):
            self.check_file.setEnabled(True)
        else:
            self.check_file.setEnabled(False)

    def setWidget(self, widget):
        # remove all widgets from localwidget

        self.hideWidget()
        # add widget to localwidget
        self.admin_action.layout().addWidget(widget)

    def hideWidget(self):
        try:
            widgets = [
                self.admin_action.layout().itemAt(i).widget()
                for i in range(self.admin_action.layout().count())
            ]
        except AttributeError:
            return
        for widget in widgets:
            self.admin_action.layout().removeWidget(widget)
            widget.deleteLater()

    def adminActions(self):
        if self.select_action_box.currentText() == "Nutzer anlegen":
            self.setWidget(UserCreate())
            self.admin_action.setTitle("Nutzer anlegen")
        elif self.select_action_box.currentText() == "Nutzer bearbeiten":
            self.setWidget(EditUser())
            self.admin_action.setTitle("Nutzer bearbeiten")
        elif self.select_action_box.currentText() == "Lehrperson bearbeiten":
            self.setWidget(EditProf())
            self.admin_action.setTitle("Lehrperson bearbeiten")
        else:
            self.hideWidget()
            self.admin_action.setTitle("")

    def toggleButton(self, button):
        if button.isChecked():
            button.setChecked(False)
        self.validate_semester()

    def open_about(self):
        About().exec()

    def quit(self):
        # delete all temporary files
        tempdelete()
        sys.exit()

    def get_apparats(self):
        alist = self.db.getAllAparats(deleted=0)
        alist = natsorted(alist, key=lambda x: x[4], reverse=True)
        self.tableWidget_apparate.setRowCount(0)
        for apparat in alist:
            self.insert_apparat_into_table(apparat)
        return alist

    def populate_appfach_dropdown(self):
        self.app_fach.clear()
        self.app_fach.addItem("")
        self.app_fach.setCurrentText("")
        self.app_fach.addItems([subject[1] for subject in self.db.getSubjects()])

    def open_documentation(self):
        logger.info("Opening Documentation")
        webbrowser.open("http://localhost:8000")

    def update_calendar(self, data):
        self.calendarWidget.setMessages([data])
        self.calendarWidget.updateCells()

    def tabW1_changed(self):
        if self.tabWidget.currentIndex() == 1:  # Statistics
            stats_layout = self.search_statistics.layout()
            if stats_layout is not None:
                # delete tabpage, re-add with same name at same position
                for widget in [
                    stats_layout.itemAt(i).widget() for i in range(stats_layout.count())
                ]:
                    stats_layout.removeWidget(widget)
                    widget.deleteLater()
            statistics = SearchStatisticPage()
            statistics.apparat_open.connect(self.open_apparat)
            statistics.refreshSignal.connect(self.update_apparat_list)
            statistics.updateCalendar.connect(self.update_calendar)
            stats_layout.addWidget(statistics)

            # #print("searchpage")
        if self.tabWidget.currentIndex() == 0:  # Apparate
            # clear all entries from the table
            self.tableWidget_apparate.setRowCount(0)
            self.get_apparats()
        if self.tabWidget.currentIndex() == 2:  # Elsa
            elsa_layout = self.elsatab.layout()
            if elsa_layout is not None:
                # delete tabpage, re-add with same name at same position
                widgets = [
                    self.elsatab.layout().itemAt(i).widget()
                    for i in range(self.elsatab.layout().count())
                ]
                for widget in widgets:
                    self.elsatab.layout().removeWidget(widget)
                    widget.deleteLater()

            elsa_layout.addWidget(ElsaDialog())
            # print("added")
            pass

    def generateSemester(self, today=False):
        """Generates the current semester.

        Args:
            today (bool, optional): If True, the current semester is generated. Defaults to False.
        Returns:
            str: The current semester
        """
        if today:
            return Semester()
        currentYear = self.sem_year.text()
        currentYear = int(currentYear[-2:])

        semester = "SoSe" if self.sem_sommer.isChecked() else "WiSe"
        if semester == "SoSe":
            return "SoSe " + str(currentYear)
        else:
            return f"WiSe {currentYear}/{currentYear + 1}"

    def open_apparat(self, apparat):
        if self.load_app_data(apparat):
            # change tab focus to tab 0
            self.tabWidget.setCurrentIndex(0)

    def populate_dropdown(self, box, data):
        box.clear()
        box.addItem("")
        box.setCurrentText("")
        box.addItems(data)

    def populate_frame(self, appdata: ApparatData):
        # populate the frame with the data from the database
        self.drpdwn_app_nr.setCurrentText(str(appdata.apparat.appnr))
        self.prof_title.setText(appdata.prof.title)
        prof_name = appdata.prof.name(True)

        self.drpdwn_prof_name.setCurrentText(prof_name)
        self.prof_mail.setText(appdata.prof.mail)
        self.prof_tel_nr.setText(appdata.prof.telnr)
        self.app_name.setText(appdata.apparat.name)
        # #print("changing dropdown app_fach from '' to ", appdata.app_fach)
        self.app_fach.setCurrentText(appdata.apparat.subject)
        # #print("changed dropdown app_fach to ", self.app_fach.currentText())
        self.sem_year.setText(appdata.apparat.get_semester.split(" ")[1])
        match appdata.apparat.get_semester.split(" ")[0]:
            case "SoSe":
                self.sem_sommer.setChecked(True)
                self.sem_winter.setChecked(False)
            case "WiSe":
                self.sem_sommer.setChecked(False)
                self.sem_winter.setChecked(True)

        self.check_eternal_app.setChecked(appdata.apparat.eternal)
        self.prof_id_adis.setText(str(appdata.apparat.prof_id_adis))
        self.apparat_id_adis.setText(str(appdata.apparat.apparat_id_adis))
        self.app_group_box.setEnabled(True)
        self.validateLoadedData()

    def validateLoadedData(self):
        self.validate_prof_mail()
        self.validate_prof_name()
        self.validate_prof_tel()
        self.validate_app_name()
        self.validate_app_fach()
        self.validate_semester()

    def update_apparat(self):
        prof = Prof()
        app = Apparat()
        app.subject = self.app_fach.currentText()
        app.name = self.app_name.text()
        app.appnr = self.active_apparat
        app.eternal = self.check_eternal_app.isChecked()
        prof.mail = self.prof_mail.text()
        prof.telnr = self.prof_tel_nr.text()
        prof.title = self.prof_title.text()
        prof.fullname = self.drpdwn_prof_name.currentText().replace(",", "")
        app.created_semester = (
            self.sem_sommer.text() + " " + self.sem_year.text()
            if self.sem_sommer.isChecked()
            else self.sem_winter.text() + " " + self.sem_year.text()
        )
        app.prof_id_adis = self.prof_id_adis.text()
        prof_id = self.db.getProfByName(prof.fullname).id
        self.add_files(prof_id)
        app.apparat_id_adis = self.apparat_id_adis.text()
        appdata = ApparatData(prof=prof, apparat=app)
        self.db.updateApparat(appdata)

        self.update_app_media_list()
        self.cancel_active_selection.click()
        self.check_send_mail.show()
        self.chkbx_show_del_media.show()
        self.cancel_active_selection.setEnabled(False)
        self.add_medium.setEnabled(False)
        # update apparat table
        self.get_apparats()

    def confirm_popup(self, message: str, title: str):
        popup = popus_confirm(title=title)
        popup.textEdit.setReadOnly(True)
        popup.textEdit.setText(message)

        popup.exec()
        return popup.result()

    def thread_check(self):
        # #print("Thread started")
        self.prof_mail.textChanged.connect(self.validate_prof_mail)
        self.drpdwn_prof_name.editTextChanged.connect(self.validate_prof_name)
        self.prof_tel_nr.textChanged.connect(self.validate_prof_tel)
        self.app_name.textChanged.connect(self.validate_app_name)
        self.app_fach.currentTextChanged.connect(self.validate_app_fach)
        self.sem_year.textChanged.connect(self.validate_semester)
        self.check_eternal_app.stateChanged.connect(self.validate_semester)

        # else:
        #     pass
        #     # self.drpdwn_prof_name.setStyleSheet("border: 1px solid black;")

    # Validators
    def __setValidState(self, widget, state, mand, index):
        if state:
            Icon("valid_true", widget, True, color="success")
            mand.setText("")
            self.change_state(index, 1)
        else:
            Icon("valid_false", widget, recolor=True, color="warning")
            mand.setText("*")
            self.change_state(index, 0)

    def validate_prof_name(self):
        if (
            self.app_group_box.isEnabled()
            and "," in self.drpdwn_prof_name.currentText()
        ):
            self.__setValidState(self.valid_check_profname, 1, self.profname_mand, 0)
        else:
            self.__setValidState(self.valid_check_profname, 0, self.profname_mand, 0)

    def validate_prof_mail(self):
        if self.app_group_box.isEnabled():
            if self.prof_mail.hasAcceptableInput():
                self.__setValidState(self.valid_check_mail, 1, self.mail_mand, 1)
            else:
                self.__setValidState(self.valid_check_mail, 0, self.mail_mand, 1)
        else:
            self.__setValidState(self.valid_check_mail, 0, self.mail_mand, 1)

    def validate_prof_tel(self):
        if self.app_group_box.isEnabled():
            if self.prof_tel_nr.text() != "" and self.prof_tel_nr.hasAcceptableInput():
                self.__setValidState(self.valid_check_telnr, 1, self.telnr_mand, 2)
            else:
                self.__setValidState(self.valid_check_telnr, 0, self.telnr_mand, 2)
        else:
            self.__setValidState(self.valid_check_telnr, 0, self.telnr_mand, 2)

    def validate_app_name(self):
        if self.app_group_box.isEnabled() and self.app_name.hasAcceptableInput():
            self.__setValidState(self.valid_check_appname, 1, self.appname_mand, 3)
        else:
            self.__setValidState(self.valid_check_appname, 0, self.appname_mand, 3)

    def validate_app_fach(self):
        if self.app_group_box.isEnabled() and self.app_fach.currentText() != "":
            self.__setValidState(self.valid_check_app_fach, 1, self.fach_mand, 4)
        else:
            self.__setValidState(self.valid_check_app_fach, 0, self.fach_mand, 4)

    def validate_semester(self):
        if (
            self.app_group_box.isEnabled()
            and (
                (self.sem_sommer.isChecked() or self.sem_winter.isChecked())
                and self.sem_year.text() != ""
                and len(self.sem_year.text())
                >= 2  # check if the year is at least 2 digits long
            )
            or self.check_eternal_app.isChecked()
        ):
            self.__setValidState(self.valid_check_semester, 1, self._mand, 5)
            self.check_eternal_app.setEnabled(True)
        else:
            self.__setValidState(self.valid_check_semester, 0, self._mand, 5)
            self.check_eternal_app.setEnabled(False)

    def change_state(self, index, state):
        global valid_input
        valid_input = list(valid_input)
        valid_input[index] = state
        valid_input = tuple(valid_input)
        self.checkValidInput()

    def set_state(self):
        # set state of semester and year
        if self.check_eternal_app.isChecked():
            self.sem_winter.setEnabled(False)
            self.sem_sommer.setEnabled(False)
            self.sem_year.setEnabled(False)
        else:
            self.sem_winter.setEnabled(True)
            self.sem_sommer.setEnabled(True)
            self.sem_year.setEnabled(True)

    def validate_fields(self):
        return all(valid_input)

    def set_prof_data(self):
        self.prof_title.clear()
        if "," not in self.drpdwn_prof_name.currentText():
            self.prof_mail.clear()
            self.prof_tel_nr.clear()
            return
        selected_prof = self.drpdwn_prof_name.currentText()
        data = self.db.getProfData(selected_prof)
        # logger.debug(data)
        prof_title = data.title
        if prof_title == "None":
            prof_title = "Kein Titel"
        self.prof_title.setText(prof_title)
        self.prof_tel_nr.setText(data.telnr)
        self.prof_mail.setText(data.mail)
        self.app_name.setFocus()

    def get_index_of_value(self, table_widget, value):
        for i in range(table_widget.rowCount()):
            for j in range(table_widget.columnCount()):
                if (
                    table_widget.item(i, j) is not None
                    and table_widget.item(i, j).text() == value
                ):
                    return i, j
        return (None, None)

    def load_app_data(self, app_id=None):
        self.cancel_active_selection.setEnabled(True)
        self.add_medium.setEnabled(True)
        for child in self.app_group_box.findChildren(QtWidgets.QToolButton):
            child.show()
        if isinstance(app_id, str):
            # double click the tableWidget_apparate row with the given app_id
            row, column = self.get_index_of_value(self.tableWidget_apparate, app_id)
            if row is None and column is None:
                return False
            # set the current index to the row
            self.tableWidget_apparate.setCurrentCell(row, 0)
        self.check_send_mail.hide()
        app_pos = self.tableWidget_apparate.currentIndex()
        appnr = self.tableWidget_apparate.item(app_pos.row(), 0).text()
        appname = self.tableWidget_apparate.item(app_pos.row(), 1).text()
        self.sem_sommer.setEnabled(False)
        self.sem_winter.setEnabled(False)
        self.sem_year.setEnabled(False)
        self.document_list.setRowCount(0)
        self.chkbx_show_del_media.setEnabled(True)
        appdata = self.db.getApparatData(appnr, appname)
        self.populate_frame(appdata)
        self.btn_apparat_save.hide()
        self.btn_reserve.show()
        self.chkbx_show_del_media.show()

        self.drpdwn_app_nr.setDisabled(True)
        self.update_app_media_list()
        self.update_document_list()
        return True

    def update_document_list(self):
        app_id = self.active_apparat
        prof_id = self.db.getProfByName(
            self.drpdwn_prof_name.currentText().replace(",", "")
        ).id
        files = self.db.getFiles(app_id, prof_id)
        for file in files:
            self.document_list.insertRow(0)
            self.document_list.setItem(0, 0, QtWidgets.QTableWidgetItem(file[0]))
            self.document_list.setItem(0, 1, QtWidgets.QTableWidgetItem(file[1]))
            self.document_list.setItem(0, 2, QtWidgets.QTableWidgetItem(""))
            self.document_list.setItem(0, 3, QtWidgets.QTableWidgetItem("Database"))
            self.document_list.item(0, 0).setToolTip(file[0])

    def btn_create_new_apparat(self):
        global valid_input
        self.steps.show()
        for child in self.app_group_box.findChildren(QtWidgets.QToolButton):
            child.show()
        # *create a new apparat
        self.btn_apparat_save.show() if self.btn_apparat_save.isHidden() else None
        # clear document_list
        self.__clear_fields()
        self.document_list.setRowCount(0)
        self.cancel_active_selection.setEnabled(True)
        self.app_group_box.setEnabled(True)
        self.add_medium.setEnabled(True)
        self.sem_year.setEnabled(True)
        self.sem_sommer.setEnabled(True)
        self.sem_winter.setEnabled(True)
        self.chkbx_show_del_media.setEnabled(True)
        self.drpdwn_app_nr.setEnabled(True)
        self.app_fach.setEnabled(True)
        self.check_send_mail.show()
        self.check_file.setEnabled(False)
        self.drpdwn_app_nr.setFocus()
        if self.tableWidget_apparat_media.rowCount() > 0:
            self.tableWidget_apparat_media.setRowCount(0)
        # clear all fields
        for item in self.app_group_box.findChildren(QtWidgets.QLineEdit):
            item.clear()
        self.drpdwn_app_nr.clear()
        self.prof_title.clear()
        self.drpdwn_prof_name.clear()
        # set drop down menu for apparat numbers to only available numbers
        taken_app_nrs = self.db.getUnavailableApparatNumbers()
        self.drpdwn_app_nr.addItems([str(i) for i in APP_NRS if i not in taken_app_nrs])

        valid_input = (0, 0, 0, 0, 0, 0)
        self.populate_prof_dropdown()

    def update_progress_label(self, curr, total):
        text = f"Medium {curr}/{total}"
        logger.info(text)
        self.progress_label.setText(text)
        # update tableWidget_apparat_media
        self.update_app_media_list()

    def hide_progress_label(self):
        logger.info("Finished adding media, hiding progress label")
        self.progress_label.hide()
        self.progress_label.setText("Bitte warten...")
        self.line_2.hide()
        self.label_info.hide()

    def btn_add_medium(self):
        media = medienadder_ui()
        media.exec()
        mode = media.mode
        data = media.data
        use_any = media.use_any
        use_exact = media.use_exact
        result = media.result()

        if result == 1:
            self.progress_label.show()
            self.line_2.show()
            self.label_info.show()
            self.progress_label.setText("Bitte warten...")
            if data == []:
                self.confirm_popup(
                    "Bitte mindestens ein Medium hinzufügen!", title="Fehler"
                )

            app_id = self.active_apparat
            prof_id = self.db.getProfId(self.profdata)
            logger.debug(prof_id)
            # check if app_id is in database
            if self.db.checkApparatExistsById(app_id) is False:
                # create apparat
                self.btn_save_apparat(False)
            # create a thread that updates the progress label after each medium

            # self.bookGrabber = None
            bookGrabber = BookGrabber(self.active_apparat)
            bookGrabber.add_values(
                mode=mode,
                prof_id=prof_id,
                app_id=app_id,
                data=data,
                any_book=use_any,
                exact=use_exact,
            )
            bookGrabber.finished.connect(self.hide_progress_label)
            bookGrabber.finished.connect(self.update_app_media_list)
            bookGrabber.updateSignal.connect(self.update_progress_label)

            bookGrabber.start()
            while bookGrabber.isRunning():
                # #print("waiting for thread to finish")
                QtWidgets.QApplication.processEvents()

            # self.__clear_fields()

        else:
            return

    def check_availability(self):
        def _update_progress(current, all_titles):
            self.avail_status.setText("{}/{}".format(current, all_titles))

        def _hide_progress_label():
            self.label_20.hide()
            self.avail_status.hide()
            self.avail_status.setText("0/0")

        # get all links from the table
        # if no index in tableWidget_apparat_media is selected, check all
        if self.tableWidget_apparat_media.currentRow() == -1:
            links = [
                self.tableWidget_apparat_media.item(i, 1).text()
                for i in range(self.tableWidget_apparat_media.rowCount())
                if self.tableWidget_apparat_media.item(i, 4).text() == "❌"
                or self.tableWidget_apparat_media.item(i, 4).text() == ""
            ]
        else:
            links = [
                self.tableWidget_apparat_media.item(
                    self.tableWidget_apparat_media.currentRow(), 1
                ).text()
            ]
        items = len(links)
        self.label_20.setText("Verfügbarkeit wird geprüft, bitte warten...")
        self.label_20.show()
        self.avail_status.setText(f"0/{items}")
        self.avail_status.show()
        books = self.db.getBooks(
            self.active_apparat,
            self.db.getProfId(self.profdata),
            deleted=0,
        )

        # thread = QThread()
        appnumber = self.active_apparat
        # #print(links)
        self.availChecker = AvailChecker(links, appnumber, books=books)
        # availcheck.moveToThread(thread)
        # availcheck.finished.connect(thread.quit)
        self.availChecker.finished.connect(self.availChecker.deleteLater)
        self.availChecker.finished.connect(self.update_app_media_list)
        self.availChecker.updateProgress.connect(_update_progress)
        self.availChecker.finished.connect(_hide_progress_label)
        self.availChecker.start()
        # kill availcheck after completion

        # self.grabbers.append(availcheck)

    def btn_cancel_active_selection(self):
        self.steps.hide()
        # clear the rows of the table
        self.tableWidget_apparat_media.setRowCount(0)
        self.document_list.setRowCount(0)
        self.app_group_box.setEnabled(False)
        self.app_fach.setCurrentText("")
        self.chkbx_show_del_media.hide()
        self.check_send_mail.hide()
        self.btn_reserve.hide()
        self.check_eternal_app.setEnabled(False)
        # set all radio buttons to unchecked
        self.sem_sommer.setChecked(False)
        self.sem_winter.setChecked(False)
        self.check_eternal_app.setChecked(False)
        self.add_medium.setEnabled(False)

        for child in self.app_group_box.findChildren(QtWidgets.QLineEdit):
            child.clear()
        for child in self.app_group_box.findChildren(QtWidgets.QToolButton):
            child.hide()
        self.validate_app_fach()
        self.validate_app_name()
        self.validate_prof_mail()
        self.validate_prof_name()
        self.validate_prof_tel()
        self.validate_semester()

    def update_app_media_list(self):
        deleted = 0 if not self.chkbx_show_del_media.isChecked() else 1
        app_id = self.active_apparat
        prof_id = self.db.getProfId(self.profdata)
        books: list[dict[int, BookData, int]] = self.db.getBooks(
            app_id, prof_id, deleted
        )

        # # #print(books)
        # take the dataclass from the tuple
        # booklist:list[BookData]=[book[0] for book in books]
        self.tableWidget_apparat_media.setRowCount(0)
        for book in books:
            book["id"]
            book_data = book["bookdata"]
            availability = book["available"]
            # bd = BookData().from_string(book)
            # # #print(bd, type(bd))
            # create a new row below the last one
            self.tableWidget_apparat_media.insertRow(
                self.tableWidget_apparat_media.rowCount()
            )
            # #set the data
            self.tableWidget_apparat_media.setItem(
                self.tableWidget_apparat_media.rowCount() - 1,
                0,
                QtWidgets.QTableWidgetItem(book_data.title),
            )
            self.tableWidget_apparat_media.setItem(
                self.tableWidget_apparat_media.rowCount() - 1,
                1,
                QtWidgets.QTableWidgetItem(book_data.signature),
            )
            self.tableWidget_apparat_media.setItem(
                self.tableWidget_apparat_media.rowCount() - 1,
                2,
                QtWidgets.QTableWidgetItem(book_data.edition),
            )
            self.tableWidget_apparat_media.setItem(
                self.tableWidget_apparat_media.rowCount() - 1,
                3,
                QtWidgets.QTableWidgetItem(book_data.author),
            )
            self.tableWidget_apparat_media.setItem(
                self.tableWidget_apparat_media.rowCount() - 1,
                6,
                QtWidgets.QTableWidgetItem(book_data.link),
            )
            if availability == 1:
                # display green checkmark at column 4 in the row
                self.tableWidget_apparat_media.setItem(
                    self.tableWidget_apparat_media.rowCount() - 1,
                    4,
                    QtWidgets.QTableWidgetItem("✅"),
                )
                # set tooltip
                self.tableWidget_apparat_media.item(
                    self.tableWidget_apparat_media.rowCount() - 1, 4
                ).setToolTip("Das Medium wurde im Apparat gefunden")
            else:
                self.tableWidget_apparat_media.setItem(
                    self.tableWidget_apparat_media.rowCount() - 1,
                    4,
                    QtWidgets.QTableWidgetItem("❌"),
                )
                self.tableWidget_apparat_media.item(
                    self.tableWidget_apparat_media.rowCount() - 1, 4
                ).setToolTip("Das Medium wurde nicht im Apparat gefunden")

        # make table link clickable
        # self.tableWidget_apparat_media.itemClicked.connect(self.open_link)
        # self.tableWidget_apparat_media.

    def open_link(self, item):
        def __openLink(link):
            if link == "":
                return
            if "http" not in link:
                link = "https://" + link
            return link
            #

        # get the name of the column
        columnname = self.tableWidget_apparat_media.horizontalHeaderItem(
            item.column()
        ).text()
        if columnname == "Link":
            link = __openLink(item.text())
            if link is not None:
                webbrowser.open(link)
                # os.system("start " + link)
                return
        else:
            pass

    def text_to_clipboard(self):
        app_id = self.active_apparat
        text = f"SQ=select distinct akkey from aupr01 where aufst='{app_id}' union select pr_isn from aks4pd where akruf ='{app_id}'"
        clipboard = QtWidgets.QApplication.clipboard()
        clipboard.setText(text)

    def populate_prof_dropdown(self):
        profs = self.db.getProfs()
        # add empty entry to dropdown and set it as current
        self.drpdwn_prof_name.addItem("Kein Name")
        profs = [f"{prof.lastname}, {prof.firstname}" for prof in profs]
        profs.sort()
        for prof in profs:
            self.drpdwn_prof_name.addItem(prof)

    def add_document(self):
        # #print("Add document")
        picker = FilePicker()
        files = picker.pick_files()
        for file in files:
            # #print(file)
            filename = file.split("/")[-1]
            filetype = filename.split(".")[-1]
            self.document_list.insertRow(0)
            self.document_list.setItem(0, 0, QtWidgets.QTableWidgetItem(filename))
            self.document_list.setItem(0, 1, QtWidgets.QTableWidgetItem(filetype))
            self.document_list.setItem(0, 2, QtWidgets.QTableWidgetItem("*"))
            self.document_list.setItem(0, 3, QtWidgets.QTableWidgetItem(file))
            # set tooltip of row 3 to the file path for each row
            self.document_list.item(0, 3).setToolTip(file)
            self.document_list.item(0, 0).setToolTip(filename)

    def open_document(self):
        _selected_doc_name = ""
        _selected_doc_filetype = ""
        try:
            _selected_doc_name = self.document_list.item(
                self.document_list.currentRow(), 0
            ).text()
            _selected_doc_location = self.document_list.item(
                self.document_list.currentRow(), 3
            ).text()
            _selected_doc_filetype = self.document_list.item(
                self.document_list.currentRow(), 1
            ).text()
        except AttributeError:
            self.confirm_popup("Bitte erst ein document auswählen!", title="Fehler")
            return
        if not _selected_doc_location == "Database":
            path = Path(_selected_doc_location + "/" + _selected_doc_name)
            # path: Path = path.resolve()
            # path.
            # path = path + "/" + _selected_doc_name
            if os.getenv("OS") == "Windows_NT":
                path = path.resolve()
                os.startfile(path)
            else:
                path = path.resolve()
                os.system(f"open {path}")
        else:
            recreateFile(
                _selected_doc_name, self.active_apparat, filetype=_selected_doc_filetype
            )

    def add_media_from_file(self):
        app_id = self.active_apparat
        prof_id = self.db.getProfId(self.profdata)

        def __open_dialog(signatures: list[str]):
            dialog = QtWidgets.QDialog()
            frame = parsed_titles_ui()
            frame.setupUi(dialog)
            dialogger.show()
            frame.signatures = signatures
            frame.populate_table()
            frame.progressBar.setMaximum(len(signatures))
            frame.progressBar.setValue(0)
            frame.progressBar.show()
            frame.count.setText(str(len(signatures)))
            frame.toolButton.click()
            data = frame.return_data()
            # if no data was returned, return

            return data
            # get

        # if files are in the table, and are selected, check for books in the file
        if self.document_list.rowCount() == 0:
            return
        else:
            # if file is selected, check for books in the file
            if self.document_list.currentRow() != -1:
                # #print("File selected")
                file = self.document_list.item(
                    self.document_list.currentRow(), 3
                ).text()

                file_type = self.document_list.item(
                    self.document_list.currentRow(), 1
                ).text()
                file_location = self.document_list.item(
                    self.document_list.currentRow(), 3
                ).text()
                file_name = self.document_list.item(
                    self.document_list.currentRow(), 0
                ).text()
                if file_location == "Database":
                    # create a temporaty file to use, delete it after use
                    temp_file = tempfile.NamedTemporaryFile(
                        delete=False, suffix="." + file_type
                    )
                    temp_file.write(
                        self.db.getBlob(file_name, int(self.active_apparat))
                    )
                    temp_file.close()
                    file = temp_file.name
                if file_type == "pdf":
                    # Todo: implement parser here
                    self.confirm_popup(
                        "PDF Dateien werden noch nicht unterstützt!", title="Fehler"
                    )
                    return
                if file_type == "csv":
                    signatures = csv_to_list(file)
                    data = __open_dialog(signatures)
                    # add the data to the database
                    for book in data:
                        if not isinstance(book, BookData):
                            continue
                        self.db.addBookToDatabase(
                            bookdata=book, app_id=app_id, prof_id=prof_id
                        )
                if file_type == "docx":
                    data = word_to_semap(file)
                    signatures = data.signatures
                    data = __open_dialog(signatures)
                    # if no data was returned, return
                    if data == []:
                        return
                    for book in data:
                        if not isinstance(book, BookData):
                            continue
                        self.db.addBookToDatabase(
                            bookdata=book, app_id=app_id, prof_id=prof_id
                        )
                self.update_app_media_list()
                # #print(len(signatures))

    def btn_check_file_threaded(self):
        # #print("Checking file")
        # get active app_id and prof_id
        self.tableWidget_apparate.setEnabled(False)
        self.tableWidget_apparate.setToolTip(
            "Bitte warten, bis alle Medien hinzugefügt wurden"
        )
        app_id = self.active_apparat
        logger.debug(self.profdata)
        prof_id = self.db.getProfId(self.profdata)

        logger.debug(prof_id)
        # check if apparat in database

        # if app_id not in database, create apparat
        created = False
        if not self.db.checkApparatExistsById(app_id):
            logger.info("Apparat does not exist, creating new apparat")
            # create apparat
            # #print("Creating apparat")
            if not self.btn_save_apparat(False):
                return
            created = True
        if self.document_list.rowCount() == 0:
            logger.info("No file selected")
            self.tableWidget_apparate.setEnabled(True)
            self.tableWidget_apparate.setToolTip("")
            return
        else:
            # if file is selected, check for books in the file
            # #print("File selected")
            file_type = self.document_list.item(
                self.document_list.currentRow(), 1
            ).text()
            file_location = self.document_list.item(
                self.document_list.currentRow(), 3
            ).text()
            file_name = self.document_list.item(
                self.document_list.currentRow(), 0
            ).text()
            logger.info("File selected: {}, {}", file_name, file_location)
            if file_location == "Database":
                file = recreateFile(file_name, app_id, file_type, open=False)
                logger.debug("recreated file from database")
            else:
                if not created:
                    self.add_files(prof_id)
            if file_type == "pdf":
                # Todo: implement parser here
                self.confirm_popup(
                    "PDF Dateien werden nicht unterstützt!", title="Fehler"
                )
                return
            if file_type == "csv":
                signatures = csv_to_list(file)
                # add the data to the database
            if file_type == "docx":
                data = word_to_semap(file)
                logger.info("Converted data from semap file")
                logger.debug("Got the data: {}", data)
                signatures = data.signatures
                logger.info("Got the signatures: {}", signatures)
            signatures = [i for i in signatures if i != ""]
            # logger.debug(signatures)
            # #print("starting thread")
            if prof_id is None:
                prof_id = self.db.getProfId(self.profdata)

                # print("Prof ID is None", prof_id)
            autoGrabber = BookGrabber(self.active_apparat)
            autoGrabber.add_values(
                mode="ARRAY", app_id=app_id, prof_id=prof_id, data=signatures
            )
            self.label_info.show()
            self.progress_label.show()
            self.line_2.show()
            # grabber.finished.connect(thread.quit)
            # self.autoGrabber.finished.connect(self.autoGrabber.deleteLater)
            autoGrabber.finished.connect(self.hide_progress_label)
            autoGrabber.finished.connect(self.unlock_apparate)

            autoGrabber.updateSignal.connect(self.update_progress_label)
            # worker.finished.connect(worker.deleteLater)

            autoGrabber.start()
            while autoGrabber.isRunning():
                QtWidgets.QApplication.processEvents()
            # end of thread
            # self.autoGrabber.exit()
            # self.__clear_fields()
            # self.btn_cancel_active_selection()

    def unlock_apparate(self):
        self.tableWidget_apparate.setEnabled(True)
        self.tableWidget_apparate.setToolTip("")

    def __clear_fields(self):
        self.drpdwn_app_nr.setCurrentText("")
        self.prof_title.clear()
        self.drpdwn_prof_name.clearMask()
        self.drpdwn_prof_name.setCurrentText("")
        self.app_name.clear()
        self.prof_mail.clear()
        self.prof_tel_nr.clear()
        self.app_fach.setCurrentText("")
        self.app_name.clear()
        self.sem_year.clear()
        self.document_list.setRowCount(0)
        self.sem_winter.setChecked(False)
        self.sem_sommer.setChecked(False)
        self.check_eternal_app.setChecked(False)
        self.prof_id_adis.clear()
        self.prof_id_adis.clear()
        self.apparat_id_adis.clear()
        self.drpdwn_prof_name.clear()
        self.tableWidget_apparat_media.setRowCount(0)
        self.app_group_box.setEnabled(False)
        self.check_send_mail.setChecked(False)
        self.cancel_active_selection.setEnabled(False)
        # check if sem_sommer, sem_winter and check_eternal_app are checked. if yes, set them to unchecked

    def btn_save_apparat(self, clear_fields=True):
        self.steps.hide()
        if not self.validate_fields():
            self.confirm_popup("Bitte alle Pflichtfelder ausfüllen!", title="Fehler")
            return False
        prof = Prof(
            fullname=self.drpdwn_prof_name.currentText(),
            telnr=self.prof_tel_nr.text(),
            mail=self.prof_mail.text(),
        )
        prof.title = self.prof_title.text()
        apparat = Apparat(
            appnr=self.active_apparat,
            name=self.app_name.text(),
            created_semester=self.generateSemester(),
            eternal=1 if self.check_eternal_app.isChecked() else 0,
            subject=self.app_fach.currentText(),
            deleted=0,
            prof_id_adis=self.prof_id_adis.text(),
            apparat_id_adis=self.apparat_id_adis.text(),
        )

        appd = ApparatData(prof=prof, apparat=apparat)

        error = self.db.createApparat(appd)
        if error:
            self.confirm_popup(error.__str__(), title="Fehler")
            return False
        if self.document_list.rowCount() > 0:
            self.add_files()
        if error is not None:
            self.confirm_popup(error.__str__(), title="Fehler")
            return
        appdata = self.db.getAllAparats()
        # merge self.appdata and appdata, remove duplicates
        self.apparats = list(set(self.apparats + appdata))
        self.apparats = natsorted(self.apparats, key=lambda x: x[4], reverse=True)

        self.update_apparat_list()

        # self.btn_load_apparat()

        if self.check_send_mail.isChecked():
            self.contact_prof(
                apparat=appd.apparat.appnr,
                mail="Information zum Semesterapparat",
                location="",
                pid=appd.prof.fullname,
            )
        if clear_fields:
            # #print("clearing fields")
            self.__clear_fields()
        return True

    def send_mail_preview(self):
        pass

    @property
    def active_apparat(self):
        return self.drpdwn_app_nr.currentText()

    @property
    def profdata(self):
        return {
            "title": self.prof_title.text(),
            "profname": self.drpdwn_prof_name.currentText(),
            "prof_mail": self.prof_mail.text(),
            "prof_tel": self.prof_tel_nr.text(),
        }

    def add_files(self, prof_id=None):
        files = []
        for i in range(self.document_list.rowCount()):
            files.append(
                {
                    "name": self.document_list.item(i, 0).text(),
                    "type": self.document_list.item(i, 1).text(),
                    "date": self.document_list.item(i, 2).text(),
                    "path": self.document_list.item(i, 3).text(),
                }
            )
            self.document_list.item(i, 2).setText("")

        self.db.insertFile(
            files,
            self.active_apparat,
            self.db.getProfId(
                {
                    "title": self.prof_title.text(),
                    "profname": self.drpdwn_prof_name.currentText(),
                    "prof_mail": self.prof_mail.text(),
                    "prof_tel": self.prof_tel_nr.text(),
                }
            ),
        )

    def update_apparat_list(self):
        self.tableWidget_apparate.setRowCount(0)

        for apparat in self.apparats:
            self.insert_apparat_into_table(apparat)
        logger.info("Inserted {} apparats into table".format(len(self.apparats)))

    def insert_apparat_into_table(self, apparat):
        # logger.debug(apparat)
        def __dauer_check(apparat):
            return "Ja" if apparat[7] == 1 else "Nein"

        semester = apparat[8] if apparat[8] is not None else apparat[5]
        self.tableWidget_apparate.insertRow(0)
        self.tableWidget_apparate.setItem(
            0, 0, QtWidgets.QTableWidgetItem(str(apparat[4]))
        )
        self.tableWidget_apparate.setItem(
            0, 1, QtWidgets.QTableWidgetItem(str(apparat[1]))
        )
        self.tableWidget_apparate.setItem(
            0,
            2,
            QtWidgets.QTableWidgetItem(
                self.db.getProfNameById(apparat[2], add_title=False)
            ),
        )
        self.tableWidget_apparate.setItem(
            0,
            3,
            QtWidgets.QTableWidgetItem(str(semester)),
        )
        self.tableWidget_apparate.setItem(
            0, 4, QtWidgets.QTableWidgetItem(__dauer_check(apparat))
        )
        self.tableWidget_apparate.setItem(
            0, 5, QtWidgets.QTableWidgetItem(str(apparat[13]))
        )

    def open_context_menu(self, position):
        menu = QtWidgets.QMenu()
        extend_action = menu.addAction("Verlängern")
        contact_action = menu.addAction("Kontaktieren")
        delete_action = menu.addAction("Löschen")
        remind_action = menu.addAction("Erinnerung")
        menu.addAction(extend_action)
        menu.addActions([contact_action, delete_action, remind_action])
        extend_action.triggered.connect(self.extend_apparat)
        remind_action.triggered.connect(self.reminder)
        # convert point to row and column
        row = self.tableWidget_apparate.rowAt(position.y())
        column = self.tableWidget_apparate.columnAt(position.x())
        pos = (str(row), str(column))
        if len(self.tableWidget_apparate.selectedItems()) == 0:
            return
        app_id = self.tableWidget_apparate.item(row, 0).text()
        pid = self.db.getProfIDByApparat(app_id)
        logger.debug(app_id, pid)
        delete_action.triggered.connect(lambda: self.delete_apparat(pos))
        # pass pos to contact_prof
        contact_action.triggered.connect(
            lambda: self.contact_prof(pid=pid, apparat=app_id)
        )
        menu.exec(self.tableWidget_apparate.mapToGlobal(position))

    def reminder(self):
        logger.info("Opening reminder dialog")
        reminder = reminder_ui()
        reminder.exec()
        tableposition = self.tableWidget_apparate.currentRow()
        appnr = self.tableWidget_apparate.item(tableposition, 0).text()
        if reminder.result() == QtWidgets.QDialogger.DialogCode.Accepted:
            data = reminder.return_message()
            # #print(data)
            self.db.addMessage(
                data,
                self.active_user,
                self.active_apparat if self.active_apparat != "" else appnr,
            )
            self.update_calendar(data)
            # self.db.update_bookdata(data, book_id)
            # self.db.update_bookdata(data)
            logger.info("committed message to database")
            # self.update_app_media_list()

    def get_reminders(self):
        messages = self.db.getAllMessages()
        logger.info(f"Got {len(messages)} messages from database")
        self.calendarWidget.setMessages(messages)
        self.calendarWidget.updateCells()

    def open_reminder(self):
        selected_date = self.calendarWidget.selectedDate().toString("yyyy-MM-dd")
        # # #print(selected_date)
        messages = self.db.getMessages(selected_date)
        if messages == []:
            return

        dialog = CalendarEntry(messages=messages, date=selected_date)
        # append dialog to self.frame_2
        self.calendarlayout.addWidget(dialog)
        dialogger.repaintSignal.connect(
            lambda: self.calendarWidget.reload(selected_date)
        )

    def open_settings(self):
        # print(settings.dict())
        settingsUI = Settings(self.active_user)
        settingsUI.exec()

        if settingsUI.result() == QtWidgets.QDialogger.DialogCode.Accepted:
            settingsUI.save()
            # print(settings.dict())

    #            self.reload()

    def reload(self):
        state = self.confirm_popup(
            "Bitte das Programm neustarten, um Änderungen zu übernehemen",
            "Einstellungen geändert",
        )
        if state == 1:
            exit()
        else:
            pass

    def media_context_menu(self, position):
        menu = QtWidgets.QMenu()

        delete_action = QtGui.QAction("Löschen")
        edit_action = QtGui.QAction("Bearbeiten")
        apparat_add_action = QtGui.QAction("Zum Apparat hinzufügen")
        apparat_move_action = QtGui.QAction("In Apparat verschieben")
        apparat_copy_action = QtGui.QAction("In Apparat kopieren")

        apparatmenu = menu.addMenu("Apparate")
        generalmenu = menu.addMenu("Allgemeines")
        apparatmenu.addActions(
            [apparat_add_action, apparat_copy_action, apparat_move_action]
        )
        generalmenu.addActions([edit_action, delete_action])
        # disable apparat_add_action
        apparat_add_action.setEnabled(False)
        delete_action.triggered.connect(self.delete_medium)
        edit_action.triggered.connect(self.edit_medium)
        apparat_add_action.triggered.connect(self.add_to_apparat)
        apparat_copy_action.triggered.connect(self.copy_to_apparat)
        apparat_move_action.triggered.connect(self.move_to_apparat)
        menu.exec(self.tableWidget_apparat_media.mapToGlobal(position))

    def copy_to_apparat(self):
        selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows()
        signatures = []
        for row in selected_rows:
            signature = self.tableWidget_apparat_media.item(row.row(), 1).text()
            book_id = self.db.getBookIdBasedOnSignature(
                self.active_apparat,
                self.db.getProfId(self.profdata),
                signature,
            )
            signatures.append(book_id)
        result, apparat = self.confirm_action_dialog(
            "In welchen Apparat sollen die Medien kopiert werden?"
        )
        if result == 1:
            for book_id in signatures:
                self.db.copyBookToApparat(book_id, apparat)
        else:
            return

    def move_to_apparat(self):
        selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows()
        signatures = []
        for row in selected_rows:
            signature = self.tableWidget_apparat_media.item(row.row(), 1).text()
            book_id = self.db.getBookIdBasedOnSignature(
                self.active_apparat,
                self.db.getProfId(self.profdata),
                signature,
            )
            signatures.append(book_id)
        result, apparat = self.confirm_action_dialog(
            "In welchen Apparat sollen die Medien verschoben werden?"
        )
        if result == 1:
            for book_id in signatures:
                self.db.moveBookToApparat(book_id, apparat)
            self.update_app_media_list()
        else:
            return

    def confirm_action_dialog(self, message, title="Bestätigung"):
        appnrs = self.db.getUnavailableApparatNumbers()
        appnrs = [str(i) for i in appnrs]
        appnrs.remove(self.active_apparat)
        if len(appnrs) == 0:
            # create a warning dialog, saying no apparats present
            self.confirm_popup("Keine weiteren Apparate vorhanden", title="Fehler")
            return (None, None)
        dialog = QtWidgets.QDialog()
        dialogger.setWindowTitle(title)
        # add a label to the dialog
        label = QtWidgets.QLabel()
        label.setText(message)
        # add a combobox to the dialog
        drpdwn = QtWidgets.QComboBox()
        drpdwn.addItems(appnrs)
        drpdwn.addItem("")

        # set layout of dialog
        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(label)
        layout.addWidget(drpdwn)

        # add buttons
        okay_button = QtWidgets.QPushButton("Okay")
        cancel_button = QtWidgets.QPushButton("Abbrechen")
        layout.addWidget(okay_button)
        layout.addWidget(cancel_button)
        okay_button.clicked.connect(dialogger.accept)
        cancel_button.clicked.connect(dialogger.reject)

        dialogger.setLayout(layout)

        return dialogger.exec(), self.db.getApparatId(
            self.db.getApparatNameByAppNr(drpdwn.currentText())
        )

    def add_to_apparat(self):
        """use playwright in background to add medium to apparat"""
        signature = self.tableWidget_apparat_media.item(
            self.tableWidget_apparat_media.currentRow(), 1
        ).text()
        self.db.getBookBasedOnSignature(
            self.drpdwn_app_nr.currentText(),
            signature=signature,
            prof_id=self.db.getProfId(self.profdata),
        )
        # print(medium.adis_idn, medium.signature)

    def edit_medium(self):
        book = self.tableWidget_apparat_media.item(
            self.tableWidget_apparat_media.currentRow(), 1
        ).text()
        prof_id = self.db.getProfId(self.profdata)
        data = self.db.getBookBasedOnSignature(
            app_id=self.active_apparat,
            signature=book,
            prof_id=prof_id,
        )
        book_id = self.db.getBookIdBasedOnSignature(
            self.active_apparat,
            prof_id,
            book,
        )
        widget = QtWidgets.QDialog()
        bookedit = edit_bookdata_ui()
        bookedit.setupUi(widget)
        widget.setWindowIcon(Icon("settings").icon)
        # change title of dialog
        widget.setWindowTitle("Metadaten")
        bookedit.populate_fields(data)
        widget.exec()
        if widget.result() == QtWidgets.QDialogger.DialogCode.Accepted:
            data = bookedit.get_data()
            # #print(data)
            self.db.updateBookdata(bookdata=data, book_id=book_id)
            # self.db.update_bookdata(data)
            # #print("accepted")
            self.update_app_media_list()
        else:
            return
        pass

    def delete_medium(self):
        selected_apparat_id = self.tableWidget_apparate.item(
            self.tableWidget_apparate.currentRow(), 0
        ).text()
        prof_id = self.db.getProfId(self.profdata)
        # check how many rows are selected
        selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows()
        if len(selected_rows) == 1:
            signature = self.tableWidget_apparat_media.item(
                self.tableWidget_apparat_media.currentRow(), 1
            ).text()
            book_id = self.db.getBookIdBasedOnSignature(
                selected_apparat_id,
                prof_id=prof_id,
                signature=signature,
            )
            message = f'Soll das Medium "{self.tableWidget_apparat_media.item(self.tableWidget_apparat_media.currentRow(), 0).text()}" wirklich gelöscht werden?'
            state = self.confirm_popup(message, title="Löschen?")
            # #print(state)
            if state == 1:
                self.db.deleteBook(book_id)
                self.update_app_media_list()
            pass
        else:
            # get all selected rows
            ranges = self.tableWidget_apparat_media.selectedRanges()
            rows = []
            for r in ranges:
                for row in range(r.topRow(), r.bottomRow() + 1):
                    rows.append(row)
            # #print(rows)
            message = f"Sollen die {len(rows)} Medien wirklich gelöscht werden?"
            state = self.confirm_popup(message, title="Löschen?")
            if state == 1:
                for _ in rows:
                    signature = self.tableWidget_apparat_media.item(_, 1).text()
                    book_id = self.db.getBookIdBasedOnSignature(
                        app_id=selected_apparat_id,
                        prof_id=prof_id,
                        signature=signature,
                    )
                    self.db.deleteBook(book_id)
                    self.update_app_media_list()

    def extend_apparat(self):
        framework = ApparatExtendDialog()
        framework.exec()
        # return data from dialog if ok is pressed
        if framework.result() == QtWidgets.QDialogger.DialogCode.Accepted:
            data = framework.get_data()
            # #print(data)
            # return data
            selected_apparat_id = self.tableWidget_apparate.item(
                self.tableWidget_apparate.currentRow(), 0
            ).text()
            # #print(selected_apparat_id)

            self.db.setNewSemesterDate(
                selected_apparat_id, data["semester"], dauerapp=data["dauerapp"]
            )
            # update the table
            self.get_apparats()
        else:
            return

    def __contact_dialog(self, apparat, location: tuple | str, mail=None, pid=""):
        logger.debug(
            "Got these values apparat: {}, location: {}, mail: {}, pid: {}".format(
                apparat, location, mail, pid
            )
        )

        active_apparat_id = (
            self.drpdwn_app_nr.currentText() if apparat is None else apparat
        )
        if not active_apparat_id:
            # get column 0 of the selected row
            pass
        if isinstance(pid, str):
            prof_id = self.db.getProfByName(pid).id
        else:
            prof_id = pid
        # if profname == "Name Kein":
        #    profname = pid
        if self.app_name.text() != "":
            app_name = self.app_name.text()
        else:
            app_name = self.db.getApparatName(active_apparat_id, prof_id)
        if self.app_fach.currentText() != "":
            app_subject = self.app_fach.currentText()
        else:
            app_subject = self.db.getApparatData(active_apparat_id, app_name)
            app_subject = app_subject.apparat.subject
        if prof_id:
            pmail = self.db.getSpecificProfData(prof_id, ["mail"])
            prof_name = self.db.getSpecificProfData(prof_id, ["fullname"])
        else:
            pmail = self.prof_mail.text()
        self.mail_thread = Mail_Dialog(
            app_id=active_apparat_id,
            prof_name=prof_name,
            prof_mail=pmail,
            app_name=app_name,
            app_subject=app_subject,
            default_mail=mail if mail != "" else "Information zum Semesterapparat",
        )
        self.mail_thread.show()

    def contact_prof(self, apparat="", location="", mail="", pid=""):
        logger.debug(apparat)
        logger.debug(location)
        if self.active_apparat == "":
            if apparat is False:
                self.confirm_popup(
                    "Bitte erst einen Apparat auswählen!", title="Apparat auswählen"
                )
                return

        self.__contact_dialog(apparat, mail=mail, pid=pid, location=location)

    def delete_apparat(self, position):
        selected_apparat_id = self.tableWidget_apparate.item(
            self.tableWidget_apparate.currentRow(), 0
        ).text()
        message = f"Soll der Apparat {selected_apparat_id} wirklich gelöscht werden?"
        state = self.confirm_popup(message, title="Löschen?")
        # #print(state)
        logger.info("Result state: {}", state)
        if state == 1:
            logger.debug("Deleting apparat {}", selected_apparat_id)
            pid = self.db.getProfIDByApparat(selected_apparat_id)
            self.db.deleteApparat(selected_apparat_id, Semester().value)
            # delete the corresponding entry from self.apparats
            for apparat in self.apparats:
                if apparat[4] == int(selected_apparat_id):
                    self.apparats.remove(apparat)
                    break
            self.old_apparats = self.apparats
            # #print(self.apparats)
            # remove the row from the table
            self.tableWidget_apparate.removeRow(self.tableWidget_apparate.currentRow())
            # send mail to prof
            self.contact_prof(mail="deleted", apparat=selected_apparat_id, pid=pid)


def launch_gui():
    # #print("trying to login")
    # #print("checking if database available")

    logger.info("Starting login dialog")
    app = QtWidgets.QApplication(sys.argv)
    login_dialog = QtWidgets.QDialog()
    ui = login_ui()
    ui.setupUi(login_dialog)
    login_dialog.exec()  # This will block until the dialog is closed

    if ui.lresult == 1:
        # if login is successful, open main window
        # show login dialog
        # #print(ui.lusername)
        MainWindow = QtWidgets.QMainWindow()
        aui = Ui(MainWindow, username=ui.lusername)

        # #print(aui.active_user)
        MainWindow.show()
        # atexit.register()
        atexit.register(tempdelete)
        atexit.register(aui.validate_thread.quit)
        sys.exit(app.exec())

    elif ui.lresult == 0:
        warning_dialog = QtWidgets.QMessageBox()
        warning_dialogger.setIcon(QtWidgets.QMessageBox.Icon.Warning)
        warning_dialogger.setText("Invalid username or password. Please try again.")
        warning_dialogger.setWindowTitle("Login Failed")
        warning_dialogger.exec()
    atexit.register(tempdelete)


if __name__ == "__main__":
    # #print("This is the main window")
    # app = QtWidgets.QApplication(sys.argv)
    # window = MainWindow()
    # app.exec()
    # open login screen
    launch_gui()
