import click

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

from .audio_layout import AudioLayout
from .iso_language import IsoLanguage
from .tag_delete_screen import TagDeleteScreen
from .tag_details_screen import TagDetailsScreen
from .track_codec import TrackCodec
from .track_descriptor import TrackDescriptor
from .track_disposition import TrackDisposition
from .track_type import TrackType

from ffx.helper import formatRichColor, removeRichColor


class TrackDetailsScreen(Screen):

    CSS = """

    Grid {
        grid-size: 5 24;
        grid-rows: 2 2 2 2 2 3 3 2 2 3 2 2 2 2 2 6 2 2 6 2 2 2;
        grid-columns: 25 25 25 25 125;
        height: 100%;
        width: 100%;
        padding: 1;
    }

    Input {
        border: none;
    }
    Button {
        border: none;
    }
    SelectionList {
        border: none;
        min-height: 6;
    }
    Select {
        border: none;
    }

    DataTable {
        min-height: 6;
    }

    DataTable .datatable--cursor {
        background: darkorange;
        color: black;
    }

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

    #toplabel {
        height: 1;
    }

    .two {
        column-span: 2;
    }
    .three {
        column-span: 3;
    }

    .four {
        column-span: 4;
    }
    .five {
        column-span: 5;
    }

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

    .yellow {
        tint: yellow 40%;
    }
    """

    def __init__(
        self,
        trackDescriptor: TrackDescriptor = None,
        patternId=None,
        patternLabel: str = "",
        siblingTrackDescriptors=None,
        trackType: TrackType = None,
        index=None,
        subIndex=None,
    ):
        super().__init__()

        self.context = self.app.getContext()

        self.__configurationData = self.context["config"].getData()

        metadataConfiguration = (
            self.__configurationData["metadata"]
            if "metadata" in self.__configurationData.keys()
            else {}
        )

        self.__removeTrackKeys = (
            metadataConfiguration["streams"]["remove"]
            if "streams" in metadataConfiguration.keys()
            and "remove" in metadataConfiguration["streams"].keys()
            else []
        )
        self.__ignoreTrackKeys = (
            metadataConfiguration["streams"]["ignore"]
            if "streams" in metadataConfiguration.keys()
            and "ignore" in metadataConfiguration["streams"].keys()
            else []
        )

        self.__isNew = trackDescriptor is None
        self.__trackDescriptor = trackDescriptor
        self.__patternId = (
            int(patternId)
            if patternId is not None
            else (
                int(trackDescriptor.getPatternId())
                if trackDescriptor is not None and trackDescriptor.getPatternId() != -1
                else -1
            )
        )
        self.__patternLabel = str(patternLabel)
        self.__siblingTrackDescriptors = list(siblingTrackDescriptors or [])

        if self.__isNew:
            self.__trackType = trackType
            self.__trackCodec = TrackCodec.UNKNOWN
            self.__audioLayout = AudioLayout.LAYOUT_UNDEFINED
            self.__index = index
            self.__subIndex = subIndex
            self.__draftTrackTags = {}
        else:
            self.__trackType = trackDescriptor.getType()
            self.__trackCodec = trackDescriptor.getCodec()
            self.__audioLayout = trackDescriptor.getAudioLayout()
            self.__index = trackDescriptor.getIndex()
            self.__subIndex = trackDescriptor.getSubIndex()
            self.__draftTrackTags = {
                key: value
                for key, value in trackDescriptor.getTags().items()
                if key not in ("language", "title")
            }

    def _descriptor_refs_same_track(self, descriptor: TrackDescriptor) -> bool:
        if self.__trackDescriptor is None:
            return False
        if descriptor.getId() != -1 and self.__trackDescriptor.getId() != -1:
            return descriptor.getId() == self.__trackDescriptor.getId()
        return (
            descriptor.getPatternId() == self.__trackDescriptor.getPatternId()
            and descriptor.getIndex() == self.__trackDescriptor.getIndex()
            and descriptor.getSubIndex() == self.__trackDescriptor.getSubIndex()
        )

    def updateTags(self):

        self.trackTagsTable.clear()

        for key, value in self.__draftTrackTags.items():
            textColor = None
            if key in self.__ignoreTrackKeys:
                textColor = "blue"
            if key in self.__removeTrackKeys:
                textColor = "red"

            row = (formatRichColor(key, textColor), formatRichColor(value, textColor))
            self.trackTagsTable.add_row(*map(str, row))

    def on_mount(self):

        self.query_one("#index_label", Static).update(
            str(self.__index) if self.__index is not None else "-"
        )
        self.query_one("#subindex_label", Static).update(
            str(self.__subIndex) if self.__subIndex is not None else "-"
        )
        self.query_one("#pattern_label", Static).update(self.__patternLabel)

        if self.__trackType is not None:
            self.query_one("#type_select", Select).value = self.__trackType.label()

        self.query_one("#audio_layout_select", Select).value = self.__audioLayout.label()

        for disposition in TrackDisposition:

            dispositionIsSet = (
                self.__trackDescriptor is not None
                and disposition in self.__trackDescriptor.getDispositionSet()
            )

            dispositionOption = (
                disposition.label(),
                disposition.index(),
                dispositionIsSet,
            )
            self.query_one("#dispositions_selection_list", SelectionList).add_option(
                dispositionOption
            )

        if self.__trackDescriptor is not None:
            self.query_one("#language_select", Select).value = (
                self.__trackDescriptor.getLanguage().label()
            )
            self.query_one("#title_input", Input).value = self.__trackDescriptor.getTitle()
            self.updateTags()

    def compose(self):

        self.trackTagsTable = DataTable(classes="five")

        self.column_key_track_tag_key = self.trackTagsTable.add_column("Key", width=50)
        self.column_key_track_tag_value = self.trackTagsTable.add_column("Value", width=100)

        self.trackTagsTable.cursor_type = "row"

        languages = [language.label() for language in IsoLanguage]

        yield Header()

        with Grid():

            yield Static(
                "New stream" if self.__isNew else "Edit stream",
                id="toplabel",
                classes="five",
            )

            yield Static("for pattern")
            yield Static("", id="pattern_label", classes="four", markup=False)

            yield Static(" ", classes="five")

            yield Static("Index / Subindex")
            yield Static("", id="index_label", classes="two")
            yield Static("", id="subindex_label", classes="two")

            yield Static(" ", classes="five")

            yield Static("Type")
            yield Select.from_values(
                [trackType.label() for trackType in TrackType],
                classes="four",
                id="type_select",
            )

            yield Static("Audio Layout")
            yield Select.from_values(
                [layout.label() for layout in AudioLayout],
                classes="four",
                id="audio_layout_select",
            )

            yield Static(" ", classes="five")

            yield Static(" ", classes="five")

            yield Static("Language")
            yield Select.from_values(languages, classes="four", id="language_select")

            yield Static(" ", classes="five")

            yield Static("Title")
            yield Input(id="title_input", classes="four")

            yield Static(" ", classes="five")

            yield Static(" ", classes="five")

            yield Static("Stream tags")
            yield Static(" ")
            yield Button("Add", id="button_add_stream_tag")
            yield Button("Edit", id="button_edit_stream_tag")
            yield Button("Delete", id="button_delete_stream_tag")

            yield self.trackTagsTable

            yield Static(" ", classes="five")

            yield Static("Stream dispositions", classes="five")

            yield SelectionList[int](
                classes="five",
                id="dispositions_selection_list",
            )

            yield Static(" ", classes="five")
            yield Static(" ", classes="five")

            yield Button("Save", id="save_button")
            yield Button("Cancel", id="cancel_button")

            yield Static(" ", classes="five")

            yield Static(" ", classes="five", id="messagestatic")

        yield Footer(id="footer")

    def getTrackDescriptorFromInput(self):

        kwargs = {}
        kwargs[TrackDescriptor.CONTEXT_KEY] = self.context

        if self.__trackDescriptor is not None and self.__trackDescriptor.getId() != -1:
            kwargs[TrackDescriptor.ID_KEY] = self.__trackDescriptor.getId()

        if self.__patternId != -1:
            kwargs[TrackDescriptor.PATTERN_ID_KEY] = int(self.__patternId)

        kwargs[TrackDescriptor.INDEX_KEY] = int(self.__index)
        kwargs[TrackDescriptor.SOURCE_INDEX_KEY] = (
            int(self.__trackDescriptor.getSourceIndex())
            if self.__trackDescriptor is not None
            else int(self.__index)
        )
        if self.__subIndex is not None and int(self.__subIndex) >= 0:
            kwargs[TrackDescriptor.SUB_INDEX_KEY] = int(self.__subIndex)

        selectedTrackType = TrackType.fromLabel(
            self.query_one("#type_select", Select).value
        )
        kwargs[TrackDescriptor.TRACK_TYPE_KEY] = selectedTrackType
        kwargs[TrackDescriptor.CODEC_KEY] = self.__trackCodec

        if selectedTrackType == TrackType.AUDIO:
            kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY] = AudioLayout.fromLabel(
                self.query_one("#audio_layout_select", Select).value
            )
        else:
            kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY] = AudioLayout.LAYOUT_UNDEFINED

        trackTags = dict(self.__draftTrackTags)

        language = self.query_one("#language_select", Select).value
        if language:
            trackTags["language"] = IsoLanguage.find(language).threeLetter()

        title = self.query_one("#title_input", Input).value
        if title:
            trackTags["title"] = title

        kwargs[TrackDescriptor.TAGS_KEY] = trackTags

        dispositionFlags = sum(
            [2 ** flag for flag in self.query_one("#dispositions_selection_list", SelectionList).selected]
        )
        kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = TrackDisposition.toSet(
            dispositionFlags
        )

        return TrackDescriptor(**kwargs)

    def getSelectedTag(self):

        try:
            row_key, _ = self.trackTagsTable.coordinate_to_cell_key(
                self.trackTagsTable.cursor_coordinate
            )

            if row_key is not None:
                selected_tag_data = self.trackTagsTable.get_row(row_key)

                tagKey = removeRichColor(selected_tag_data[0])
                tagValue = removeRichColor(selected_tag_data[1])

                return tagKey, tagValue

            return None

        except CellDoesNotExist:
            return None

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

        if event.button.id == "save_button":
            trackDescriptor = self.getTrackDescriptorFromInput()

            siblingTrackList = [
                descriptor
                for descriptor in self.__siblingTrackDescriptors
                if not self._descriptor_refs_same_track(descriptor)
            ]
            siblingTrackList = [
                descriptor
                for descriptor in siblingTrackList
                if descriptor.getType() == trackDescriptor.getType()
            ]

            numDefaultTracks = len(
                [
                    descriptor
                    for descriptor in siblingTrackList
                    if TrackDisposition.DEFAULT in descriptor.getDispositionSet()
                ]
            )
            numForcedTracks = len(
                [
                    descriptor
                    for descriptor in siblingTrackList
                    if TrackDisposition.FORCED in descriptor.getDispositionSet()
                ]
            )

            if self.__isNew:
                trackDescriptor.setSubIndex(len(siblingTrackList))
            elif self.__subIndex is not None and int(self.__subIndex) >= 0:
                trackDescriptor.setSubIndex(int(self.__subIndex))

            if (
                TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet()
                and numDefaultTracks
            ) or (
                TrackDisposition.FORCED in trackDescriptor.getDispositionSet()
                and numForcedTracks
            ):

                self.query_one("#messagestatic", Static).update(
                    "Cannot add another stream with disposition flag 'default' or 'forced' set"
                )
            else:
                self.query_one("#messagestatic", Static).update(" ")
                self.dismiss(trackDescriptor)

        if event.button.id == "cancel_button":
            self.app.pop_screen()

        if event.button.id == "button_add_stream_tag":
            self.app.push_screen(TagDetailsScreen(), self.handle_update_tag)

        if event.button.id == "button_edit_stream_tag":
            selectedTag = self.getSelectedTag()
            if selectedTag is not None:
                self.app.push_screen(
                    TagDetailsScreen(key=selectedTag[0], value=selectedTag[1]),
                    self.handle_update_tag,
                )

        if event.button.id == "button_delete_stream_tag":
            selectedTag = self.getSelectedTag()
            if selectedTag is not None:
                self.app.push_screen(
                    TagDeleteScreen(key=selectedTag[0], value=selectedTag[1]),
                    self.handle_delete_tag,
                )

    def handle_update_tag(self, tag):
        if tag is None:
            return
        self.__draftTrackTags[str(tag[0])] = str(tag[1])
        self.updateTags()

    def handle_delete_tag(self, trackTag):
        if trackTag is None:
            return
        self.__draftTrackTags.pop(str(trackTag[0]), None)
        self.updateTags()
