import os
import sqlite3
from typing import Any

import sympy as sp
from sympy import MutableDenseMatrix

from Berechnungen import Einheitenumrechnung
from decimal import Decimal



class Datenbank_anlegen:
    def __init__(self, pfad_datenbank: str) -> None:
        self.pfad_datenbank = pfad_datenbank
        self.db_anlegen()

    def db_anlegen(self) -> None:
        # pfad = r"C:\Users\fabia\OneDrive\Jade HS\Master\MGW2\Masterprojekt_allgemein\Masterprojekt\Programmierung\Campusnetz\Campusnetz.db"
        if not os.path.exists(self.pfad_datenbank):
            con = sqlite3.connect(self.pfad_datenbank)
            cursor = con.cursor()
            cursor.executescript("""CREATE TABLE Netzpunkte (
                           punktnummer TEXT(10),
                           naeherungx_lh NUMERIC(9,3),
                            naeherungy_lh NUMERIC(7,3),
                            naeherungz_lh NUMERIC(8,3),
                            naeherungz_lh_niv NUMERIC(8,3),
                            naeherungx_us NUMERIC(9,3),
                            naeherungy_us NUMERIC(7,3),
                            naeherungz_us NUMERIC(8,3),
                            normalhoehe_hfp NUMERIC(5,3),
                            datumskoordinate_x INTEGER DEFAULT 0,
                            datumskoordinate_y INTEGER DEFAULT 0, 
                            datumskoordinate_z INTEGER DEFAULT 0,
                            stabw_vorinfo_x NUMERIC(3, 8),
                            stabw_vorinfo_y NUMERIC(3, 8),
                            stabw_vorinfo_z NUMERIC(3, 8),
                            CONSTRAINT pk_Netzpunkte PRIMARY KEY (punktnummer)
                            );
                           """);
            cursor.executescript("""CREATE TABLE Beobachtungen(
                                    beobachtungenID INTEGER,
                                    beobachtungsgruppeID INTEGER,
                                    punktnummer_sp TEXT(10),
                                    punktnummer_zp TEXT(10),
                                    instrumenteID INTEGER,
                                    tachymeter_richtung NUMERIC(8, 6),
                                    tachymeter_richtung_ausschalten INTEGER DEFAULT 0,
                                    tachymeter_zenitwinkel_roh NUMERIC(8, 6),
                                    tachymeter_zenitwinkel NUMERIC(8, 6),
                                    tachymeter_zenitwinkel_ausschalten INTEGER DEFAULT 0,
                                    tachymeter_distanz_roh NUMERIC(8, 4),
                                    tachymeter_distanz NUMERIC(8, 4),
                                    tachymeter_distanz_auschalten INTEGER DEFAULT 0,
                                    tachymeter_instrumentenhoehe NUMERIC(3, 8),
                                    tachymeter_prismenhoehe NUMERIC(3, 8),
                                    gnss_bx NUMERIC(7, 4),
                                    gnss_bx_ausschalten INTEGER DEFAULT 0,
                                    gnss_by NUMERIC(7, 4),
                                    gnss_by_ausschalten INTEGER DEFAULT 0,
                                    gnss_bz NUMERIC(7, 4),
                                    gnss_bz_ausschalten INTEGER DEFAULT 0,
                                    gnss_s0                          NUMERIC(1, 8),
                                    gnss_cxx                          NUMERIC(1, 8),
                                    gnss_cxy                          NUMERIC(1, 8),
                                    gnss_cxz                          NUMERIC(1, 8),
                                    gnss_cyy                          NUMERIC(1, 8),
                                    gnss_cyz                          NUMERIC(1, 8),
                                    gnss_czz                          NUMERIC(1, 8),
                                    niv_dh                            NUMERIC(8, 6),
                                    niv_strecke                       NUMERIC(8, 6),
                                    niv_anz_standpkte                 INTEGER,
                                    niv_ausschalten INTEGER DEFAULT 0,
                                    dateiname TEXT(200),
                                    CONSTRAINT pk_Beobachtungen PRIMARY KEY (beobachtungenID),
                                    CONSTRAINT fk_Beobachtungen_Netzpunktesp FOREIGN KEY (punktnummer_sp) REFERENCES Netzpunkte(punktnummer),
                                    CONSTRAINT fk_Beobachtungen_Netzpunktezp FOREIGN KEY (punktnummer_zp) REFERENCES Netzpunkte(punktnummer),
                                    CONSTRAINT fk_Beobachtungen_Instrumente FOREIGN KEY (instrumenteID) REFERENCES Instrumente(instrumenteID)
                                    );
                                 """);
            cursor.executescript("""CREATE TABLE Instrumente(
                                    instrumenteID INTEGER,
                                    typ TEXT(200),
                                    name TEXT(200),
                                    CONSTRAINT pk_Instrumente PRIMARY KEY (instrumenteID)
                                    );
                                    """);
            cursor.executescript("""CREATE TABLE Genauigkeiten(
                                    genauigkeitenID INTEGER,
                                    instrumenteID INTEGER,
                                    beobachtungsart TEXT(25),
                                    stabw_apriori_konstant NUMERIC(3, 8),
                                    stabw_apriori_streckenprop NUMERIC(3, 8),
                                    CONSTRAINT pk_Genauigkeiten PRIMARY KEY (genauigkeitenID),
                                    CONSTRAINT fk_Genauigkeiten_Instrumente FOREIGN KEY (instrumenteID) REFERENCES Instrumente(instrumenteID)
                                    );
                                    """);
            cursor.executescript("""CREATE TABLE Varianzkomponentenschaetzung(
                                    varianzkomponenteID INTEGER,
                                    instrumenteID INTEGER,
                                    beobachtungsgruppe TEXT(50),
                                    varianz_varianzkomponentenschaetzung NUMERIC(3, 16) DEFAULT 1,
                                    s0_apriori_vorherige_iteration NUMERIC(3, 16) DEFAULT 1,
                                    CONSTRAINT pk_Varianzkomponentenschaetzung PRIMARY KEY (varianzkomponenteID),
                                    CONSTRAINT fk_Varianzkomponentenschaetzung_Instrumente FOREIGN KEY (instrumenteID) REFERENCES Instrumente(instrumenteID)
                                    );
                                    """)

            con.commit()
            cursor.close()
            con.close()

class Datenbankzugriff:
    def __init__(self, pfad_datenbank: str) -> None:
        self.pfad_datenbank = pfad_datenbank

    def set_koordinaten(self, dict_koordinaten: dict, koordinatenart: str) -> None:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()

        daten = []

        for punktnummer, wert in dict_koordinaten.items():
            daten.append((
                float(wert[0]),
                float(wert[1]),
                float(wert[2]),
                str(punktnummer)
            ))

        if koordinatenart == "naeherung_lh":
            pass
        elif koordinatenart == "naeherung_us":
            cursor.executemany(f"""UPDATE Netzpunkte SET naeherungx_us = ?, naeherungy_us = ?, naeherungz_us = ? WHERE punktnummer = ? AND naeherungx_us IS NULL AND naeherungy_us IS NULL AND naeherungz_us IS NULL""", daten)


        con.commit()
        cursor.close()
        con.close()

    def set_instrument(self, typ: str, name: str, liste_beobachtungsarten: list) -> None:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        liste_instrumente = cursor.execute("SELECT * FROM Instrumente WHERE typ = ? AND name =?", (typ, name)).fetchall()
        if liste_instrumente == []:
            cursor.execute(
                "INSERT INTO Instrumente (typ, name) VALUES (?, ?)", (typ, name)
            )
            id_instrument = cursor.lastrowid
            print(f"Das Instrument {name} wurde erfolgreich hinzugefügt.")

            if self.get_varianzkomponentenschaetzung() == []:
                cursor.execute(
                    "INSERT INTO Instrumente (typ, name) VALUES (?, ?)", ("Anschlusspunkte", "lA")
                )
                id_instrument_anschlusspunkte = cursor.lastrowid

                cursor.execute(
                    "INSERT INTO Varianzkomponentenschaetzung (instrumenteID, beobachtungsgruppe) VALUES (?, ?)",
                    (id_instrument_anschlusspunkte, "Anschlusspunkte")
                )

            for beobachtungsart in liste_beobachtungsarten:
                    cursor.execute(
                        "INSERT INTO Varianzkomponentenschaetzung (instrumenteID, beobachtungsgruppe) VALUES (?, ?)",
                        (id_instrument, beobachtungsart)
                    )
        else:
            id_instrument = cursor.execute(
                "SELECT instrumenteID FROM Instrumente WHERE typ = ? AND name =?", (typ, name))
            print(f"Das Instrument {name} ist bereits in der Datenbank vorhanden.\nEs hat die ID {id_instrument.fetchone()[0]}")
        con.commit()
        cursor.close()
        con.close()

    def set_genauigkeiten(self, instrumenteID: int, beobachtungsart: str, stabw_apriori_konstant: float = None,
                          stabw_apriori_streckenprop: float = None) -> None:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()

        instrumentenname = cursor.execute("SELECT name FROM Instrumente WHERE instrumenteID = ?",
                                          (instrumenteID, )).fetchone()
        if instrumentenname is None:
            print(
                f"Die InstumentenID {instrumenteID} ist in der Datenbank nicht vorhanden. Bitte zuerst das Instrument hinzufügen.")
            cursor.close()
            con.close()
            return
        instrumentenname = instrumentenname[0]

        if stabw_apriori_konstant is None and stabw_apriori_streckenprop is None:
            print(
                "Es wurden keine Genauigkeiten importiert. Bitte stabw_apriori_konstant und / oder stabw_apriori_streckenprop angeben.")
            cursor.close()
            con.close()
            return

        if beobachtungsart == "Tachymeter_Richtung" or beobachtungsart == "Tachymeter_Zenitwinkel" :
            stabw_apriori_konstant = Einheitenumrechnung.mgon_to_rad_Decimal(stabw_apriori_konstant)

        if beobachtungsart == "Tachymeter_Strecke" or beobachtungsart == "Geometrisches_Nivellement":
            stabw_apriori_konstant = Einheitenumrechnung.mm_to_m(stabw_apriori_konstant)

        if isinstance(stabw_apriori_konstant, Decimal):
            stabw_apriori_konstant = float(stabw_apriori_konstant)

        if isinstance(stabw_apriori_streckenprop, Decimal):
            stabw_apriori_streckenprop = float(stabw_apriori_streckenprop)

        sql = "SELECT 1 FROM Genauigkeiten WHERE instrumenteID = ? AND beobachtungsart = ?"
        params = [instrumenteID, beobachtungsart]

        if stabw_apriori_konstant is None:
            sql += " AND stabw_apriori_konstant IS NULL"
        else:
            sql += " AND stabw_apriori_konstant = ?"
            params.append(stabw_apriori_konstant)

        if stabw_apriori_streckenprop is None:
            sql += " AND stabw_apriori_streckenprop IS NULL"
        else:
            sql += " AND stabw_apriori_streckenprop = ?"
            params.append(stabw_apriori_streckenprop)

        liste_genauigkeiten = cursor.execute(sql, tuple(params)).fetchall()

        if liste_genauigkeiten == []:
            if stabw_apriori_konstant is not None and stabw_apriori_streckenprop is not None:
                cursor.execute(
                    "INSERT INTO Genauigkeiten (instrumenteID, beobachtungsart, stabw_apriori_konstant, stabw_apriori_streckenprop) VALUES (?, ?, ?, ?)",
                    (instrumenteID, beobachtungsart, stabw_apriori_konstant, stabw_apriori_streckenprop)
                )
                print(
                    f"Die Genauigkeitsangabe für die Beobachtungsart {beobachtungsart} des Instrumentes {instrumentenname} wurde erfolgreich hinzugefügt.")

            elif stabw_apriori_konstant is None and stabw_apriori_streckenprop is not None:
                cursor.execute(
                    "INSERT INTO Genauigkeiten (instrumenteID, beobachtungsart, stabw_apriori_streckenprop) VALUES (?, ?, ?)",
                    (instrumenteID, beobachtungsart, stabw_apriori_streckenprop)
                )
                print(
                    f"Die Genauigkeitsangabe für die Beobachtungsart {beobachtungsart} des Instrumentes {instrumentenname} wurde erfolgreich hinzugefügt.")

            elif stabw_apriori_streckenprop is None and stabw_apriori_konstant is not None:
                cursor.execute(
                    "INSERT INTO Genauigkeiten (instrumenteID, beobachtungsart, stabw_apriori_konstant) VALUES (?, ?, ?)",
                    (instrumenteID, beobachtungsart, stabw_apriori_konstant)
                )
                print(
                    f"Die Genauigkeitsangabe für die Beobachtungsart {beobachtungsart} des Instrumentes {instrumentenname} wurde erfolgreich hinzugefügt.")
        else:
            print("Die Genauigkeitsangabe ist bereits in der Datenbank vorhanden.")

        con.commit()
        cursor.close()
        con.close()

    def set_datumskoordinaten(self, liste_datumskoordinaten_x: list, liste_datumskoordinaten_y: list,
                              liste_datumskoordinaten_z: list,
                              liste_datumskoordinaten_x_y_z: list) -> None:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()

        liste_stabw_vorinfo_x = [str(row[0]).strip() for row in cursor.execute(
            "SELECT punktnummer FROM Netzpunkte WHERE stabw_vorinfo_x IS NOT NULL").fetchall()]
        liste_stabw_vorinfo_y = [str(row[0]).strip() for row in cursor.execute(
            "SELECT punktnummer FROM Netzpunkte WHERE stabw_vorinfo_y IS NOT NULL").fetchall()]
        liste_stabw_vorinfo_z = [str(row[0]).strip() for row in cursor.execute(
            "SELECT punktnummer FROM Netzpunkte WHERE stabw_vorinfo_z IS NOT NULL").fetchall()]
        liste_stabw_vorinfo_x_y_z = [str(row[0]).strip() for row in cursor.execute(
            "SELECT punktnummer FROM Netzpunkte WHERE stabw_vorinfo_x IS NOT NULL AND stabw_vorinfo_y IS NOT NULL AND stabw_vorinfo_z IS NOT NULL").fetchall()]

        if liste_datumskoordinaten_x != []:
                for punktnummer in liste_datumskoordinaten_x:
                    punktnummer = str(punktnummer).strip()
                    if punktnummer in liste_stabw_vorinfo_x:
                        cursor.execute(f"UPDATE Netzpunkte SET datumskoordinate_x = 1 WHERE punktnummer = ? AND stabw_vorinfo_x IS NOT NULL", (str(punktnummer),))
                    else:
                        print(f"Die X-Koordinate des Punktes {punktnummer} wurde nicht in eine Datumskoordinate geändert, weil keine Vorinformationen zur Standardabweichung der X-Koordinate des Punktes vorliegen. Diese bitte zuerst erfassen und Datumsdefinition wiederholen.")
        if liste_datumskoordinaten_y != []:
            for punktnummer in liste_datumskoordinaten_y:
                punktnummer = str(punktnummer).strip()
                if punktnummer in liste_stabw_vorinfo_y:
                    cursor.execute(f"UPDATE Netzpunkte SET datumskoordinate_y = 1 WHERE punktnummer = ? AND stabw_vorinfo_y IS NOT NULL", (str(punktnummer),))
                else:
                    print(f"Die Y-Koordinate des Punktes {punktnummer} wurde nicht in eine Datumskoordinate geändert, weil keine Vorinformationen zur Standardabweichung der Y-Koordinate des Punktes vorliegen. Diese bitte zuerst erfassen und Datumsdefinition wiederholen.")

        if liste_datumskoordinaten_z != []:
            for punktnummer in liste_datumskoordinaten_z:
                punktnummer = str(punktnummer).strip()
                if punktnummer in liste_stabw_vorinfo_z:
                    cursor.execute(f"UPDATE Netzpunkte SET datumskoordinate_z = 1 WHERE punktnummer = ? AND stabw_vorinfo_z IS NOT NULL", (str(punktnummer),))
                else:
                    print(f"Die Z-Koordinate des Punktes {punktnummer} wurde nicht in eine Datumskoordinate geändert, weil keine Vorinformationen zur Standardabweichung der Z-Koordinate des Punktes vorliegen. Diese bitte zuerst erfassen und Datumsdefinition wiederholen.")

        if liste_datumskoordinaten_x_y_z != []:
            for punktnummer in liste_datumskoordinaten_x_y_z:
                punktnummer = str(punktnummer).strip()
                if punktnummer in liste_stabw_vorinfo_x_y_z:
                    cursor.execute(f"UPDATE Netzpunkte SET datumskoordinate_x = 1, datumskoordinate_y = 1, datumskoordinate_z = 1 WHERE punktnummer = ? AND stabw_vorinfo_x IS NOT NULL AND stabw_vorinfo_y IS NOT NULL AND stabw_vorinfo_z IS NOT NULL", (str(punktnummer),))
                else:
                    print(f"Der Punkt {punktnummer} wurde nicht in einen Datumspunkt geändert, weil nicht alle Vorinformationen zur Standardabweichung der Koordinaten des Punktes vorliegen. Diese bitte zuerst erfassen und Datumsdefinition wiederholen.")

        con.commit()
        cursor.close()
        con.close()

    def set_datumskoordinaten_to_neupunkte(self, liste_datumskoordinaten_x: list, liste_datumskoordinaten_y: list,
                                           liste_datumskoordinaten_z: list,
                                           liste_datumskoordinaten_x_y_z: list) -> None:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        if liste_datumskoordinaten_x != []:
            for punktnummer in liste_datumskoordinaten_x:
                punktnummer = str(punktnummer).strip()
                cursor.execute(
                    f"UPDATE Netzpunkte SET datumskoordinate_x = 0 WHERE punktnummer = ?",
                    (str(punktnummer),))
        if liste_datumskoordinaten_y != []:
            for punktnummer in liste_datumskoordinaten_y:
                punktnummer = str(punktnummer).strip()
                cursor.execute(
                    f"UPDATE Netzpunkte SET datumskoordinate_y = 0 WHERE punktnummer = ?",
                    (str(punktnummer),))
        if liste_datumskoordinaten_z != []:
            for punktnummer in liste_datumskoordinaten_z:
                punktnummer = str(punktnummer).strip()
                cursor.execute(
                    f"UPDATE Netzpunkte SET datumskoordinate_z = 0 WHERE punktnummer = ?",
                    (str(punktnummer),))
        if liste_datumskoordinaten_x_y_z != []:
            for punktnummer in liste_datumskoordinaten_x_y_z:
                punktnummer = str(punktnummer).strip()
                cursor.execute(
                    f"UPDATE Netzpunkte SET datumskoordinate_x = 0, datumskoordinate_y = 0, datumskoordinate_z = 0 WHERE punktnummer = ?",
                    (str(punktnummer),))

        con.commit()
        cursor.close()
        con.close()

    def set_normalhoehe_hfp(self, liste_normalhoehe_hfp: list) -> LiteralString | str:
        liste_hfp_in_db = self.get_normalhoehe_hfp()
        if liste_normalhoehe_hfp != []:
            con = sqlite3.connect(self.pfad_datenbank)
            cursor = con.cursor()
            liste_punktnummern_in_db = [str(r[0]) for r in cursor.execute(
                "SELECT punktnummer FROM Netzpunkte"
            ).fetchall()]
            ausgaben = []
            for hfp in liste_normalhoehe_hfp:
                if str(hfp[0]) in liste_punktnummern_in_db:
                    cursor.execute("UPDATE Netzpunkte SET normalhoehe_hfp = ? WHERE punktnummer = ?",
                        (hfp[3], hfp[0])
                    )
                    ausgaben.append(f"Der HFP {hfp[0]} wurde aktualisiert.")
                else:
                    cursor.execute(
                        "INSERT INTO Netzpunkte (punktnummer, normalhoehe_hfp) VALUES (?, ?)",
                        (hfp[0], hfp[1])
                    )
                    ausgaben.append(f"Der HFP {hfp[0]} wurde neu hinzugefügt.")

            con.commit()
            cursor.close()
            con.close()
            return "\n".join(ausgaben)
        else:
            return f"Es wurden keine neuen Normalhöhen übergeben. Folgende Normalhöhen sind in der Datenbank enthalten: {liste_hfp_in_db}"

    def set_beobachtung_ausschalten(self, dict_beobachtung_ausschalten: dict) -> None:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()

        for beobachtung, ausschalten in dict_beobachtung_ausschalten.items():
            if ausschalten == "beobachtung_ausschalten":
                beobachtung_gesplittet = beobachtung.split("_")
                if beobachtung_gesplittet[1] == "SD":
                    #print(f"SD: {beobachtung_gesplittet}")
                    try:
                        cursor.execute(f"""UPDATE Beobachtungen SET tachymeter_distanz_auschalten = 1 
                        WHERE beobachtungenID = ? AND beobachtungsgruppeID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
                                       (beobachtung_gesplittet[0], beobachtung_gesplittet[2], beobachtung_gesplittet[3], beobachtung_gesplittet[4]))
                    except:
                        print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
                if beobachtung_gesplittet[1] == "R":
                    #print(f"R: {beobachtung_gesplittet}")
                    try:
                        cursor.execute(f"""UPDATE Beobachtungen SET tachymeter_richtung_ausschalten = 1 
                        WHERE beobachtungenID = ? AND beobachtungsgruppeID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
                                       (beobachtung_gesplittet[0], beobachtung_gesplittet[2], beobachtung_gesplittet[3], beobachtung_gesplittet[4]))
                    except:
                        print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")

                if beobachtung_gesplittet[1] == "ZW":
                    #print(f"ZW: {beobachtung_gesplittet}")
                    try:
                        cursor.execute(f"""UPDATE Beobachtungen SET tachymeter_zenitwinkel_ausschalten = 1 
                        WHERE beobachtungenID = ? AND beobachtungsgruppeID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
                                       (beobachtung_gesplittet[0], beobachtung_gesplittet[2], beobachtung_gesplittet[3], beobachtung_gesplittet[4]))
                    except:
                        print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")

                if beobachtung_gesplittet[1] == "gnssbx":
                    #print(f"gnssbx: {beobachtung_gesplittet}")
                    try:
                        cursor.execute(f"""UPDATE Beobachtungen SET gnss_bx_ausschalten = 1 
                        WHERE beobachtungenID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
                                       (beobachtung_gesplittet[0], beobachtung_gesplittet[2], beobachtung_gesplittet[3]))
                    except:
                        print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")


                if beobachtung_gesplittet[1] == "gnssby":
                    #print(f"gnssby: {beobachtung_gesplittet}")
                    try:
                        cursor.execute(f"""UPDATE Beobachtungen SET gnss_by_ausschalten = 1 
                        WHERE beobachtungenID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
                                       (beobachtung_gesplittet[0], beobachtung_gesplittet[2], beobachtung_gesplittet[3]))
                    except:
                        print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")

                if beobachtung_gesplittet[1] == "gnssbz":
                    #print(f"gnssbz: {beobachtung_gesplittet}")
                    try:
                        cursor.execute(f"""UPDATE Beobachtungen SET gnss_bz_ausschalten = 1 
                        WHERE beobachtungenID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
                                       (beobachtung_gesplittet[0], beobachtung_gesplittet[2], beobachtung_gesplittet[3]))
                    except:
                        print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")

                if beobachtung_gesplittet[1] == "niv":
                    #print(f"niv: {beobachtung_gesplittet}")
                    try:
                        cursor.execute(f"""UPDATE Beobachtungen SET niv_ausschalten = 1 
                        WHERE beobachtungenID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
                                       (beobachtung_gesplittet[0], beobachtung_gesplittet[2], beobachtung_gesplittet[3]))
                    except:
                        print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
        print("Die ausgewählten Beobachtungen werden für die folgenden Iterationen ausgeschaltet.")
        con.commit()
        cursor.close()
        con.close()

    def set_varianzkomponente(self, liste_varianzkomponten_anpassen: list) -> None:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        if liste_varianzkomponten_anpassen != []:
            for varianzkomponente in liste_varianzkomponten_anpassen:
                cursor.execute(
                    f"UPDATE Varianzkomponentenschaetzung SET varianz_varianzkomponentenschaetzung = ? WHERE instrumenteID = ? AND beobachtungsgruppe = ?",
                    (varianzkomponente[2], varianzkomponente[0], varianzkomponente[1]))
            print(f"Folgende Varianzkomponente wurde für die nächste Iteration gespeichert: {liste_varianzkomponten_anpassen}.")
        con.commit()
        cursor.close()
        con.close()

    def set_s0_apriori(self, liste_s0_apriori_anpassen: list) -> None:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        if liste_s0_apriori_anpassen != []:
            for s0_apriori in liste_s0_apriori_anpassen:
                cursor.execute(
                    f"UPDATE Varianzkomponentenschaetzung SET s0_apriori_vorherige_iteration = ? WHERE instrumenteID = ? AND beobachtungsgruppe = ?",
                    (s0_apriori[2], s0_apriori[0], s0_apriori[1]))
            print(f"Folgende S0_apriori wurde für die nächste Iteration gespeichert: {liste_s0_apriori_anpassen}.")
        con.commit()
        cursor.close()
        con.close()


    def get_koordinaten(self, koordinatenart: str, ausgabeart: str = "Dict") -> dict[Any, MutableDenseMatrix] | None:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        if koordinatenart == "naeherung_lh":
            values = "punktnummer, naeherungx_lh, naeherungy_lh, naeherungz_lh"
        elif koordinatenart == "naeherung_us":
            values = "punktnummer, naeherungx_us, naeherungy_us, naeherungz_us"

        liste_koordinaten = cursor.execute(f"""
        SELECT {values} FROM Netzpunkte;
        """).fetchall()
        cursor.close()
        con.close()

        liste_koordinaten = [
            koordinate for koordinate in liste_koordinaten
            if koordinate[1] is not None and koordinate[2] is not None and koordinate[3] is not None
        ]
        if ausgabeart == "Dict":
            return {
                koordinate[0]: sp.Matrix([
                    sp.Float(str(koordinate[1])),
                    sp.Float(str(koordinate[2])),
                    sp.Float(str(koordinate[3]))
                ])
                for koordinate in liste_koordinaten
            }

    def get_normalhoehe_hfp(self) -> list[Any]:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        liste_hfp = cursor.execute("SELECT punktnummer, normalhoehe_hfp FROM Netzpunkte WHERE normalhoehe_hfp IS NOT NULL").fetchall()
        cursor.close()
        con.close()
        return liste_hfp


    def get_instrument_liste(self, typ: str) -> list:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        liste_instrumente = cursor.execute("SELECT * FROM Instrumente WHERE typ = ?", (typ,)).fetchall()
        liste_typen = cursor.execute("SELECT DISTINCT typ FROM Instrumente").fetchall()
        cursor.close()
        con.close()
        if liste_instrumente == []:
            liste_instrumente = f"Kein Instrument vom Typ {typ} gefunden. Folgende Typen stehen aktuell zur Auswahl: {liste_typen}"
        return liste_instrumente

    def get_genauigkeiten_dict(self) -> dict[Any, Any]:
        dict = {}
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        liste_genauigkeiten = cursor.execute("SELECT * FROM Genauigkeiten").fetchall()
        for genauigkeit in liste_genauigkeiten:
            dict[genauigkeit[0]] = genauigkeit[1:]
        cursor.close()
        con.close()
        return dict

    def get_instrumenteID_beobachtungenID_dict(self) -> dict[Any, Any]:
        dict = {}
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        liste_beobachtungen_instrumente = cursor.execute("SELECT beobachtungenID, instrumenteID FROM Beobachtungen").fetchall()
        for i in liste_beobachtungen_instrumente:
            dict[i[0]] = i[1]
        cursor.close()
        con.close()
        return dict

    def get_beobachtungen_id_beobachtungsgruppe_standpunkt_zielpunkt(self, beobachtungsart: str) -> list[Any]:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        sql_ausdruck = f"SELECT beobachtungenID, beobachtungsgruppeID, punktnummer_sp, punktnummer_zp FROM Beobachtungen WHERE {beobachtungsart} IS NOT NULL"
        if beobachtungsart == "tachymeter_distanz":
            sql_ausdruck += " AND tachymeter_distanz_auschalten = 0"
        elif beobachtungsart == "tachymeter_richtung":
            sql_ausdruck += " AND tachymeter_richtung_ausschalten = 0"
        elif beobachtungsart == "tachymeter_zenitwinkel":
            sql_ausdruck += " AND tachymeter_zenitwinkel_ausschalten = 0"
        liste_beobachtungen = cursor.execute(sql_ausdruck).fetchall()
        cursor.close()
        con.close()
        return liste_beobachtungen

    def get_beobachtungen_from_beobachtungenid(self) -> list[Any]:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        liste_beobachtungen = cursor.execute(f"SELECT punktnummer_sp, punktnummer_zp, beobachtungenID, beobachtungsgruppeID, tachymeter_richtung, tachymeter_zenitwinkel, tachymeter_distanz FROM Beobachtungen WHERE tachymeter_richtung IS NOT NULL").fetchall()
        cursor.close()
        con.close()
        return liste_beobachtungen

    def get_beobachtungen_gnssbasislinien(self) -> list[Any]:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        liste_beobachtungen = cursor.execute(f"SELECT beobachtungenID, punktnummer_sp, punktnummer_zp, gnss_bx, gnss_by, gnss_bz, gnss_s0, gnss_cxx, gnss_cxy, gnss_cxz, gnss_cyy, gnss_cyz, gnss_czz FROM Beobachtungen WHERE gnss_bx IS NOT NULL AND gnss_by IS NOT NULL AND gnss_bz IS NOT NULL AND gnss_s0 IS NOT NULL AND gnss_cxx IS NOT NULL AND gnss_cxy IS NOT NULL AND gnss_cxz IS NOT NULL AND gnss_cyy IS NOT NULL AND gnss_cyz IS NOT NULL AND gnss_czz IS NOT NULL").fetchall()
        cursor.close()
        con.close()
        return liste_beobachtungen

    def get_beobachtungen_nivellement(self) -> list[Any]:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        liste_beobachtungen = cursor.execute(f"SELECT beobachtungenID, punktnummer_sp, punktnummer_zp, niv_dh, niv_strecke, niv_anz_standpkte FROM Beobachtungen WHERE niv_dh IS NOT NULL AND niv_strecke IS NOT NULL AND niv_anz_standpkte IS NOT NULL AND niv_ausschalten = 0").fetchall()
        cursor.close()
        con.close()
        return liste_beobachtungen

    def get_datumskoordinate(self) -> list[Any]:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        liste_datumskoordinate_x = cursor.execute(
            f"SELECT punktnummer FROM Netzpunkte WHERE datumskoordinate_x = 1").fetchall()
        liste_datumskoordinate_y = cursor.execute(
            f"SELECT punktnummer FROM Netzpunkte WHERE datumskoordinate_y = 1").fetchall()
        liste_datumskoordinate_z = cursor.execute(
            f"SELECT punktnummer FROM Netzpunkte WHERE datumskoordinate_z = 1").fetchall()

        cursor.close()
        con.close()

        liste_datumskoordinaten = []
        if liste_datumskoordinate_x != []:
            for datumskoordinate in liste_datumskoordinate_x:
                datumskoordinate = str(datumskoordinate[0]).strip()
                liste_datumskoordinaten.append(f"X{datumskoordinate}")
        if liste_datumskoordinate_y != []:
            for datumskoordinate in liste_datumskoordinate_y:
                datumskoordinate = str(datumskoordinate[0]).strip()
                liste_datumskoordinaten.append(f"Y{datumskoordinate}")
        if liste_datumskoordinate_z != []:
            for datumskoordinate in liste_datumskoordinate_z:
                datumskoordinate = str(datumskoordinate[0]).strip()
                liste_datumskoordinaten.append(f"Z{datumskoordinate}")

        return liste_datumskoordinaten

    def get_stabw_AA_Netzpunkte(self) -> dict[Any, Any]:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        liste_stabwAA_x = cursor.execute(f"SELECT punktnummer, stabw_vorinfo_x FROM Netzpunkte WHERE stabw_vorinfo_x IS NOT NULL").fetchall()
        liste_stabwAA_y = cursor.execute(
            f"SELECT punktnummer, stabw_vorinfo_y FROM Netzpunkte WHERE stabw_vorinfo_y IS NOT NULL").fetchall()
        liste_stabwAA_z = cursor.execute(
            f"SELECT punktnummer, stabw_vorinfo_z FROM Netzpunkte WHERE stabw_vorinfo_z IS NOT NULL").fetchall()

        cursor.close()
        con.close()

        dict_stabwAA = {}
        for stabwAA_x in liste_stabwAA_x:
            punktnummer = str(stabwAA_x[0]).strip()
            wert = stabwAA_x[1]
            dict_stabwAA[f"StabwAA_X{punktnummer}"]= wert

        for stabwAA_y in liste_stabwAA_y:
            punktnummer = str(stabwAA_y[0]).strip()
            wert = stabwAA_y[1]
            dict_stabwAA[f"StabwAA_Y{punktnummer}"]= wert

        for stabwAA_z in liste_stabwAA_z:
            punktnummer = str(stabwAA_z[0]).strip()
            wert = stabwAA_z[1]
            dict_stabwAA[f"StabwAA_Z{punktnummer}"]= wert

        return dict_stabwAA

    def get_gnss_beobachtungen_punktnummern(self, gnss_komponente: str) -> list[Any]:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        sql = f"""SELECT beobachtungenID, punktnummer_sp, punktnummer_zp
                      FROM Beobachtungen
                      WHERE {gnss_komponente} IS NOT NULL
                        AND gnss_s0 IS NOT NULL
                        AND gnss_cxx IS NOT NULL AND gnss_cxy IS NOT NULL AND gnss_cxz IS NOT NULL
                        AND gnss_cyy IS NOT NULL AND gnss_cyz IS NOT NULL AND gnss_czz IS NOT NULL"""

        if gnss_komponente == "gnss_bx":
            sql += " AND gnss_bx_ausschalten = 0"
        elif gnss_komponente == "gnss_by":
            sql += " AND gnss_by_ausschalten = 0"
        elif gnss_komponente == "gnss_bz":
            sql += " AND gnss_bz_ausschalten = 0"

        liste_gnss_beobachtungen = cursor.execute(sql).fetchall()
        cursor.close()
        con.close()
        return liste_gnss_beobachtungen

    def get_nivellement_beobachtungen_punktnummern(self) -> list[Any]:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        liste_nivellement_beobachtungen = cursor.execute(f"SELECT beobachtungenID, punktnummer_sp, punktnummer_zp FROM Beobachtungen WHERE niv_dh IS NOT NULL AND niv_strecke IS NOT NULL AND niv_anz_standpkte IS NOT NULL AND niv_ausschalten = 0").fetchall()
        cursor.close()
        con.close()
        return liste_nivellement_beobachtungen

    def get_varianzkomponentenschaetzung(self) -> list[Any]:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        liste_varianzkomponenten = cursor.execute(f"SELECT varianzkomponenteID, instrumenteID, beobachtungsgruppe, varianz_varianzkomponentenschaetzung FROM Varianzkomponentenschaetzung").fetchall()
        cursor.close()
        con.close()
        return liste_varianzkomponenten

    def get_s0_apriori(self) -> list[Any]:
        con = sqlite3.connect(self.pfad_datenbank)
        cursor = con.cursor()
        liste_s0_apriori = cursor.execute(f"SELECT varianzkomponenteID, instrumenteID, beobachtungsgruppe, varianz_varianzkomponentenschaetzung, s0_apriori_vorherige_iteration FROM Varianzkomponentenschaetzung").fetchall()
        cursor.close()
        con.close()
        return liste_s0_apriori


