"""
Module with the classes for the tool manager and machine manager
"""

import os
import math
import json
import datetime
import numpy as np

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import utils.MethodsWerkzeugauslegung as W_Auslegung
from utils.globalSettings import (
    # Import GUI Settings to access global settings
    GUI_SETTINGS,
    # with Language Settings language pack can be accessed
    LANGUAGE_SETTINGS,
    PROCESSING_SETTINGS_FILE
)
from utils.commonFunctions import *
from LayoutElements import setupTabmenu, mainWindowGUI

# JSON NAMES contains json names as mentioned in the table
JSON_NAMES_TOOL = [
    "toolname",
    "spitzenwinkel",
    "d_kopf",
    "schneidenwinkel",
    "d_schaft",
    "l_kopf",
    "l_kopf_gesamt",
    "l_werkzeug",
    "freiwinkel",
    "winkeloffset",
    "l_schaft",
    "l_werkzeug_gesamt",
    "d_schaft2"
    ]

DEFAULTTOOLCONFIG = {
    "toolname": "---",
    "d_kopf": 0.0,
    "l_kopf": 0.0,
    "l_kopf_gesamt": 0.0,
    "d_schaft": 0.0,
    "l_werkzeug": 0.0,
    "l_werkzeug_gesamt": 0.0,
    "l_schaft": 0.0,
    "d_schaft2": 0.0,
    "spitzenwinkel": 0.0,
    "schneidenwinkel": 0.0,
    "freiwinkel": 0.0,
    "winkeloffset": 0.0
}

DEFAULTMACHINECONFIG = {
    "Name": "---",
    "Steuerung": "Sinumerik 840d sl (lagegeregelter Spindelbetrieb)",
    "WKS": "---",
    "MCodeWerkzeugwechsel": "---",
    "MCodeDrehachsenKlemmen": "---",
    "MCodeDrehachsenLoesen": "---",
    "MCodeKontrollMaschinenhalt": "---",
    "MCodeEinschaltenKSS": "---",
    "MCodeAusschaltenKSS": "---",
    "MCodeEinschaltenSpindel": "---",
    "MCodeEinschaltenSpindelAlt": "---",
    "MCodeAusschaltenSpindel": "---"
}

SICHERHEIT= GUI_SETTINGS["safteyTolerance"]

# Definiton of Validators for QLineEdits for tools
TOOL_EDIT_LINE_VALIDATORS = [
    lambda default, statusBar: stringEditLine(default, False, "[a-zA-Z0-9_\s\-]+", statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInput"]),
    lambda default, statusBar: doubleEditLine(default, 0, 180, 2, statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "12,34"),
    lambda default, statusBar: doubleEditLine(default, 0, float("inf"), 2, statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "12,34"),
    lambda default, statusBar: doubleEditLine(default, 0, float("inf"), 2, statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "12,34"),
    lambda default, statusBar: doubleEditLine(default, 0, float("inf"), 2, statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "12,34"),
    lambda default, statusBar: doubleEditLine(default, 0, float("inf"), 2, statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "12,34"),
    lambda default, statusBar: doubleEditLine(default, 0, float("inf"), 2, statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "12,34"),
    lambda default, statusBar: doubleEditLine(default, 0, float("inf"), 2, statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "12,34"),
    lambda default, statusBar: doubleEditLine(default, 0, 90, 2, statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "12,34"),
    lambda default, statusBar: doubleEditLine(default, -360, 360, 2, statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "12,34"),
    lambda default, statusBar: doubleEditLine(default, 0, float("inf"), 2, statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "12,34"),
    lambda default, statusBar: doubleEditLine(default, 0, float("inf"), 2, statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "12,34"),
    lambda default, statusBar: doubleEditLine(default, 0, float("inf"), 2, statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "12,34")
]


JSON_NAMES_MACHINE = [
    "Name",
    "Steuerung",
    "WKS", 
    "MCodeWerkzeugwechsel", 
    "MCodeDrehachsenKlemmen", 
    "MCodeDrehachsenLoesen",
    "MCodeKontrollMaschinenhalt",
    "MCodeEinschaltenKSS", 
    "MCodeAusschaltenKSS", 
    "MCodeEinschaltenSpindel", 
    "MCodeEinschaltenSpindelAlt",
    "MCodeAusschaltenSpindel" 
]
MACHINE_EDIT_LINE_VALIDATORS = [
    lambda default, statusBar: stringEditLine(default, False, "[a-zA-Z0-9_\s\-]+", statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInput"]), 
    lambda default, statusBar: stringEditLine(default, False, "[a-zA-Z0-9_\s\-]+", statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInput"]), 
    lambda default, statusBar: stringEditLine(default, True, "((G|g)[0-9]+)?", statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "G**"), 
    lambda default, statusBar: stringEditLine(default, True, "((M|m)[0-9]+\s?)+", statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "M**"), 
    lambda default, statusBar: stringEditLine(default, True, "((M|m)[0-9]+\s?)+", statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "M**"), 
    lambda default, statusBar: stringEditLine(default, True, "((M|m)[0-9]+\s?)+", statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "M**"), 
    lambda default, statusBar: stringEditLine(default, True, "((M|m)[0-9]+\s?)+", statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "M**"), 
    lambda default, statusBar: stringEditLine(default, True, "((M|m)[0-9]+\s?)+", statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "M**"), 
    lambda default, statusBar: stringEditLine(default, True, "((M|m)[0-9]+\s?)+", statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "M**"), 
    lambda default, statusBar: stringEditLine(default, True, "((M|m)[0-9]+\s?)+", statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "M**"), 
    lambda default, statusBar: stringEditLine(default, True, "((M|m)[0-9]+\s?)+", statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "M**"), 
    lambda default, statusBar: stringEditLine(default, True, "((M|m)[0-9]+\s?)+", statusBar, LANGUAGE_SETTINGS.languagePack["configManager"]["rejectedInputSyntax"] + "M**")
]

#degines default paramter boudnaries for tools
DEFAULT_WERKZEUG_PAR_BOUNDARIES = {
    "toolname": None,
    "d_kopf": (0, float("inf")),
    "l_kopf": (0, float("inf")),
    "l_kopf_gesamt": (0, float("inf")),
    "d_schaft": (0, float("inf")),
    "l_werkzeug": (0, float("inf")),
    "l_werkzeug_gesamt": (0, float("inf")),
    "l_schaft": (0, float("inf")),
    "d_schaft2": (0, float("inf")),
    "spitzenwinkel": (0, 180),
    "schneidenwinkel": (0, 90),
    "freiwinkel": (0, 90),
    "winkeloffset": (-360, 360),
    "FilePath": None}


STLYE_LOCKED_FIELD = "color: #AAAAAA; border: none"
STYLE_OPEN_FIELD = "color: #000000; border: none"

class configManager(QWidget):
    """General Class for the tool and machine Manager"""

    def __init__(self, tabmenu: setupTabmenu, configDir: str, jsonNames: list[str], lineValidators: list[callable], flag_tool: bool = True) -> None:
        """Create the standard configManager on wich the tool manager an machine manager is based,"""
        super().__init__()

        self.needToBeFilled = []
        self.tabmenu: setupTabmenu = tabmenu
        self.configTable = QTableWidget()
        self.configDir = configDir
        self.jsonNames = jsonNames
        self.lineValidators = lineValidators
        self.parameterBoundaries = {}  # Dictionary of Tuples with (min,max) boundaries
        self.tool_key = "tool"
        if not flag_tool:
            self.tool_key = "machine"

        if flag_tool:
            # add expert Flag
            self.flag_expert = False

        self.layout = QVBoxLayout()
        self.thisStatusBar = QStatusBar()  # Statusbar
        self.configTable.currentCellChanged.connect(self.cellChanged)

        self.buttonLayout = QHBoxLayout()
        self.buttonLayout.addSpacing(500)

        self.stop_blinking_function = None
        self.mainWindow_message_function = None

        # Add expert Modus
        if self.tool_key == "tool":
            self.expertButton = QPushButton(LANGUAGE_SETTINGS.languagePack["configManager"]["expertModus"])
            self.expertButton.clicked.connect(lambda: self.expertModus())

        # create Buttons for Table
        self.editButton = QPushButton(LANGUAGE_SETTINGS.languagePack["configManager"]["editButton"][self.tool_key])
        self.editButton.clicked.connect(lambda: self.makeEditable())
        self.createNewButton = QPushButton(LANGUAGE_SETTINGS.languagePack["configManager"]["createNewButton"][self.tool_key])
        self.createNewButton.clicked.connect(lambda: self.createNew())
        self.saveButton = QPushButton(LANGUAGE_SETTINGS.languagePack["configManager"]["saveButton"])
        self.saveButton.clicked.connect(lambda: self.saveConfig())
        self.deleteButton = QPushButton(LANGUAGE_SETTINGS.languagePack["configManager"]["deleteModeButton"][self.tool_key])
        self.deleteButton.clicked.connect(lambda: self.deleteConfig())

        if self.tool_key == "tool":
            self.buttonLayout.addWidget(self.expertButton)
        self.buttonLayout.addWidget(self.editButton)
        self.buttonLayout.addWidget(self.deleteButton)
        self.buttonLayout.addWidget(self.createNewButton)
        self.buttonLayout.addWidget(self.saveButton)

        self.layout.addLayout(self.buttonLayout)

        # add Table to Layout, Layout is populated in init of its manager
        self.layout.addWidget(self.configTable)
        self.layout.addWidget(self.thisStatusBar)
        self.setLayout(self.layout)

    def showManager(self) -> None:
        """shows the manager"""
        self.updateContents()
        self.show()

    def closeEvent(self, event):
        """Handle the widget close event"""
        if self.stop_blinking_function:
            self.stop_blinking_function()

        if self.mainWindow_message_function:
            # Check of ToolMangaer or MachineManager
            if self.tool_key == "tool":
                self.mainWindow_message_function(LANGUAGE_SETTINGS.languagePack["setupTabmenu"]["toolTab"]["MaschinenverwaltungClosed"], 5000)
            else:
                self.mainWindow_message_function(LANGUAGE_SETTINGS.languagePack["setupTabmenu"]["machineTab"]["MaschinenverwaltungClosed"], 5000)

        # Call the base class method to ensure the widget is closed
        event.accept()

    def set_close_functions(self, stop_blinking, mainWindow_message):
        self.stop_blinking_function = stop_blinking
        self.mainWindow_message_function = mainWindow_message

    def loadConfigs(self, path: str) -> list[dict]:
        """load the configs into the manager from selected path, returns configs in a list of dicts"""

        configs = list()

        if os.path.isdir(path):
            for f in os.listdir(path):
                filePath = path + f
                if os.path.isfile(filePath) and filePath.endswith('.json'):
                    with open(filePath, 'r') as config:
                        try: 
                            jsonConfig = json.load(config)
                            jsonConfig.update({"FilePath": filePath})
                            configs.append(jsonConfig)
                        except:
                            pass

        return configs

    def updateContents(self) -> None:
        """Clears the table in the manager an reloads all configs"""
        self.configTable.clearContents()
        self.configTable.setRowCount(0)
        self.populateRows(self.loadConfigs(self.configDir), self.jsonNames, self.lineValidators)

    def updateParBounds(self,thisConfig:dict):
        """parent method will be overwritten in child class"""
        pass

    def IsParInRange(self, x: float, bot: float, top: float) -> bool:
        """checks if value x is in range defined by bot and top"""
        if (x>=bot and x<=top):
            return True
        return False

    def setToolStatus(self):
        jsonConfig = {}
        selectedTool = {}
        IsOutOfRange = False

        if os.path.exists(PROCESSING_SETTINGS_FILE):
            with open(PROCESSING_SETTINGS_FILE, 'r') as config:
                try:
                    jsonConfig = json.load(config)
                except json.JSONDecodeError:
                    pass
        else:
            # Log an error message or handle the missing file case
            print(f"{PROCESSING_SETTINGS_FILE} does not exist.")

        toolpath = jsonConfig.get("toolPath")

        if toolpath and os.path.exists(toolpath):
            with open(toolpath, 'r') as config:
                try:
                    selectedTool = json.load(config)
                except json.JSONDecodeError:
                    pass
        else:
            # Log an error message or handle the missing file case
            if toolpath:
                print(f"{toolpath} does not exist.")
                print("Select Default tool parameter")
                selectedTool = DEFAULTTOOLCONFIG

        if selectedTool:
            IsOutOfRange, names = self.checkLineValidators(selectedTool, self.jsonNames, self.lineValidators)

        if IsOutOfRange:
            self.tabmenu.werkzeugstatus.setText(
                LANGUAGE_SETTINGS.languagePack["setupTabmenu"]["toolTab"]["WerkzeugNichtGeignet"])
            text = LANGUAGE_SETTINGS.languagePack["setupTabmenu"]["toolTab"]["WerkzeugNichtGeignet_tooltip"]
            for name in names:
                text = text + "\n - " + LANGUAGE_SETTINGS.languagePack["setupTabmenu"]["toolTab"][name]
            self.tabmenu.werkzeugstatus.setToolTip(text)
            self.tabmenu.werkzeugstatus.setStyleSheet(f'background-color: #e85154; color: black;')
        else:
            self.tabmenu.werkzeugstatus.setText(
                LANGUAGE_SETTINGS.languagePack["setupTabmenu"]["toolTab"]["WerkzeugGeignet"])
            self.tabmenu.werkzeugstatus.setToolTip(LANGUAGE_SETTINGS.languagePack["setupTabmenu"]["toolTab"]["WerkzeugGeignet"])
            self.tabmenu.werkzeugstatus.setStyleSheet(f'background-color: green; color: white;')

    def checkLineValidators(self, config: dict, jsonNames: list[str], lineValidators: list[callable]):
        IsOutOfRange = False
        names = []

        currentGeometry = self.tabmenu.generateGeometryJson()
        try:
            self.updateParBounds(currentGeometry, config)
        except ZeroDivisionError as e:
            print(f"Error (checkLineValidators): Division by zero occurred - {e}")
            # Handle division by zero
            IsOutOfRange = True
            names.append('Berechnungsfehler')
            return IsOutOfRange, names
        except ValueError as e:
            print(f"Error (checkLineValidators): Invalid value encountered - {e}")
            # Handle square root of a negative number or other value errors
            IsOutOfRange = True
            names.append('Berechnungsfehler')
            return IsOutOfRange, names
        except Exception as e:
            print(f"An unexpected error occurred (checkLineValidators)- {e}")
            # Handle any other unexpected errors
            IsOutOfRange = True
            names.append('Berechnungsfehler')
            return IsOutOfRange, names

        if len(jsonNames) != len(lineValidators):
            print('jsonNames and LineValidators do not have the same length')
            print('The Program might not work properly')

        for thisName, thisValidator in zip(jsonNames, lineValidators):
            print(thisName)
            # thisLineEdit = QLineEdit(str(thisConfig[thisName]))

            # Check if the key `thisName` exists in the dictionary, if not, add it with the default value
            if thisName not in config:
                if self.tool_key == "tool":
                    config[thisName] = DEFAULTTOOLCONFIG[thisName]
                else:
                    config[thisName] = DEFAULTMACHINECONFIG[thisName]

            thisLineEdit: QLineEditAnimated = thisValidator(str(config[thisName]), self.thisStatusBar)

            # checks if there are parameterBoundaries and if so checks if value is in range
            if (self.parameterBoundaries[thisName] != None):
                try:
                    config_value = float(config[thisName])
                except ValueError:
                    # Handle the case where the value cannot be converted
                    config_value = 0
                if not self.IsParInRange(x=config_value, bot=self.parameterBoundaries[thisName][0],
                                         top=self.parameterBoundaries[thisName][1]):
                    names.append(thisName)
                    IsOutOfRange = True
        return IsOutOfRange, names

    def populateRows(self, configs: dict, jsonNames: list[str], lineValidators: list[callable]) -> None:
        """
        populates alls Rows in Manager with the values from configs,
        jsonNames are used to access values in configs,
        lineValidators is al list of functions wich returns corrosponding QLineEditsAnimated
        """

        i = 0
        #get
        self.currentGeometry = self.tabmenu.generateGeometryJson()
        geometryConifg= self.currentGeometry
        for thisConfig in configs:
            #update Boundaries
            try:
                self.updateParBounds(geometryConifg, thisConfig)
            except ZeroDivisionError as e:
                print(f"Error (populateRows): Division by zero occurred - {e}")
                # Handle division by zero
            except ValueError as e:
                print(f"Error (populateRows): Invalid value encountered - {e}")
                # Handle square root of a negative number or other value errors
            except Exception as e:
                print(f"An unexpected error occurred (populateRows)- {e}")
                # Handle any other unexpected errors

            self.configTable.insertRow(i)

            # path is saved in column 0 and hidden
            tableItem = QTableWidgetItem(str(thisConfig["FilePath"]))
            tableItem.setFlags(Qt.NoItemFlags)
            self.configTable.setItem(i, 0, tableItem)

            selectButton = QPushButton(LANGUAGE_SETTINGS.languagePack["configManager"]["selectButton"])
            # bind thisConfig to tmpConfig as secondary parameter in lambda function as first parameter comes from button
            selectButton.clicked.connect(lambda event, tmpConfig=thisConfig: self.returnSelected(tmpConfig))

            self.configTable.setCellWidget(i, 1, selectButton)

            j = 2
            ToolTip = LANGUAGE_SETTINGS.languagePack["configManager"]["OutOfRange"]
            IsOutOfRange=False

            if len(jsonNames) != len(lineValidators):
                print('jsonNames and LineValidators do not have the same length')
                print('The Program might not work properly')

            for thisName, thisValidator in zip(jsonNames, lineValidators):
                #thisLineEdit = QLineEdit(str(thisConfig[thisName]))
                if thisName == "Steuerung":
                    thisComboBox = self.createSteuerungComboBox(current_value=thisConfig[thisName])
                    thisComboBox.setDisabled(True)
                    #thisComboBox.setStyleSheet(STYLE_LOCKED_FIELD)
                    self.configTable.setCellWidget(i, j, thisComboBox)
                else:
                    if thisName not in thisConfig:
                        if self.tool_key == "tool":
                            thisConfig[thisName] = DEFAULTTOOLCONFIG[thisName]
                        else:
                            thisConfig[thisName] = DEFAULTMACHINECONFIG[thisName]

                    thisLineEdit: QLineEditAnimated = thisValidator(str(thisConfig[thisName]), self.thisStatusBar)

                    # Connect the textChanged signal to a handler function
                    thisLineEdit.textChanged.connect(self.textChanged)

                    #checks if there are parameterBoundaries and if so checks if value is in range
                    if (self.parameterBoundaries[thisName] != None):
                        try:
                            config_value = float(thisConfig[thisName])
                        except ValueError:
                            # Handle the case where the value cannot be converted
                            config_value = 0

                        if not self.IsParInRange(x=config_value, bot=self.parameterBoundaries[thisName][0],
                                             top=self.parameterBoundaries[thisName][1]):
                            IsOutOfRange=True
                            ToolTip=ToolTip+LANGUAGE_SETTINGS.languagePack["configManager"]["toolAndMachineNames"][thisName]+", "

                    thisLineEdit.setReadOnly(True)
                    #thisLineEdit.setValidator(thisValidator)
                    thisLineEdit.setStyleSheet(STLYE_LOCKED_FIELD)
                    self.configTable.setCellWidget(i, j, thisLineEdit)

                j = j + 1

            if IsOutOfRange:
                # Remove comma
                ToolTip = ToolTip[:-2]

                print('Werkzeug sperren deaktiviert')
                #if not advanced user disable button
                if GUI_SETTINGS["GUI"]["userRole"]!="advanced":
                    self.configTable.cellWidget(i, 1).setDisabled(True)
                #set tooltip and make button red
                selectButton.setToolTip(ToolTip)
                selectButton.setStyleSheet("QPushButton {background-color: #e85154}"
                                           "QPushButton:hover {background-color: #d41519}"
                                           )

            i = i + 1


    def makeEditable(self) -> None:
        self.thisStatusBar.showMessage("")
        rows = self.configTable.rowCount()

        self.updateCalculatedValues()

        lockedList = ["l_kopf_gesamt"]

        for i in range(rows):
            self.configTable.cellWidget(i, 1).setDisabled(True)

            for j, thisName in enumerate(self.jsonNames[:], start=2):
                if thisName == "Steuerung":
                    self.configTable.cellWidget(i, j).setDisabled(False)
                elif thisName not in lockedList:
                    self.configTable.cellWidget(i, j).setReadOnly(False)
                    self.configTable.cellWidget(i, j).setStyleSheet(STYLE_OPEN_FIELD)
                else:
                    self.configTable.cellWidget(i, j).setReadOnly(True)
                    self.configTable.cellWidget(i, j).setStyleSheet(STLYE_LOCKED_FIELD)
            print('Row done:', i)
        print('MakeEditable: Done')


    def expertModus(self):
        # Load configs
        configs = self.loadConfigs(self.configDir)

        reply = QMessageBox.No
        if not self.flag_expert:
            # Create a custom message box
            msgBox = QMessageBox()
            msgBox.setIcon(QMessageBox.Warning)
            msgBox.setWindowTitle(LANGUAGE_SETTINGS.languagePack["setupTabmenu"]["WarningTitle"])
            msgBox.setText(LANGUAGE_SETTINGS.languagePack["configManager"]["ExpertModeWarning"])

            # Add custom buttons
            yes_button = msgBox.addButton(LANGUAGE_SETTINGS.languagePack["configManager"]["JaButton"], QMessageBox.YesRole)
            no_button = msgBox.addButton(LANGUAGE_SETTINGS.languagePack["configManager"]["NeinButton"], QMessageBox.NoRole)

            # Set the default button (optional)
            msgBox.setDefaultButton(no_button)

            # Show the message box and get the response
            msgBox.exec_()

            if msgBox.clickedButton() == yes_button:
                reply = QMessageBox.Yes
            else:
                reply = QMessageBox.No

        if reply == QMessageBox.Yes:
            if not self.flag_expert:
                self.flag_expert = True
                self.expertButton.setText(LANGUAGE_SETTINGS.languagePack["configManager"]["normalModus"])
                for i, thisConfig in enumerate(configs):
                    self.configTable.cellWidget(i, 1).setDisabled(False)
        else:
            self.flag_expert = False
            self.expertButton.setText(LANGUAGE_SETTINGS.languagePack["configManager"]["expertModus"])
            for i, thisConfig in enumerate(configs):
                self.configTable.cellWidget(i, 1).setDisabled(True)

    def saveConfig(self) -> None:
        rows = self.configTable.rowCount()
        listToBeSaved = []

        for f in os.listdir(self.configDir):
            os.remove(os.path.join(self.configDir, f))

        for i in range(rows):
            self.configTable.cellWidget(i, 1).setDisabled(False)
            toBeSaved = True

            config = {}

            if self.configTable.item(i, 0) is None:
                name = str(self.configTable.cellWidget(i, 2).text())
                name = name.replace("\s", "")
                timestampStr = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
                filePath = self.configDir + name + timestampStr + ".json"
            else:
                filePath = self.configTable.item(i, 0).text()

            for j, thisName in enumerate(self.jsonNames, start=2):
                cellWidget = self.configTable.cellWidget(i, j)

                if thisName == "Steuerung":
                    config[thisName] = cellWidget.currentText()
                else:
                    cellWidget.setReadOnly(True)
                    # cellWidget.setStyleSheet(STYLE_LOCKED_FIELD)
                    cellText = cellWidget.text()

                    if cellText == "" and thisName in self.needToBeFilled:
                        toBeSaved = False
                        config[thisName] = ''
                    else:
                        dummyStr = cellText.replace(".", "").replace(",", "")
                        if dummyStr.isnumeric():
                            config[thisName] = float(cellText.replace(",", "."))
                        else:
                            config[thisName] = cellText

            if toBeSaved:
                listToBeSaved.append(filePath)
                with open(filePath, 'w') as f:
                    json.dump(config, f, indent=4)
            else:
                msg = LANGUAGE_SETTINGS.languagePack["configManager"]["messageMissingInput"] + ": "
                for name in self.needToBeFilled:
                    msg = msg + LANGUAGE_SETTINGS.languagePack["configManager"]["toolAndMachineNames"][name] + ", "
                QMessageBox().about(self, LANGUAGE_SETTINGS.languagePack["configManager"]["titleMsgBoxMissingInput"],
                                    msg)
                listToBeSaved.append(filePath)
                with open(filePath, 'w') as f:
                    json.dump(config, f, indent=4)

        for f in os.listdir(self.configDir):
            if not os.path.join(self.configDir, f) in listToBeSaved:
                os.remove(os.path.join(self.configDir, f))
        self.updateContents()

        # Update GUI
        # Open General Config
        if os.path.exists(PROCESSING_SETTINGS_FILE):
            with open(PROCESSING_SETTINGS_FILE, 'r') as config:
                try:
                    jsonConfig = json.load(config)
                except json.JSONDecodeError:
                    pass
        else:
            # Log an error message or handle the missing file case
            print(f"{PROCESSING_SETTINGS_FILE} does not exist.")

        # Open tool config
        if self.tool_key == "tool":
            if os.path.exists(jsonConfig["toolPath"]):
                with open(jsonConfig["toolPath"], 'r') as config:
                    try:
                        selectedDict = json.load(config)
                        selectedDict["FilePath"] = jsonConfig["toolPath"]
                    except json.JSONDecodeError:
                        pass
            else:
                # Log an error message or handle the missing file case
                print(f"{jsonConfig['toolPath']} does not exist.")
                print("Set tool to default values")
                selectedDict = DEFAULTTOOLCONFIG
                selectedDict["FilePath"] = jsonConfig["toolPath"]

        # Open machine config
        if self.tool_key == "machine":
            if os.path.exists(jsonConfig["machinePath"]):
                with open(jsonConfig["machinePath"], 'r') as config:
                    try:
                        selectedDict = json.load(config)
                        # add File Path
                        selectedDict["FilePath"] = jsonConfig["machinePath"]
                    except json.JSONDecodeError:
                        pass
            else:
                # Log an error message or handle the missing file case
                print(f"{jsonConfig['machinePath']} does not exist.")
                print("Set machine to default values")
                selectedDict = DEFAULTMACHINECONFIG
                selectedDict["FilePath"] = jsonConfig["machinePath"]

        self.returnSelected(selectedDict, False)

    def createNew(self) -> None:
        """adds a new row to the table in the manager"""

        self.thisStatusBar.showMessage("")
        i = self.configTable.rowCount()
        self.configTable.insertRow(i)
        rows = self.configTable.rowCount()

        selectButton = QPushButton(LANGUAGE_SETTINGS.languagePack["configManager"]["selectButton"])
        selectButton.setDisabled(True)
        self.configTable.setCellWidget(rows - 1, 1, selectButton)

        # Start j at 2 using enumerate
        for i, (thisName, thisValidator) in enumerate(zip(self.jsonNames, self.lineValidators), start=2):
            if thisName == "Steuerung":
                # Create the combobox for "Steuerung"
                thisComboBox = self.createSteuerungComboBox(
                    current_value=LANGUAGE_SETTINGS.languagePack["configManager"]["Steuerungstyp"]["Sinumerik"][
                        "LagegeregelterSpindelbetrieb"]
                )
                thisComboBox.setDisabled(False)
                self.configTable.setCellWidget(rows - 1, i, thisComboBox)
            else:
                # Create a QLineEdit for other cases
                thisLineEdit: QLineEditAnimated = thisValidator("", self.thisStatusBar)
                self.configTable.setCellWidget(rows - 1, i, thisLineEdit)
                # Connect the textChanged signal to a handler function
                thisLineEdit.textChanged.connect(self.textChanged)

        self.makeEditable()

    def finishDeleting(self) -> None:
        """function for closing the delete mode and restarting the normal mode for the config manager"""
        self.saveButton.setDisabled(False)
        self.createNewButton.setDisabled(False)
        self.editButton.setDisabled(False)
        self.deleteButton = QPushButton(LANGUAGE_SETTINGS.languagePack["configManager"]["deleteModeButton"][self.tool_key])
        self.deleteButton.clicked.connect(lambda: self.deleteConfig())
        self.buttonLayout.replaceWidget(self.finishDeletingButton, self.deleteButton)
        self.finishDeletingButton.hide()
        self.configTable.currentCellChanged.connect(self.cellChanged)
        self.saveConfig()

    def deleteConfig(self) -> None:
        """method for intializing the delete mode for the config manager"""
        self.saveButton.setDisabled(True)
        self.createNewButton.setDisabled(True)
        self.editButton.setDisabled(True)
        self.configTable.currentCellChanged.disconnect()
        nrows=self.configTable.rowCount()

        for i in range(0, nrows):
            tempdeleteButton = QPushButton(LANGUAGE_SETTINGS.languagePack["configManager"]["deleteButton"][self.tool_key])
            tempdeleteButton.clicked.connect(lambda event, j=i: self.deleteRow(j))
            self.configTable.setCellWidget(i, 1, tempdeleteButton)

        self.finishDeletingButton = QPushButton(LANGUAGE_SETTINGS.languagePack["configManager"]["deleteModeStopButton"])
        self.finishDeletingButton.clicked.connect(self.finishDeleting)
        self.buttonLayout.replaceWidget(self.deleteButton, self.finishDeletingButton)
        self.deleteButton.hide()

    def deleteRow(self,iRow: int) -> None:
        """function for deleting a row for a given row index"""
        self.configTable.removeRow(iRow)

        #update row buttons
        nrows = self.configTable.rowCount()
        for i in range(0, nrows):
            tempdeleteButton = QPushButton(LANGUAGE_SETTINGS.languagePack["configManager"]["deleteButton"][self.tool_key])
            tempdeleteButton.clicked.connect(lambda event, j=i: self.deleteRow(j))
            self.configTable.setCellWidget(i, 1, tempdeleteButton)

    def cellChanged(self):
        """parent method, is overwritten is child classes"""
        pass

    def textChanged(self):
        """parent method, is overwritten is child classes"""
        pass

    def updateParBounds(self,geometryConifg,thisConfig=None):
        """parent method, is overwritten is child classes"""
        pass

    def updateCalculatedValues(self):
        """parent method, is overwritten is child classes"""
        pass

    def createSteuerungComboBox(self, current_value=None):
        comboBox = QComboBox()
        options = [LANGUAGE_SETTINGS.languagePack["configManager"]["Steuerungstyp"]["Sinumerik"]["LagegeregelterSpindelbetrieb"],
                   LANGUAGE_SETTINGS.languagePack["configManager"]["Steuerungstyp"]["Sinumerik"]["SpindelnPositionieren"]]
        comboBox.addItems(options)
        if current_value and current_value in options:
            comboBox.setCurrentText(current_value)
        return comboBox

class toolManager(configManager):
    """Class for the tool Manager based on the configManger class"""

    def __init__(self, tabmenu: setupTabmenu):
        """Create a toolManager to select and edit the tool"""
        super().__init__(tabmenu, os.path.expandvars(GUI_SETTINGS["paths"]["toolConfigs"]), JSON_NAMES_TOOL, TOOL_EDIT_LINE_VALIDATORS)

        self.needToBeFilled = JSON_NAMES_TOOL
        nColumns = len(JSON_NAMES_TOOL) + 2
        self.configTable.setColumnCount(nColumns)
        self.configTable.hideColumn(0)
        self.parameterBoundaries =DEFAULT_WERKZEUG_PAR_BOUNDARIES
        #dictionary assinging paramters the function for calculating the parameter boundaries
        self.DictionaryCalculationFunctions = {
            "toolname": None,
            "d_kopf": W_Auslegung.calcDKopf,
            "l_kopf": W_Auslegung.calcLKopf,
            "l_kopf_gesamt": W_Auslegung.calcLKopfGesamt,
            "d_schaft": W_Auslegung.calcDSchaft,
            "l_werkzeug": W_Auslegung.calcLWerkzeug,
            "l_werkzeug_gesamt": W_Auslegung.calcLWerkzeugGesamt,
            "l_schaft": W_Auslegung.calcLSchaft,
            "d_schaft2": W_Auslegung.calcDSchaft2,
            "spitzenwinkel": W_Auslegung.calcSpitzenwinkel,
            "schneidenwinkel": W_Auslegung.calcSchneidewinkel,
            "freiwinkel": W_Auslegung.calcFreiwinkel,
            "winkeloffset": W_Auslegung.calcWinkeloffset,
            "FilePath": None
        }

        headerLabels = [LANGUAGE_SETTINGS.languagePack["configManager"]["toolManagerHeader"]["hidden"]["label"],
                        LANGUAGE_SETTINGS.languagePack["configManager"]["toolManagerHeader"]["Auswählen"]["label"]]
        headerTooltips = [None, None]

        for name in JSON_NAMES_TOOL:
            elementDict = LANGUAGE_SETTINGS.languagePack["configManager"]["toolManagerHeader"][name]
            headerLabels.append(elementDict["label"])
            if "tooltip" in elementDict:
                text = elementDict["tooltip"]
            else:
                text = None
            if "picturePath" in elementDict:
                picturePath = elementDict["picturePath"]
            else:
                picturePath = None
            headerTooltips.append(self.create_tooltip(text, picturePath))

        self.configTable.setHorizontalHeaderLabels(headerLabels)

        # resize columns
        header = self.configTable.horizontalHeader()
        for i in range(2, nColumns):
            header.setSectionResizeMode(i, QHeaderView.ResizeMode.ResizeToContents)

        # Set tooltips for the horizontal header labels
        for i, tooltip in enumerate(headerTooltips):
            header_item = self.configTable.horizontalHeaderItem(i)
            if header_item:
                header_item.setToolTip(tooltip)

        self.setWindowTitle(LANGUAGE_SETTINGS.languagePack["configManager"]["toolManagerWindowTitle"])

    def create_tooltip(self, text, icon_filename=None, scale_factor=None):
        # Replace newline characters with HTML line breaks
        formatted_text = text.replace("\n", "<br>")

        if icon_filename and os.path.isfile(icon_filename):
            # Load the icon image
            icon = QPixmap(icon_filename)

            if scale_factor:
                # Create the HTML-formatted tooltip text with the icon image
                tooltip_text = f'<html><body><p>{formatted_text}</p><p><img src="{icon_filename}" width="{icon.width() * scale_factor}" height="{icon.height() * scale_factor}"></p></body></html>'
            else:
                # Create the HTML-formatted tooltip text with the icon image without scaling
                tooltip_text = f'<html><body><p>{formatted_text}</p><p><img src="{icon_filename}"></p></body></html>'
        else:
            # Return only the text if no valid image file is provided
            tooltip_text = f'<html><body><p>{formatted_text}</p></body></html>'

        return tooltip_text
    
    def updateParBounds(self, geometryConifg: dict, thisConfig: dict = None) -> None:
        """updates paramteter boundaries for the tools for given geometry and tool config"""
        d_hb = geometryConifg["Verschneidung"]["d_hb"]
        d_qb = geometryConifg["Verschneidung"]["d_qb"]
        av = geometryConifg["Verschneidung"]["av"]
        fb_soll = geometryConifg["Verschneidung"]["fb_soll"]
        sicherheitsebene2 = geometryConifg["Sicherheitsebenen"]["Sicherheitsebene2"]
        bohrtiefe = geometryConifg["Sicherheitsebenen"]["Bohrtiefe"]
        ebener_Austritt = geometryConifg["Allgemein"]["ebener_Austritt"]

        #if geometry is empty set default boundaries
        if d_hb=="" or d_qb=="" or av=="" or fb_soll=="":
            self.parameterBoundaries = DEFAULT_WERKZEUG_PAR_BOUNDARIES
            return

        #if config is given use tool paramters
        if thisConfig!=None:
            d_kopf=thisConfig["d_kopf"]
            schneidenwinkel= thisConfig["schneidenwinkel"] * math.pi / 180
            spitzenwinkel=thisConfig["spitzenwinkel"] * math.pi / 180
            l_kopf=thisConfig["l_kopf"]
            d_schaft=thisConfig["d_schaft"]
            l_hals = thisConfig["l_werkzeug"]
            l_schaft = thisConfig["l_schaft"]
            self.parameterBoundaries = {
                "toolname": None,
                "d_kopf": self.DictionaryCalculationFunctions["d_kopf"](d_qb),
                "l_kopf": self.DictionaryCalculationFunctions["l_kopf"](spitzenwinkel, d_hb, d_qb, av, fb_soll, schneidenwinkel, d_schaft, ebener_Austritt),
                "l_kopf_gesamt": self.DictionaryCalculationFunctions["l_kopf_gesamt"](d_hb, d_qb, av, fb_soll, ebener_Austritt),
                "d_schaft": self.DictionaryCalculationFunctions["d_schaft"](d_kopf, schneidenwinkel, fb_soll, SICHERHEIT),
                "l_werkzeug": self.DictionaryCalculationFunctions["l_werkzeug"](schneidenwinkel, spitzenwinkel, l_kopf, fb_soll, bohrtiefe, d_hb, d_qb, av),
                "l_werkzeug_gesamt": self.DictionaryCalculationFunctions["l_werkzeug_gesamt"](spitzenwinkel, d_kopf, l_kopf, schneidenwinkel, d_schaft, l_hals, l_schaft),
                "l_schaft": self.DictionaryCalculationFunctions["l_schaft"](),
                "d_schaft2": self.DictionaryCalculationFunctions["d_schaft2"](),
                "spitzenwinkel": self.DictionaryCalculationFunctions["spitzenwinkel"](),
                "schneidenwinkel": self.DictionaryCalculationFunctions["schneidenwinkel"](d_hb, d_qb, av, ebener_Austritt),
                "freiwinkel": self.DictionaryCalculationFunctions["freiwinkel"](d_hb, d_qb, av),
                "winkeloffset": self.DictionaryCalculationFunctions["winkeloffset"](),
                "FilePath": None
            }
            return

        #if config is not given
        self.parameterBoundaries = {
            "toolname": None,
            "d_kopf": self.DictionaryCalculationFunctions["d_kopf"](d_qb, d_qb),
            "l_kopf": self.DictionaryCalculationFunctions["l_kopf"](d_hb,d_qb,av,SICHERHEIT),
            "l_kopf_gesamt": self.DictionaryCalculationFunctions["l_kopf_gesamt"](d_hb, d_qb, av, SICHERHEIT),
            "d_schaft": self.DictionaryCalculationFunctions["d_schaft"](d_qb,0,fb_soll,SICHERHEIT),
            "l_werkzeug": self.DictionaryCalculationFunctions["l_werkzeug"](0,SICHERHEIT,d_qb),
            "l_werkzeug_gesamt": self.DictionaryCalculationFunctions["l_werkzeug_gesamt"](),
            "l_schaft": self.DictionaryCalculationFunctions["l_schaft"](),
            "d_schaft2": self.DictionaryCalculationFunctions["d_schaft2"](),
            "spitzenwinkel": self.DictionaryCalculationFunctions["spitzenwinkel"](),
            "schneidenwinkel": self.DictionaryCalculationFunctions["schneidenwinkel"](d_hb, d_qb, av, ebener_Austritt),
            "freiwinkel": self.DictionaryCalculationFunctions["freiwinkel"](),
            "winkeloffset": self.DictionaryCalculationFunctions["winkeloffset"](),
            "FilePath": None
        }

    def cellChanged(self) -> None:
        """function for updating the paramter boundaries and displaying them in the status bar depending on the curren active cell"""
        #get current cell/row/column
        i = self.configTable.currentRow()
        column = self.configTable.currentColumn()
        if column<2 or i<0:
            self.thisStatusBar.showMessage("")
            return
        name = self.jsonNames[column-2]

        #create Config out of values in current row
        config = {}
        for j, thisName in enumerate(self.jsonNames, start=2):
            cellText = self.configTable.cellWidget(i, j).text()
            dummyStr = cellText.replace(".", "").replace(",", "")

            if dummyStr.isnumeric():
                config[thisName] = float(cellText.replace(",", "."))
            elif j == 2:
                config[thisName] = cellText
            else:
                config[thisName] = 0

        #update parameter boundaries
        try:
            self.updateParBounds(self.currentGeometry, config)
        except ZeroDivisionError as e:
            print(f"Error (cellChanged): Division by zero occurred - {e}")
            # Handle division by zero
        except ValueError as e:
            print(f"Error (cellChanged): Invalid value encountered - {e}")
            # Handle square root of a negative number or other value errors
        except Exception as e:
            print(f"An unexpected error occurred (cellChanged) - {e}")
            # Handle any other unexpected errors
        #self.updateParBounds(self.currentGeometry,config)

        #display boundaries of current cell
        bounds = self.parameterBoundaries[name]
        if bounds!=None:
            lower = str(bounds[0])
            upper = str(bounds[1])
        else:
            upper = "None"
            lower = "None"
        if "Statusbar" in LANGUAGE_SETTINGS.languagePack["configManager"]["toolManagerHeader"][name]:
            msg = LANGUAGE_SETTINGS.languagePack["configManager"]["toolManagerHeader"][name]["Statusbar"]
        else:
            msg = LANGUAGE_SETTINGS.languagePack["configManager"]["toolManagerHeader"][name]["label"]

        self.thisStatusBar.showMessage(msg + ", "
                                       + LANGUAGE_SETTINGS.languagePack["configManager"]["Reihe"] + ": " + str(i+1)
                                       + ", Min: " + lower + ", Max: " + upper)

    def textChanged(self):
        self.updateCalculatedValues()

    def updateCalculatedValues(self):
        variable_dict = {}
        target_variables = ["spitzenwinkel", "d_kopf", "l_kopf", "l_kopf_gesamt", "schneidenwinkel", "d_schaft"]

        for j, thisName in enumerate(self.jsonNames, start=2):
            if thisName in target_variables:
                variable_dict[thisName] = j

        rows = self.configTable.rowCount()
        for i in range(rows):
            # Set Kopflaenge Gesamt
            targetCellWidget = self.configTable.cellWidget(i, variable_dict["l_kopf_gesamt"])

            def safe_float_conversion(cellWidget, default_value=0.0):
                text = cellWidget.text().replace(',', '.')
                return float(text) if text else default_value

            # Retrieve the cell widgets
            spitzenwinkel_widget = self.configTable.cellWidget(i, variable_dict["spitzenwinkel"])
            d_kopf_widget = self.configTable.cellWidget(i, variable_dict["d_kopf"])
            l_kopf_widget = self.configTable.cellWidget(i, variable_dict["l_kopf"])
            schneidenwinkel_widget = self.configTable.cellWidget(i, variable_dict["schneidenwinkel"])
            d_schaft_widget = self.configTable.cellWidget(i, variable_dict["d_schaft"])

            # Convert to floats with safety checks
            spitzenwinkel = safe_float_conversion(spitzenwinkel_widget)
            d_kopf = safe_float_conversion(d_kopf_widget)
            l_kopf = safe_float_conversion(l_kopf_widget)
            if spitzenwinkel == 0:
                l_spitzenwinkel = 0
            else:
                l_spitzenwinkel = (d_kopf / 2.0) / np.tan((spitzenwinkel / 2) * np.pi / 180)

            schneidenwinkel = safe_float_conversion(schneidenwinkel_widget)
            d_schaft = safe_float_conversion(d_schaft_widget)
            delta_r = (d_kopf - d_schaft) / 2.0
            if schneidenwinkel == 0 or d_kopf == 0 or d_schaft == 0:
                l_schneide = 0
            else:
                l_schneide = delta_r / np.tan(schneidenwinkel * np.pi / 180)

            value = l_spitzenwinkel + l_kopf + l_schneide

            # Format the value to 2 decimal places and replace '.' with ','
            formatted_value = f"{value:.2f}".replace('.', ',')
            targetCellWidget.setText(formatted_value)

    def returnSelected(self, selectedTool: dict, CloseFlag=True) -> None:
        """Sets the value in tabmenu based on selectedTool and closes the window"""

        self.tabmenu.selectedToolConfig = selectedTool
        self.tabmenu.selectedToolPath = selectedTool["FilePath"]
        self.tabmenu.selectedTool.setText(str(selectedTool["toolname"]))
        IsOutOfRange, names = self.checkLineValidators(selectedTool, self.jsonNames, self.lineValidators)
        if IsOutOfRange:
            self.tabmenu.werkzeugstatus.setText(LANGUAGE_SETTINGS.languagePack["setupTabmenu"]["toolTab"]["WerkzeugNichtGeignet"])
            text = LANGUAGE_SETTINGS.languagePack["setupTabmenu"]["toolTab"]["WerkzeugNichtGeignet_tooltip"]
            for name in names:
                text = text + "\n - " + LANGUAGE_SETTINGS.languagePack["setupTabmenu"]["toolTab"][name]
            self.tabmenu.werkzeugstatus.setToolTip(text)
            self.tabmenu.werkzeugstatus.setStyleSheet(f'background-color: #e85154; color: black;')
        else:
            self.tabmenu.werkzeugstatus.setText(LANGUAGE_SETTINGS.languagePack["setupTabmenu"]["toolTab"]["WerkzeugGeignet"])
            self.tabmenu.werkzeugstatus.setToolTip(LANGUAGE_SETTINGS.languagePack["setupTabmenu"]["toolTab"]["WerkzeugGeignet"])
            self.tabmenu.werkzeugstatus.setStyleSheet(f'background-color: green; color: white;')
        self.tabmenu.d_kopf.setText(str(selectedTool["d_kopf"]).replace(".", ","))
        self.tabmenu.l_kopf.setText(str(selectedTool["l_kopf"]).replace(".", ","))
        self.tabmenu.l_kopf_gesamt.setText(str(selectedTool["l_kopf_gesamt"]).replace(".", ","))
        self.tabmenu.d_schaft.setText(str(selectedTool["d_schaft"]).replace(".", ","))
        self.tabmenu.l_werkzeug.setText(str(selectedTool["l_werkzeug"]).replace(".", ","))
        self.tabmenu.l_werkzeug_gesamt.setText(str(selectedTool["l_werkzeug_gesamt"]).replace(".", ","))
        self.tabmenu.l_schaft.setText(str(selectedTool["l_schaft"]).replace(".", ","))
        self.tabmenu.d_schaft2.setText(str(selectedTool["d_schaft2"]).replace(".", ","))
        self.tabmenu.spitzenwinkel.setText(str(selectedTool["spitzenwinkel"]).replace(".", ","))
        self.tabmenu.freiwinkel.setText(str(selectedTool["freiwinkel"]).replace(".", ","))
        self.tabmenu.schneidenwinkel.setText(str(selectedTool["schneidenwinkel"]).replace(".", ","))
        self.tabmenu.winkeloffset.setText(str(selectedTool["winkeloffset"]).replace(".", ","))

        self.tabmenu.setPlotStatusBarChanged()
        self.tabmenu.setGCodeStaturBarChanged()

        if CloseFlag:
            self.close()


class machineManager(configManager):
    """Class for the machine Manager based on the configManger class"""

    def __init__(self, tabmenu: setupTabmenu):
        """Create a machineManager to select and edit the machines"""
        super().__init__(tabmenu, os.path.expandvars(GUI_SETTINGS["paths"]["machineConfigs"]), JSON_NAMES_MACHINE, MACHINE_EDIT_LINE_VALIDATORS, False)

        #defines which columns need to be filled out
        self.needToBeFilled = ["Name", "Steuerung", "WKS", "MCodeWerkzeugwechsel", "MCodeEinschaltenSpindel",
                               "MCodeEinschaltenSpindelAlt", "MCodeAusschaltenSpindel"]

        nColumns=14
        self.configTable.setColumnCount(nColumns)
        self.configTable.hideColumn(0)
        self.parameterBoundaries = {"Name": None,
                                   "Steuerung": None,
                                   "WKS": None,
                                   "MCodeWerkzeugwechsel": None,
                                   "MCodeDrehachsenKlemmen": None,
                                   "MCodeDrehachsenLoesen": None,
                                   "MCodeKontrollMaschinenhalt": None,
                                   "MCodeEinschaltenKSS": None,
                                   "MCodeAusschaltenKSS": None,
                                   "MCodeEinschaltenSpindel": None,
                                   "MCodeEinschaltenSpindelAlt": None,
                                   "MCodeAusschaltenSpindel": None,
                                   "FilePath": None}

        self.configTable.setHorizontalHeaderLabels(LANGUAGE_SETTINGS.languagePack["configManager"]["machineManagerHeaderLabels"])

        #resize columns to content
        header= self.configTable.horizontalHeader()
        for i in range(2,nColumns):
            header.setSectionResizeMode(i, QHeaderView.ResizeMode.ResizeToContents)

        self.setWindowTitle(LANGUAGE_SETTINGS.languagePack["configManager"]["machineManagerWindowTitle"])

    def returnSelected(self, selectedMachine: dict, CloseFlag=True) -> None:
        """Sets the value in tabmenu based on selectedMachine and closes the window"""

        self.tabmenu.selectedMachineConfig = selectedMachine
        self.tabmenu.selectedMachinePath = selectedMachine["FilePath"]
        self.tabmenu.selectedMachine.setText(selectedMachine["Name"])
        self.tabmenu.steuerung.setText(selectedMachine["Steuerung"])
        self.tabmenu.wks.setText(selectedMachine["WKS"])
        self.tabmenu.mCodeWerkzeugwechsel.setText(selectedMachine["MCodeWerkzeugwechsel"])
        self.tabmenu.mCodeDrehachsenKlemmen.setText(selectedMachine["MCodeDrehachsenKlemmen"])
        self.tabmenu.mCodeDrehachsenLoesen.setText(selectedMachine["MCodeDrehachsenLoesen"])
        self.tabmenu.mCodeKontrollMaschinenhalt.setText(selectedMachine["MCodeKontrollMaschinenhalt"])
        self.tabmenu.mCodeEinschaltenKSS.setText(selectedMachine["MCodeEinschaltenKSS"])
        self.tabmenu.mCodeAusschaltenKSS.setText(selectedMachine["MCodeAusschaltenKSS"])
        self.tabmenu.mCodeEinschaltenSpindel.setText(selectedMachine["MCodeEinschaltenSpindel"])
        self.tabmenu.mCodeEinschaltenSpindelAlt.setText(selectedMachine["MCodeEinschaltenSpindelAlt"])
        self.tabmenu.mCodeAusschaltenSpindel.setText(selectedMachine["MCodeAusschaltenSpindel"])

        self.tabmenu.setGCodeStaturBarChanged()

        if CloseFlag:
            self.close()