import os, click, re

from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button, Input, DataTable
from textual.containers import Grid

from ffx.audio_layout import AudioLayout

from .show_details_screen import ShowDetailsScreen
from .pattern_details_screen import PatternDetailsScreen
from .screen_support import build_screen_bootstrap, build_screen_controllers

from ffx.track_type import TrackType
from ffx.track_codec import TrackCodec
from ffx.model.track import Track

from ffx.track_disposition import TrackDisposition
from ffx.track_descriptor import TrackDescriptor
from ffx.show_descriptor import ShowDescriptor

from textual.widgets._data_table import CellDoesNotExist

from ffx.media_descriptor import MediaDescriptor
from ffx.file_properties import FileProperties

from ffx.media_descriptor_change_set import MediaDescriptorChangeSet

from ffx.helper import formatRichColor, DIFF_ADDED_KEY, DIFF_CHANGED_KEY, DIFF_REMOVED_KEY, DIFF_UNCHANGED_KEY


# Screen[dict[int, str, int]]
class MediaDetailsScreen(Screen):

    CSS = """

    Grid {
        grid-size: 5 8;
        grid-rows: 8 2 2 2 2 8 2 2 8;
        grid-columns: 15 25 90 10 105;
        height: 100%;
        width: 100%;
        padding: 1;
    }
    
    DataTable .datatable--cursor {
        background: darkorange;
        color: black;
    }

    DataTable .datatable--header {
        background: steelblue;
        color: white;
    }

    Input {
        border: none;
    }
    Button {
        border: none;
    }

    DataTable {
        min-height: 40;
    }

    #toplabel {
        height: 1;
    }
    .two {
        column-span: 2;
    }   
    .three {
        column-span: 3;
    }
    
    .four {
        column-span: 4;
    }
    .five {
        column-span: 5;
    }

    .triple {
        row-span: 3;
    }

    .box {
        height: 100%;
        border: solid green;
    }

    .purple {
        tint: purple 40%;
    }

    .yellow {
        tint: yellow 40%;
    }

    #differences-table {
        row-span: 8;
        /* tint: magenta 40%; */
    }

    /* #pattern_input {
        tint: red 40%;
    }*/
    """


    TRACKS_TABLE_INDEX_COLUMN_LABEL = "Index"
    TRACKS_TABLE_TYPE_COLUMN_LABEL = "Type"
    TRACKS_TABLE_SUB_INDEX_COLUMN_LABEL = "SubIndex"
    TRACKS_TABLE_CODEC_COLUMN_LABEL = "Codec"
    TRACKS_TABLE_LAYOUT_COLUMN_LABEL = "Layout"
    TRACKS_TABLE_LANGUAGE_COLUMN_LABEL = "Language"
    TRACKS_TABLE_TITLE_COLUMN_LABEL = "Title"
    TRACKS_TABLE_DEFAULT_COLUMN_LABEL = "Default"
    TRACKS_TABLE_FORCED_COLUMN_LABEL = "Forced"

    DIFFERENCES_TABLE_DIFFERENCES_COLUMN_LABEL = 'Differences (file->db/output)'


    BINDINGS = [
        ("n", "new_pattern", "New Pattern"),
        ("u", "update_pattern", "Update Pattern"),
        ("e", "edit_pattern", "Edit Pattern"),
    ]


    def __init__(self):
        super().__init__()

        bootstrap = build_screen_bootstrap(self.app.getContext())
        self.context = bootstrap.context

        self.__removeGlobalKeys = bootstrap.remove_global_keys
        self.__ignoreGlobalKeys = bootstrap.ignore_global_keys

        controllers = build_screen_controllers(
            self.context,
            pattern=True,
            show=True,
            track=True,
            tag=True,
        )
        self.__pc = controllers['pattern']
        self.__sc = controllers['show']
        self.__tc = controllers['track']
        self.__tac = controllers['tag']

        if not 'command' in self.context.keys() or self.context['command'] != 'inspect':
            raise click.ClickException(f"MediaDetailsScreen.__init__(): Can only perform command 'inspect'")
        
        if not 'arguments' in self.context.keys() or not 'filename' in self.context['arguments'].keys() or not self.context['arguments']['filename']:
            raise click.ClickException(f"MediaDetailsScreen.__init__(): Argument 'filename' is required to be provided for command 'inspect'")

        self.__mediaFilename = self.context['arguments']['filename']

        if not os.path.isfile(self.__mediaFilename):
            raise click.ClickException(f"MediaDetailsScreen.__init__(): Media file {self.__mediaFilename} does not exist")

        self.loadProperties()


    def removeShow(self, showId : int = -1):
        """Remove show entry from DataTable.
        Removes the <New show> entry if showId is not set"""

        for rowKey, row in self.showsTable.rows.items(): # dict[RowKey, Row]
        
            rowData = self.showsTable.get_row(rowKey)

            try:
                if (showId == -1 and rowData[0] == ' '
                        or showId == int(rowData[0])):
                    self.showsTable.remove_row(rowKey)
                    return
            except:
                continue

        

    def getRowIndexFromShowId(self, showId : int = -1) -> int:
        """Find the index of the row where the value in the specified column matches the target_value."""

        for rowKey, row in self.showsTable.rows.items(): # dict[RowKey, Row]
        
            rowData = self.showsTable.get_row(rowKey)

            try:
                if ((showId == -1 and rowData[0] == ' ')
                        or showId == int(rowData[0])):
                    return int(self.showsTable.get_row_index(rowKey))
            except:
                continue

        return None


    def loadProperties(self):

        self.__mediaFileProperties = FileProperties(self.context, self.__mediaFilename)
        self.__sourceMediaDescriptor = self.__mediaFileProperties.getMediaDescriptor()

        #HINT: This is None if the filename did not match anything in database
        self.__currentPattern = self.__mediaFileProperties.getPattern()

        # keine tags vorhanden
        self.__targetMediaDescriptor = self.__currentPattern.getMediaDescriptor(self.context) if self.__currentPattern is not None else None

        # Enumerating differences between media descriptors
        # from file (=current) vs from stored in database (=target)
        try:
            mdcs = MediaDescriptorChangeSet(self.context,
                                            self.__targetMediaDescriptor,
                                            self.__sourceMediaDescriptor)

            self.__mediaChangeSetObj = mdcs.getChangeSetObj()
        except ValueError:
            self.__mediaChangeSetObj = {}


    def updateDifferences(self):

        self.loadProperties()

        self.differencesTable.clear()


        if MediaDescriptorChangeSet.TAGS_KEY in self.__mediaChangeSetObj.keys():

            if DIFF_ADDED_KEY in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TAGS_KEY].keys():
                for tagKey, tagValue in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TAGS_KEY][DIFF_ADDED_KEY].items():
                    if tagKey not in self.__ignoreGlobalKeys:
                        row = (f"add media tag: key='{tagKey}' value='{tagValue}'",)
                        self.differencesTable.add_row(*map(str, row))  

            if DIFF_REMOVED_KEY in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TAGS_KEY].keys():
                for tagKey, tagValue in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TAGS_KEY][DIFF_REMOVED_KEY].items():
                    if tagKey not in self.__ignoreGlobalKeys and tagKey not in self.__removeGlobalKeys:
                        row = (f"remove media tag: key='{tagKey}' value='{tagValue}'",)
                        self.differencesTable.add_row(*map(str, row))  

            if DIFF_CHANGED_KEY in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TAGS_KEY].keys():
                for tagKey, tagValue in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TAGS_KEY][DIFF_CHANGED_KEY].items():
                    if tagKey not in self.__ignoreGlobalKeys:
                        row = (f"change media tag: key='{tagKey}' value='{tagValue}'",)
                        self.differencesTable.add_row(*map(str, row))  


        if MediaDescriptorChangeSet.TRACKS_KEY in self.__mediaChangeSetObj.keys():

            if DIFF_ADDED_KEY in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TRACKS_KEY].keys():

                trackDescriptor: TrackDescriptor
                for trackIndex, trackDescriptor in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TRACKS_KEY][DIFF_ADDED_KEY].items():                     
                    row = (f"add {trackDescriptor.getType().label()} track: index={trackDescriptor.getIndex()} lang={trackDescriptor.getLanguage().threeLetter()}",)
                    self.differencesTable.add_row(*map(str, row))  

            if DIFF_REMOVED_KEY in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TRACKS_KEY].keys():
                for trackIndex, trackDescriptor in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TRACKS_KEY][DIFF_REMOVED_KEY].items():
                    row = (f"remove stream #{trackIndex}",)
                    self.differencesTable.add_row(*map(str, row))  

            if DIFF_CHANGED_KEY in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TRACKS_KEY].keys():

                changedTracks: dict = self.__mediaChangeSetObj[MediaDescriptorChangeSet.TRACKS_KEY][DIFF_CHANGED_KEY]

                targetTrackDescriptors = self.__targetMediaDescriptor.getTrackDescriptors()

                trackDiffObj: dict
                for trackIndex, trackDiffObj in changedTracks.items():

                    ttd: TrackDescriptor = targetTrackDescriptors[trackIndex]


                    if MediaDescriptorChangeSet.TAGS_KEY in trackDiffObj.keys():

                        removedTags = (trackDiffObj[MediaDescriptorChangeSet.TAGS_KEY][DIFF_REMOVED_KEY]
                                            if DIFF_REMOVED_KEY in trackDiffObj[MediaDescriptorChangeSet.TAGS_KEY].keys() else {})
                        for tagKey, tagValue in removedTags.items():
                            row = (f"change stream #{ttd.getIndex()} ({ttd.getType().label()}:{ttd.getSubIndex()}) remove key={tagKey} value={tagValue}",)
                            self.differencesTable.add_row(*map(str, row))  

                        addedTags = (trackDiffObj[MediaDescriptorChangeSet.TAGS_KEY][DIFF_ADDED_KEY]
                                        if DIFF_ADDED_KEY in trackDiffObj[MediaDescriptorChangeSet.TAGS_KEY].keys() else {})
                        for tagKey, tagValue in addedTags.items():
                            row = (f"change stream #{ttd.getIndex()} ({ttd.getType().label()}:{ttd.getSubIndex()}) add key={tagKey} value={tagValue}",)
                            self.differencesTable.add_row(*map(str, row))  

                        changedTags = (trackDiffObj[MediaDescriptorChangeSet.TAGS_KEY][DIFF_CHANGED_KEY]
                                        if DIFF_CHANGED_KEY in trackDiffObj[MediaDescriptorChangeSet.TAGS_KEY].keys() else {})
                        for tagKey, tagValue in changedTags.items():
                            row = (f"change stream #{ttd.getIndex()} ({ttd.getType().label()}:{ttd.getSubIndex()}) change key={tagKey} value={tagValue}",)
                            self.differencesTable.add_row(*map(str, row))  

                    
                    if MediaDescriptorChangeSet.DISPOSITION_SET_KEY in trackDiffObj.keys():

                        addedDispositions = (trackDiffObj[MediaDescriptorChangeSet.DISPOSITION_SET_KEY][DIFF_ADDED_KEY]
                                                if DIFF_ADDED_KEY in trackDiffObj[MediaDescriptorChangeSet.DISPOSITION_SET_KEY].keys() else set())
                        for ad in addedDispositions:
                            row = (f"change stream #{ttd.getIndex()} ({ttd.getType().label()}:{ttd.getSubIndex()}) add disposition={ad.label()}",)
                            self.differencesTable.add_row(*map(str, row))  

                        removedDispositions = (trackDiffObj[MediaDescriptorChangeSet.DISPOSITION_SET_KEY][DIFF_REMOVED_KEY]
                                                if DIFF_REMOVED_KEY in trackDiffObj[MediaDescriptorChangeSet.DISPOSITION_SET_KEY].keys() else set())
                        for rd in removedDispositions:
                            row = (f"change stream #{ttd.getIndex()} ({ttd.getType().label()}:{ttd.getSubIndex()}) remove disposition={rd.label()}",)
                            self.differencesTable.add_row(*map(str, row))  


    def on_mount(self):

        if self.__currentPattern is None:
            row = (' ', '<New show>', ' ') # Convert each element to a string before adding
            self.showsTable.add_row(*map(str, row))
    
        for show in self.__sc.getAllShows():
            row = (int(show.id), show.name, show.year) # Convert each element to a string before adding
            self.showsTable.add_row(*map(str, row))

        for mediaTagKey, mediaTagValue in self.__sourceMediaDescriptor.getTags().items():

            textColor = None
            if mediaTagKey in self.__ignoreGlobalKeys:
                textColor = 'blue'
            if mediaTagKey in self.__removeGlobalKeys:
                textColor = 'red'

            row = (formatRichColor(mediaTagKey, textColor), formatRichColor(mediaTagValue, textColor)) # Convert each element to a string before adding
            self.mediaTagsTable.add_row(*map(str, row))

        self.updateTracks()


        if self.__currentPattern is not None:

            showIdentifier = self.__currentPattern.getShowId()
            showRowIndex = self.getRowIndexFromShowId(showIdentifier)
            if showRowIndex is not None:
                self.showsTable.move_cursor(row=showRowIndex)

            self.query_one("#pattern_input", Input).value = self.__currentPattern.getPattern()

            self.updateDifferences()

        else:

            self.query_one("#pattern_input", Input).value = self.__mediaFilename
            self.highlightPattern(True)


    def highlightPattern(self, state : bool):
        if state:
            self.query_one("#pattern_input", Input).styles.background = 'red'
        else:
            self.query_one("#pattern_input", Input).styles.background = None


    def updateTracks(self):

        self.tracksTable.clear()

        # trackDescriptorList = self.__sourceMediaDescriptor.getAllTrackDescriptors()
        trackDescriptorList = self.__sourceMediaDescriptor.getTrackDescriptors()

        typeCounter = {}

        for td in trackDescriptorList:

            trackType = td.getType()
            if not trackType in typeCounter.keys():
                typeCounter[trackType] = 0

            dispoSet = td.getDispositionSet()
            audioLayout = td.getAudioLayout()
            row = (td.getIndex(),
                    trackType.label(),
                    typeCounter[trackType],
                    td.getCodec().label(),
                    audioLayout.label() if trackType == TrackType.AUDIO
                        and audioLayout != AudioLayout.LAYOUT_UNDEFINED else ' ',
                    td.getLanguage().label(),
                    td.getTitle(),
                    'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No',
                    'Yes' if TrackDisposition.FORCED in dispoSet else 'No')

            self.tracksTable.add_row(*map(str, row))

            typeCounter[trackType] += 1


    def compose(self):

        # Create the DataTable widget
        self.showsTable = DataTable(classes="two")

        # Define the columns with headers
        self.column_key_show_id = self.showsTable.add_column("ID", width=10)
        self.column_key_show_name = self.showsTable.add_column("Name", width=80)
        self.column_key_show_year = self.showsTable.add_column("Year", width=10)

        self.showsTable.cursor_type = 'row'


        self.mediaTagsTable = DataTable(classes="two")

        # Define the columns with headers
        self.column_key_track_tag_key = self.mediaTagsTable.add_column("Key", width=30)
        self.column_key_track_tag_value = self.mediaTagsTable.add_column("Value", width=70)

        self.mediaTagsTable.cursor_type = 'row'


        self.tracksTable = DataTable(classes="two")

        # Define the columns with headers
        self.column_key_track_index = self.tracksTable.add_column(MediaDetailsScreen.TRACKS_TABLE_INDEX_COLUMN_LABEL, width=5)
        self.column_key_track_type = self.tracksTable.add_column(MediaDetailsScreen.TRACKS_TABLE_TYPE_COLUMN_LABEL, width=10)
        self.column_key_track_sub_index = self.tracksTable.add_column(MediaDetailsScreen.TRACKS_TABLE_SUB_INDEX_COLUMN_LABEL, width=8)
        self.column_key_track_codec = self.tracksTable.add_column(MediaDetailsScreen.TRACKS_TABLE_CODEC_COLUMN_LABEL, width=10)
        self.column_key_track_layout = self.tracksTable.add_column(MediaDetailsScreen.TRACKS_TABLE_LAYOUT_COLUMN_LABEL, width=10)
        self.column_key_track_language = self.tracksTable.add_column(MediaDetailsScreen.TRACKS_TABLE_LANGUAGE_COLUMN_LABEL, width=15)
        self.column_key_track_title = self.tracksTable.add_column(MediaDetailsScreen.TRACKS_TABLE_TITLE_COLUMN_LABEL, width=48)
        self.column_key_track_default = self.tracksTable.add_column(MediaDetailsScreen.TRACKS_TABLE_DEFAULT_COLUMN_LABEL, width=8)
        self.column_key_track_forced = self.tracksTable.add_column(MediaDetailsScreen.TRACKS_TABLE_FORCED_COLUMN_LABEL, width=8)

        self.tracksTable.cursor_type = 'row'


        # Create the DataTable widget
        self.differencesTable = DataTable(id='differences-table') # classes="triple"

        # Define the columns with headers
        self.column_key_differences = self.differencesTable.add_column(MediaDetailsScreen.DIFFERENCES_TABLE_DIFFERENCES_COLUMN_LABEL, width=100)

        self.differencesTable.cursor_type = 'row'

        yield Header()

        with Grid():

            # 1
            yield Static("Show")
            yield self.showsTable
            yield Static(" ")
            yield self.differencesTable
            
            # 2
            yield Static(" ", classes="four")

            # 3
            yield Static(" ")
            yield Button("Substitute", id="pattern_button")
            yield Static(" ", classes="two")

            # 4
            yield Static("Pattern")
            yield Input(type="text", id='pattern_input', classes="two")

            yield Static(" ")
            
            # 5
            yield Static(" ", classes="four")

            # 6
            yield Static("Media Tags")
            yield self.mediaTagsTable
            yield Static(" ")

            # 7
            yield Static(" ", classes="four")

            # 8
            yield Static(" ")
            yield Button("Set Default", id="select_default_button")
            yield Button("Set Forced", id="select_forced_button")
            yield Static(" ")
            # 9
            yield Static("Streams")
            yield self.tracksTable
            yield Static(" ")

        yield Footer()


    def getPatternObjFromInput(self):
        """Returns show id and pattern as obj from corresponding inputs"""
        patternObj = {}
        try:
            patternObj['show_id'] = self.getSelectedShowDescriptor().getId()
            patternObj['pattern'] = str(self.query_one("#pattern_input", Input).value)
        except:
            return {}
        return patternObj


    def on_button_pressed(self, event: Button.Pressed) -> None:

        if event.button.id == "pattern_button":

            pattern = self.query_one("#pattern_input", Input).value

            patternMatch = re.search(FileProperties.SE_INDICATOR_PATTERN, pattern)

            if patternMatch:
                self.query_one("#pattern_input", Input).value = pattern.replace(patternMatch.group(1), FileProperties.SE_INDICATOR_PATTERN)


        if event.button.id == "select_default_button":
            selectedTrackDescriptor = self.getSelectedTrackDescriptor()
            self.__sourceMediaDescriptor.setDefaultSubTrack(selectedTrackDescriptor.getType(), selectedTrackDescriptor.getSubIndex())
            self.updateTracks()

        if event.button.id == "select_forced_button":
            selectedTrackDescriptor = self.getSelectedTrackDescriptor()
            self.__sourceMediaDescriptor.setForcedSubTrack(selectedTrackDescriptor.getType(), selectedTrackDescriptor.getSubIndex())
            self.updateTracks()


    def getSelectedTrackDescriptor(self):
        """Returns a partial track descriptor"""
        try:

            # Fetch the currently selected row when 'Enter' is pressed
            #selected_row_index = self.table.cursor_row
            row_key, col_key = self.tracksTable.coordinate_to_cell_key(self.tracksTable.cursor_coordinate)

            if row_key is not None:
                selected_track_data = self.tracksTable.get_row(row_key)

                kwargs = {}
                kwargs[TrackDescriptor.CONTEXT_KEY] = self.context
                kwargs[TrackDescriptor.INDEX_KEY] = int(selected_track_data[0])
                kwargs[TrackDescriptor.TRACK_TYPE_KEY] = TrackType.fromLabel(selected_track_data[1])
                kwargs[TrackDescriptor.SUB_INDEX_KEY] = int(selected_track_data[2])
                kwargs[TrackDescriptor.CODEC_KEY] = TrackCodec.fromLabel(selected_track_data[3])
                kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY] = AudioLayout.fromLabel(selected_track_data[4])

                return TrackDescriptor(**kwargs)
            else:
                return None

        except CellDoesNotExist:
            return None


    def getSelectedShowDescriptor(self) -> ShowDescriptor:
        
        try:

            row_key, col_key = self.showsTable.coordinate_to_cell_key(self.showsTable.cursor_coordinate)

            if row_key is not None:
                selected_row_data = self.showsTable.get_row(row_key)

                try:
                    kwargs = {}

                    kwargs[ShowDescriptor.CONTEXT_KEY] = self.context
                    kwargs[ShowDescriptor.ID_KEY] = int(selected_row_data[0])                    
                    kwargs[ShowDescriptor.NAME_KEY] = str(selected_row_data[1])
                    kwargs[ShowDescriptor.YEAR_KEY] = int(selected_row_data[2])

                    return ShowDescriptor(**kwargs)   

                except ValueError:
                    return None

        except CellDoesNotExist:
            return None


    def handle_new_pattern(self, showDescriptor: ShowDescriptor):
        """"""

        if type(showDescriptor) is not ShowDescriptor:
            raise TypeError("MediaDetailsScreen.handle_new_pattern(): Argument 'showDescriptor' has to be of type ShowDescriptor")

        self.removeShow()

        showRowIndex = self.getRowIndexFromShowId(showDescriptor.getId())
        if showRowIndex is None:
            show = (showDescriptor.getId(), showDescriptor.getName(), showDescriptor.getYear())
            self.showsTable.add_row(*map(str, show))

        showRowIndex = self.getRowIndexFromShowId(showDescriptor.getId()) 
        if showRowIndex is not None:
            self.showsTable.move_cursor(row=showRowIndex)

        patternObj = self.getPatternObjFromInput()
        
        if patternObj:
            mediaTags = {}
            for tagKey, tagValue in self.__sourceMediaDescriptor.getTags().items():

                # Filter tags that make no sense to preserve
                if tagKey not in self.__ignoreGlobalKeys and not tagKey in self.__removeGlobalKeys:
                    mediaTags[tagKey] = tagValue

            patternId = self.__pc.savePatternSchema(
                patternObj,
                trackDescriptors=self.__sourceMediaDescriptor.getTrackDescriptors(),
                mediaTags=mediaTags,
            )
            if patternId:
                self.highlightPattern(False)


    def action_new_pattern(self):
        """Adding new patterns
        
        If the corresponding show does not exists in DB it is added beforehand"""

        selectedShowDescriptor = self.getSelectedShowDescriptor()
        
        #HINT: Callback is invoked after this method has exited. As a workaround the callback is executed directly
        # from here with a mock-up screen result containing the necessary part of keys to perform correctly.
        if selectedShowDescriptor is None:
            self.app.push_screen(ShowDetailsScreen(), self.handle_new_pattern)
        else:
            self.handle_new_pattern(selectedShowDescriptor)


    def action_update_pattern(self):
        """Updating patterns
        
        When updating the database the actions must reverse the difference (eq to diff db->file)"""

        if self.__currentPattern is not None:
            patternObj = self.getPatternObjFromInput()
            if (patternObj
                    and self.__currentPattern.getPattern() != patternObj['pattern']):
                return self.__pc.updatePattern(self.__currentPattern.getId(), patternObj)

        self.loadProperties()

        # __mediaChangeSetObj is file vs database
        if MediaDescriptorChangeSet.TAGS_KEY in self.__mediaChangeSetObj.keys():

            if DIFF_ADDED_KEY in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TAGS_KEY].keys():
                for addedTagKey in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TAGS_KEY][DIFF_ADDED_KEY].keys():
                    # click.ClickException(f"delete media tag patternId={self.__currentPattern.getId()} addedTagKey={addedTagKey}")
                    self.__tac.deleteMediaTagByKey(self.__currentPattern.getId(), addedTagKey)

            if DIFF_REMOVED_KEY in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TAGS_KEY].keys():
                for removedTagKey in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TAGS_KEY][DIFF_REMOVED_KEY].keys():
                    currentTags = self.__sourceMediaDescriptor.getTags()
                    # click.ClickException(f"delete media tag patternId={self.__currentPattern.getId()} removedTagKey={removedTagKey} currentTags={currentTags[removedTagKey]}")
                    self.__tac.updateMediaTag(self.__currentPattern.getId(), removedTagKey, currentTags[removedTagKey])

            if DIFF_CHANGED_KEY in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TAGS_KEY].keys():
                for changedTagKey in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TAGS_KEY][DIFF_CHANGED_KEY].keys():
                    currentTags = self.__sourceMediaDescriptor.getTags()
                    # click.ClickException(f"delete media tag patternId={self.__currentPattern.getId()} changedTagKey={changedTagKey} currentTags={currentTags[changedTagKey]}")
                    self.__tac.updateMediaTag(self.__currentPattern.getId(), changedTagKey, currentTags[changedTagKey])

        if MediaDescriptorChangeSet.TRACKS_KEY in self.__mediaChangeSetObj.keys():

            if DIFF_ADDED_KEY in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TRACKS_KEY].keys():

                for trackIndex, trackDescriptor in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TRACKS_KEY][DIFF_ADDED_KEY].items():
                    #targetTracks = [t for t in self.__targetMediaDescriptor.getAllTrackDescriptors() if t.getIndex() == addedTrackIndex]
                    # if targetTracks:
                    #     self.__tc.deleteTrack(targetTracks[0].getId()) # id
                    # self.__tc.deleteTrack(targetTracks[0].getId())
                    self.__tc.addTrack(trackDescriptor, patternId = self.__currentPattern.getId())

            if DIFF_REMOVED_KEY in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TRACKS_KEY].keys():
                trackDescriptor: TrackDescriptor
                for trackIndex, trackDescriptor in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TRACKS_KEY][DIFF_REMOVED_KEY].items():
                    # Track per inspect/update hinzufügen
                    #self.__tc.addTrack(removedTrack, patternId = self.__currentPattern.getId())
                    self.__tc.deleteTrack(trackDescriptor.getId())

            if DIFF_CHANGED_KEY in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TRACKS_KEY].keys():

                # [vsTracks[tp].getIndex()] = trackDiff
                for trackIndex, trackDiff in self.__mediaChangeSetObj[MediaDescriptorChangeSet.TRACKS_KEY][DIFF_CHANGED_KEY].items():

                    targetTracks = [t for t in self.__targetMediaDescriptor.getTrackDescriptors() if t.getIndex() == trackIndex]
                    targetTrackId = targetTracks[0].getId() if targetTracks else None
                    targetTrackIndex = targetTracks[0].getIndex() if targetTracks else None

                    changedCurrentTracks = [t for t in self.__sourceMediaDescriptor.getTrackDescriptors() if t.getIndex() == trackIndex]
                    # changedCurrentTrackId #HINT: Undefined as track descriptors do not come from file with track_id 

                    if TrackDescriptor.TAGS_KEY in trackDiff.keys():
                        tagsDiff = trackDiff[TrackDescriptor.TAGS_KEY]

                        if DIFF_ADDED_KEY in tagsDiff.keys():
                            for tagKey, tagValue in tagsDiff[DIFF_ADDED_KEY].items():

                                # if targetTracks:
                                #     self.__tac.deleteTrackTagByKey(targetTrackId, addedTrackTagKey)
                                self.__tac.updateTrackTag(targetTrackId, tagKey, tagValue)


                        if DIFF_REMOVED_KEY in tagsDiff.keys():
                            for tagKey, tagValue in tagsDiff[DIFF_REMOVED_KEY].items():
                                # if changedCurrentTracks:
                                #     self.__tac.updateTrackTag(targetTrackId, removedTrackTagKey, changedCurrentTracks[0].getTags()[removedTrackTagKey])
                                self.__tac.deleteTrackTagByKey(targetTrackId, tagKey)

                        if DIFF_CHANGED_KEY in tagsDiff.keys():
                            for tagKey, tagValue in tagsDiff[DIFF_CHANGED_KEY].items():
                                # if changedCurrentTracks:
                                #     self.__tac.updateTrackTag(targetTrackId, changedTrackTagKey, changedCurrentTracks[0].getTags()[changedTrackTagKey])
                                self.__tac.updateTrackTag(targetTrackId, tagKey, tagValue)


                    if TrackDescriptor.DISPOSITION_SET_KEY in trackDiff.keys():
                        changedTrackDispositionDiff = trackDiff[TrackDescriptor.DISPOSITION_SET_KEY]

                        if DIFF_ADDED_KEY in changedTrackDispositionDiff.keys():
                            for changedDisposition in changedTrackDispositionDiff[DIFF_ADDED_KEY]:
                                if targetTrackIndex is not None:
                                    self.__tc.setDispositionState(self.__currentPattern.getId(), targetTrackIndex, changedDisposition, True)

                        if DIFF_REMOVED_KEY in changedTrackDispositionDiff.keys():
                            for changedDisposition in changedTrackDispositionDiff[DIFF_REMOVED_KEY]:
                                if targetTrackIndex is not None:
                                    self.__tc.setDispositionState(self.__currentPattern.getId(), targetTrackIndex, changedDisposition, False)
                    

        self.updateDifferences()



    def action_edit_pattern(self):

        patternObj = self.getPatternObjFromInput()

        if patternObj['pattern']:

            selectedPatternId = self.__pc.findPattern(patternObj)
            
            if selectedPatternId is None:
                raise click.ClickException(f"MediaDetailsScreen.action_edit_pattern(): Pattern to edit has no id")

            self.app.push_screen(PatternDetailsScreen(patternId = selectedPatternId, showId = self.getSelectedShowDescriptor().getId()), self.handle_edit_pattern) # <-


    def handle_edit_pattern(self, screenResult):
        self.query_one("#pattern_input", Input).value = screenResult['pattern']
        self.updateDifferences()
