# Released under the MIT License. See LICENSE for details.
#
"""Provides UI for editing a soundtrack."""

from __future__ import annotations

import copy
import os
from typing import TYPE_CHECKING, cast

import bascenev1 as bs
import bauiv1 as bui

if TYPE_CHECKING:
    from typing import Any


class SoundtrackEditWindow(bui.Window):
    """Window for editing a soundtrack."""

    def __init__(
        self,
        existing_soundtrack: str | dict[str, Any] | None,
        transition: str = 'in_right',
    ):
        # pylint: disable=too-many-statements
        appconfig = bui.app.config
        self._r = 'editSoundtrackWindow'
        self._folder_tex = bui.gettexture('folder')
        self._file_tex = bui.gettexture('file')
        assert bui.app.classic is not None
        uiscale = bui.app.ui_v1.uiscale
        self._width = 848 if uiscale is bui.UIScale.SMALL else 648
        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
        self._height = (
            395
            if uiscale is bui.UIScale.SMALL
            else 450 if uiscale is bui.UIScale.MEDIUM else 560
        )
        super().__init__(
            root_widget=bui.containerwidget(
                size=(self._width, self._height),
                transition=transition,
                scale=(
                    2.08
                    if uiscale is bui.UIScale.SMALL
                    else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
                ),
                stack_offset=(
                    (0, -48)
                    if uiscale is bui.UIScale.SMALL
                    else (0, 15) if uiscale is bui.UIScale.MEDIUM else (0, 0)
                ),
            )
        )
        cancel_button = bui.buttonwidget(
            parent=self._root_widget,
            position=(38 + x_inset, self._height - 60),
            size=(160, 60),
            autoselect=True,
            label=bui.Lstr(resource='cancelText'),
            scale=0.8,
        )
        save_button = bui.buttonwidget(
            parent=self._root_widget,
            position=(self._width - (168 + x_inset), self._height - 60),
            autoselect=True,
            size=(160, 60),
            label=bui.Lstr(resource='saveText'),
            scale=0.8,
        )
        bui.widget(edit=save_button, left_widget=cancel_button)
        bui.widget(edit=cancel_button, right_widget=save_button)
        bui.textwidget(
            parent=self._root_widget,
            position=(0, self._height - 50),
            size=(self._width, 25),
            text=bui.Lstr(
                resource=self._r
                + (
                    '.editSoundtrackText'
                    if existing_soundtrack is not None
                    else '.newSoundtrackText'
                )
            ),
            color=bui.app.ui_v1.title_color,
            h_align='center',
            v_align='center',
            maxwidth=280,
        )
        v = self._height - 110
        if 'Soundtracks' not in appconfig:
            appconfig['Soundtracks'] = {}

        self._soundtrack_name: str | None
        self._existing_soundtrack_name: str | None
        if existing_soundtrack is not None:
            # if they passed just a name, pull info from that soundtrack
            if isinstance(existing_soundtrack, str):
                self._soundtrack = copy.deepcopy(
                    appconfig['Soundtracks'][existing_soundtrack]
                )
                self._soundtrack_name = existing_soundtrack
                self._existing_soundtrack_name = existing_soundtrack
                self._last_edited_song_type = None
            else:
                # otherwise they can pass info on an in-progress edit
                self._soundtrack = existing_soundtrack['soundtrack']
                self._soundtrack_name = existing_soundtrack['name']
                self._existing_soundtrack_name = existing_soundtrack[
                    'existing_name'
                ]
                self._last_edited_song_type = existing_soundtrack[
                    'last_edited_song_type'
                ]
        else:
            self._soundtrack_name = None
            self._existing_soundtrack_name = None
            self._soundtrack = {}
            self._last_edited_song_type = None

        bui.textwidget(
            parent=self._root_widget,
            text=bui.Lstr(resource=self._r + '.nameText'),
            maxwidth=80,
            scale=0.8,
            position=(105 + x_inset, v + 19),
            color=(0.8, 0.8, 0.8, 0.5),
            size=(0, 0),
            h_align='right',
            v_align='center',
        )

        # if there's no initial value, find a good initial unused name
        if existing_soundtrack is None:
            i = 1
            st_name_text = bui.Lstr(
                resource=self._r + '.newSoundtrackNameText'
            ).evaluate()
            if '${COUNT}' not in st_name_text:
                # make sure we insert number *somewhere*
                st_name_text = st_name_text + ' ${COUNT}'
            while True:
                self._soundtrack_name = st_name_text.replace('${COUNT}', str(i))
                if self._soundtrack_name not in appconfig['Soundtracks']:
                    break
                i += 1

        self._text_field = bui.textwidget(
            parent=self._root_widget,
            position=(120 + x_inset, v - 5),
            size=(self._width - (160 + 2 * x_inset), 43),
            text=self._soundtrack_name,
            h_align='left',
            v_align='center',
            max_chars=32,
            autoselect=True,
            description=bui.Lstr(resource=self._r + '.nameText'),
            editable=True,
            padding=4,
            on_return_press_call=self._do_it_with_sound,
        )

        scroll_height = self._height - 180
        self._scrollwidget = scrollwidget = bui.scrollwidget(
            parent=self._root_widget,
            highlight=False,
            position=(40 + x_inset, v - (scroll_height + 10)),
            size=(self._width - (80 + 2 * x_inset), scroll_height),
            simple_culling_v=10,
            claims_left_right=True,
            claims_tab=True,
            selection_loops_to_parent=True,
        )
        bui.widget(edit=self._text_field, down_widget=self._scrollwidget)
        self._col = bui.columnwidget(
            parent=scrollwidget,
            claims_left_right=True,
            claims_tab=True,
            selection_loops_to_parent=True,
        )

        self._song_type_buttons: dict[str, bui.Widget] = {}
        self._refresh()
        bui.buttonwidget(edit=cancel_button, on_activate_call=self._cancel)
        bui.containerwidget(edit=self._root_widget, cancel_button=cancel_button)
        bui.buttonwidget(edit=save_button, on_activate_call=self._do_it)
        bui.containerwidget(edit=self._root_widget, start_button=save_button)
        bui.widget(edit=self._text_field, up_widget=cancel_button)
        bui.widget(edit=cancel_button, down_widget=self._text_field)

    def _refresh(self) -> None:
        for widget in self._col.get_children():
            widget.delete()

        types = [
            'Menu',
            'CharSelect',
            'ToTheDeath',
            'Onslaught',
            'Keep Away',
            'Race',
            'Epic Race',
            'ForwardMarch',
            'FlagCatcher',
            'Survival',
            'Epic',
            'Hockey',
            'Football',
            'Flying',
            'Scary',
            'Marching',
            'GrandRomp',
            'Chosen One',
            'Scores',
            'Victory',
        ]

        # FIXME: We should probably convert this to use translations.
        type_names_translated = bui.app.lang.get_resource('soundtrackTypeNames')
        prev_type_button: bui.Widget | None = None
        prev_test_button: bui.Widget | None = None

        for index, song_type in enumerate(types):
            row = bui.rowwidget(
                parent=self._col,
                size=(self._width - 40, 40),
                claims_left_right=True,
                claims_tab=True,
                selection_loops_to_parent=True,
            )
            type_name = type_names_translated.get(song_type, song_type)
            bui.textwidget(
                parent=row,
                size=(230, 25),
                always_highlight=True,
                text=type_name,
                scale=0.7,
                h_align='left',
                v_align='center',
                maxwidth=190,
            )

            if song_type in self._soundtrack:
                entry = self._soundtrack[song_type]
            else:
                entry = None

            if entry is not None:
                # Make sure they don't muck with this after it gets to us.
                entry = copy.deepcopy(entry)

            icon_type = self._get_entry_button_display_icon_type(entry)
            self._song_type_buttons[song_type] = btn = bui.buttonwidget(
                parent=row,
                size=(230, 32),
                label=self._get_entry_button_display_name(entry),
                text_scale=0.6,
                on_activate_call=bui.Call(
                    self._get_entry, song_type, entry, type_name
                ),
                icon=(
                    self._file_tex
                    if icon_type == 'file'
                    else self._folder_tex if icon_type == 'folder' else None
                ),
                icon_color=(
                    (1.1, 0.8, 0.2) if icon_type == 'folder' else (1, 1, 1)
                ),
                left_widget=self._text_field,
                iconscale=0.7,
                autoselect=True,
                up_widget=prev_type_button,
            )
            if index == 0:
                bui.widget(edit=btn, up_widget=self._text_field)
            bui.widget(edit=btn, down_widget=btn)

            if (
                self._last_edited_song_type is not None
                and song_type == self._last_edited_song_type
            ):
                bui.containerwidget(
                    edit=row, selected_child=btn, visible_child=btn
                )
                bui.containerwidget(
                    edit=self._col, selected_child=row, visible_child=row
                )
                bui.containerwidget(
                    edit=self._scrollwidget,
                    selected_child=self._col,
                    visible_child=self._col,
                )
                bui.containerwidget(
                    edit=self._root_widget,
                    selected_child=self._scrollwidget,
                    visible_child=self._scrollwidget,
                )

            if prev_type_button is not None:
                bui.widget(edit=prev_type_button, down_widget=btn)
            prev_type_button = btn
            bui.textwidget(parent=row, size=(10, 32), text='')  # spacing
            assert bui.app.classic is not None
            btn = bui.buttonwidget(
                parent=row,
                size=(50, 32),
                label=bui.Lstr(resource=self._r + '.testText'),
                text_scale=0.6,
                on_activate_call=bui.Call(self._test, bs.MusicType(song_type)),
                up_widget=(
                    prev_test_button
                    if prev_test_button is not None
                    else self._text_field
                ),
            )
            if prev_test_button is not None:
                bui.widget(edit=prev_test_button, down_widget=btn)
            bui.widget(edit=btn, down_widget=btn, right_widget=btn)
            prev_test_button = btn

    @classmethod
    def _restore_editor(
        cls, state: dict[str, Any], musictype: str, entry: Any
    ) -> None:
        assert bui.app.classic is not None
        music = bui.app.classic.music

        # Apply the change and recreate the window.
        soundtrack = state['soundtrack']
        existing_entry = (
            None if musictype not in soundtrack else soundtrack[musictype]
        )
        if existing_entry != entry:
            bui.getsound('gunCocking').play()

        # Make sure this doesn't get mucked with after we get it.
        if entry is not None:
            entry = copy.deepcopy(entry)

        entry_type = music.get_soundtrack_entry_type(entry)
        if entry_type == 'default':
            # For 'default' entries simply exclude them from the list.
            if musictype in soundtrack:
                del soundtrack[musictype]
        else:
            soundtrack[musictype] = entry

        bui.app.ui_v1.set_main_menu_window(
            cls(state, transition='in_left').get_root_widget(),
            from_window=False,  # Disable check here.
        )

    def _get_entry(
        self, song_type: str, entry: Any, selection_target_name: str
    ) -> None:
        assert bui.app.classic is not None
        music = bui.app.classic.music

        # no-op if our underlying widget is dead or on its way out.
        if not self._root_widget or self._root_widget.transitioning_out:
            return

        if selection_target_name != '':
            selection_target_name = "'" + selection_target_name + "'"
        state = {
            'name': self._soundtrack_name,
            'existing_name': self._existing_soundtrack_name,
            'soundtrack': self._soundtrack,
            'last_edited_song_type': song_type,
        }
        bui.containerwidget(edit=self._root_widget, transition='out_left')
        bui.app.ui_v1.set_main_menu_window(
            music.get_music_player()
            .select_entry(
                bui.Call(self._restore_editor, state, song_type),
                entry,
                selection_target_name,
            )
            .get_root_widget(),
            from_window=self._root_widget,
        )

    def _test(self, song_type: bs.MusicType) -> None:
        assert bui.app.classic is not None
        music = bui.app.classic.music

        # Warn if volume is zero.
        if bui.app.config.resolve('Music Volume') < 0.01:
            bui.getsound('error').play()
            bui.screenmessage(
                bui.Lstr(resource=self._r + '.musicVolumeZeroWarning'),
                color=(1, 0.5, 0),
            )
        music.set_music_play_mode(bui.app.classic.MusicPlayMode.TEST)
        music.do_play_music(
            song_type,
            mode=bui.app.classic.MusicPlayMode.TEST,
            testsoundtrack=self._soundtrack,
        )

    def _get_entry_button_display_name(self, entry: Any) -> str | bui.Lstr:
        assert bui.app.classic is not None
        music = bui.app.classic.music
        etype = music.get_soundtrack_entry_type(entry)
        ename: str | bui.Lstr
        if etype == 'default':
            ename = bui.Lstr(resource=self._r + '.defaultGameMusicText')
        elif etype in ('musicFile', 'musicFolder'):
            ename = os.path.basename(music.get_soundtrack_entry_name(entry))
        else:
            ename = music.get_soundtrack_entry_name(entry)
        return ename

    def _get_entry_button_display_icon_type(self, entry: Any) -> str | None:
        assert bui.app.classic is not None
        music = bui.app.classic.music
        etype = music.get_soundtrack_entry_type(entry)
        if etype == 'musicFile':
            return 'file'
        if etype == 'musicFolder':
            return 'folder'
        return None

    def _cancel(self) -> None:
        from bauiv1lib.soundtrack import browser as stb

        # no-op if our underlying widget is dead or on its way out.
        if not self._root_widget or self._root_widget.transitioning_out:
            return

        assert bui.app.classic is not None
        music = bui.app.classic.music

        # Resets music back to normal.
        music.set_music_play_mode(bui.app.classic.MusicPlayMode.REGULAR)
        bui.containerwidget(edit=self._root_widget, transition='out_right')
        bui.app.ui_v1.set_main_menu_window(
            stb.SoundtrackBrowserWindow(transition='in_left').get_root_widget(),
            from_window=self._root_widget,
        )

    def _do_it(self) -> None:
        from bauiv1lib.soundtrack import browser as stb

        # no-op if our underlying widget is dead or on its way out.
        if not self._root_widget or self._root_widget.transitioning_out:
            return

        assert bui.app.classic is not None
        music = bui.app.classic.music
        cfg = bui.app.config
        new_name = cast(str, bui.textwidget(query=self._text_field))
        if new_name != self._soundtrack_name and new_name in cfg['Soundtracks']:
            bui.screenmessage(
                bui.Lstr(resource=self._r + '.cantSaveAlreadyExistsText')
            )
            bui.getsound('error').play()
            return
        if not new_name:
            bui.getsound('error').play()
            return
        if (
            new_name
            == bui.Lstr(
                resource=self._r + '.defaultSoundtrackNameText'
            ).evaluate()
        ):
            bui.screenmessage(
                bui.Lstr(resource=self._r + '.cantOverwriteDefaultText')
            )
            bui.getsound('error').play()
            return

        # Make sure config exists.
        if 'Soundtracks' not in cfg:
            cfg['Soundtracks'] = {}

        # If we had an old one, delete it.
        if (
            self._existing_soundtrack_name is not None
            and self._existing_soundtrack_name in cfg['Soundtracks']
        ):
            del cfg['Soundtracks'][self._existing_soundtrack_name]
        cfg['Soundtracks'][new_name] = self._soundtrack
        cfg['Soundtrack'] = new_name

        cfg.commit()
        bui.getsound('gunCocking').play()
        bui.containerwidget(edit=self._root_widget, transition='out_right')

        # Resets music back to normal.
        music.set_music_play_mode(
            bui.app.classic.MusicPlayMode.REGULAR, force_restart=True
        )

        bui.app.ui_v1.set_main_menu_window(
            stb.SoundtrackBrowserWindow(transition='in_left').get_root_widget(),
            from_window=self._root_widget,
        )

    def _do_it_with_sound(self) -> None:
        bui.getsound('swish').play()
        self._do_it()
