# Urwid __init__.py - all the stuff you're likely to care about
#
#    Copyright (C) 2004-2012  Ian Ward
#
#    This library is free software; you can redistribute it and/or
#    modify it under the terms of the GNU Lesser General Public
#    License as published by the Free Software Foundation; either
#    version 2.1 of the License, or (at your option) any later version.
#
#    This library is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#    Lesser General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public
#    License along with this library; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Urwid web site: https://urwid.org/


from __future__ import annotations

import importlib.util
import sys
import types
import typing
import warnings

from urwid.canvas import (
    BlankCanvas,
    Canvas,
    CanvasCache,
    CanvasCombine,
    CanvasError,
    CanvasJoin,
    CanvasOverlay,
    CompositeCanvas,
    SolidCanvas,
    TextCanvas,
)
from urwid.command_map import (
    ACTIVATE,
    CURSOR_DOWN,
    CURSOR_LEFT,
    CURSOR_MAX_LEFT,
    CURSOR_MAX_RIGHT,
    CURSOR_PAGE_DOWN,
    CURSOR_PAGE_UP,
    CURSOR_RIGHT,
    CURSOR_UP,
    REDRAW_SCREEN,
    CommandMap,
    command_map,
)
from urwid.font import (
    Font,
    FontRegistry,
    HalfBlock5x4Font,
    HalfBlock6x5Font,
    HalfBlock7x7Font,
    HalfBlockHeavy6x5Font,
    Sextant2x2Font,
    Sextant3x3Font,
    Thin3x3Font,
    Thin4x3Font,
    Thin6x6Font,
    get_all_fonts,
)
from urwid.signals import (
    MetaSignals,
    Signals,
    connect_signal,
    disconnect_signal,
    disconnect_signal_by_key,
    emit_signal,
    register_signal,
)
from urwid.str_util import calc_text_pos, calc_width, is_wide_char, move_next_char, move_prev_char, within_double_byte
from urwid.text_layout import LayoutSegment, StandardTextLayout, TextLayout, default_layout
from urwid.util import (
    MetaSuper,
    TagMarkupException,
    apply_target_encoding,
    calc_trim_text,
    decompose_tagmarkup,
    detected_encoding,
    get_encoding_mode,
    int_scale,
    is_mouse_event,
    set_encoding,
    supports_unicode,
)
from urwid.version import version as __version__
from urwid.version import version_tuple as __version_tuple__

from . import display, event_loop, widget
from .display import (
    BLACK,
    BROWN,
    DARK_BLUE,
    DARK_CYAN,
    DARK_GRAY,
    DARK_GREEN,
    DARK_MAGENTA,
    DARK_RED,
    DEFAULT,
    LIGHT_BLUE,
    LIGHT_CYAN,
    LIGHT_GRAY,
    LIGHT_GREEN,
    LIGHT_MAGENTA,
    LIGHT_RED,
    UPDATE_PALETTE_ENTRY,
    WHITE,
    YELLOW,
    AttrSpec,
    AttrSpecError,
    BaseScreen,
    RealTerminal,
    ScreenError,
)
from .event_loop import AsyncioEventLoop, EventLoop, ExitMainLoop, MainLoop, SelectEventLoop
from .widget import (
    ANY,
    BOTTOM,
    BOX,
    CENTER,
    CLIP,
    ELLIPSIS,
    FIXED,
    FLOW,
    GIVEN,
    LEFT,
    MIDDLE,
    PACK,
    RELATIVE,
    RELATIVE_100,
    RIGHT,
    SPACE,
    TOP,
    WEIGHT,
    AbstractWidget,
    Align,
    AttrMap,
    AttrMapError,
    AttrWrap,
    BarGraph,
    BarGraphError,
    BarGraphMeta,
    BigText,
    BoxAdapter,
    BoxAdapterError,
    Button,
    CheckBox,
    CheckBoxError,
    Columns,
    ColumnsError,
    Divider,
    Edit,
    EditError,
    Filler,
    FillerError,
    Frame,
    FrameError,
    GraphVScale,
    GridFlow,
    GridFlowError,
    IntEdit,
    LineBox,
    ListBox,
    ListBoxError,
    ListWalker,
    ListWalkerError,
    MonitoredFocusList,
    MonitoredList,
    Overlay,
    OverlayError,
    Padding,
    PaddingError,
    ParentNode,
    Pile,
    PileError,
    PopUpLauncher,
    PopUpTarget,
    ProgressBar,
    RadioButton,
    Scrollable,
    ScrollBar,
    SelectableIcon,
    SimpleFocusListWalker,
    SimpleListWalker,
    Sizing,
    SolidFill,
    Text,
    TextError,
    TreeListBox,
    TreeNode,
    TreeWalker,
    TreeWidget,
    TreeWidgetError,
    VAlign,
    WHSettings,
    Widget,
    WidgetContainerMixin,
    WidgetDecoration,
    WidgetDisable,
    WidgetError,
    WidgetMeta,
    WidgetPlaceholder,
    WidgetWrap,
    WidgetWrapError,
    WrapMode,
    delegate_to_widget_mixin,
    fixed_size,
    scale_bar_values,
)

__all__: tuple[str, ...] = (
    "ACTIVATE",
    "ANY",
    "BLACK",
    "BOTTOM",
    "BOX",
    "BROWN",
    "CENTER",
    "CLIP",
    "CURSOR_DOWN",
    "CURSOR_LEFT",
    "CURSOR_MAX_LEFT",
    "CURSOR_MAX_RIGHT",
    "CURSOR_PAGE_DOWN",
    "CURSOR_PAGE_UP",
    "CURSOR_RIGHT",
    "CURSOR_UP",
    "DARK_BLUE",
    "DARK_CYAN",
    "DARK_GRAY",
    "DARK_GREEN",
    "DARK_MAGENTA",
    "DARK_RED",
    "DEFAULT",
    "ELLIPSIS",
    "FIXED",
    "FLOW",
    "GIVEN",
    "LEFT",
    "LIGHT_BLUE",
    "LIGHT_CYAN",
    "LIGHT_GRAY",
    "LIGHT_GREEN",
    "LIGHT_MAGENTA",
    "LIGHT_RED",
    "MIDDLE",
    "PACK",
    "REDRAW_SCREEN",
    "RELATIVE",
    "RELATIVE_100",
    "RIGHT",
    "SPACE",
    "TOP",
    "UPDATE_PALETTE_ENTRY",
    "WEIGHT",
    "WHITE",
    "YELLOW",
    "AbstractWidget",
    "Align",
    "AsyncioEventLoop",
    "AttrMap",
    "AttrMapError",
    "AttrSpec",
    "AttrSpecError",
    "AttrWrap",
    "BarGraph",
    "BarGraphError",
    "BarGraphMeta",
    "BaseScreen",
    "BigText",
    "BlankCanvas",
    "BoxAdapter",
    "BoxAdapterError",
    "Button",
    "Canvas",
    "CanvasCache",
    "CanvasCombine",
    "CanvasError",
    "CanvasJoin",
    "CanvasOverlay",
    "CheckBox",
    "CheckBoxError",
    "Columns",
    "ColumnsError",
    "CommandMap",
    "CompositeCanvas",
    "Divider",
    "Edit",
    "EditError",
    "EventLoop",
    "ExitMainLoop",
    "Filler",
    "FillerError",
    "Font",
    "FontRegistry",
    "Frame",
    "FrameError",
    "GraphVScale",
    "GridFlow",
    "GridFlowError",
    "HalfBlock5x4Font",
    "HalfBlock6x5Font",
    "HalfBlock7x7Font",
    "HalfBlockHeavy6x5Font",
    "IntEdit",
    "LayoutSegment",
    "LineBox",
    "ListBox",
    "ListBoxError",
    "ListWalker",
    "ListWalkerError",
    "MainLoop",
    "MetaSignals",
    "MetaSuper",
    "MonitoredFocusList",
    "MonitoredList",
    "Overlay",
    "OverlayError",
    "Padding",
    "PaddingError",
    "ParentNode",
    "Pile",
    "PileError",
    "PopUpLauncher",
    "PopUpTarget",
    "ProgressBar",
    "RadioButton",
    "RealTerminal",
    "ScreenError",
    "ScrollBar",
    "Scrollable",
    "SelectEventLoop",
    "SelectableIcon",
    "Sextant2x2Font",
    "Sextant3x3Font",
    "Signals",
    "SimpleFocusListWalker",
    "SimpleListWalker",
    "Sizing",
    "SolidCanvas",
    "SolidFill",
    "StandardTextLayout",
    "TagMarkupException",
    "Text",
    "TextCanvas",
    "TextError",
    "TextLayout",
    "Thin3x3Font",
    "Thin4x3Font",
    "Thin6x6Font",
    "TreeListBox",
    "TreeNode",
    "TreeWalker",
    "TreeWidget",
    "TreeWidgetError",
    "VAlign",
    "WHSettings",
    "Widget",
    "WidgetContainerMixin",
    "WidgetDecoration",
    "WidgetDisable",
    "WidgetError",
    "WidgetMeta",
    "WidgetPlaceholder",
    "WidgetWrap",
    "WidgetWrapError",
    "WrapMode",
    "__version__",
    "__version_tuple__",
    "apply_target_encoding",
    "calc_text_pos",
    "calc_trim_text",
    "calc_width",
    "command_map",
    "connect_signal",
    "decompose_tagmarkup",
    "default_layout",
    "delegate_to_widget_mixin",
    "detected_encoding",
    "disconnect_signal",
    "disconnect_signal_by_key",
    "display",
    "emit_signal",
    "event_loop",
    "fixed_size",
    "get_all_fonts",
    "get_encoding_mode",
    "int_scale",
    "is_mouse_event",
    "is_wide_char",
    "move_next_char",
    "move_prev_char",
    "register_signal",
    "scale_bar_values",
    "set_encoding",
    "supports_unicode",
    "widget",
    "within_double_byte",
)


# Optional event loops with external dependencies

try:
    from .event_loop import TornadoEventLoop
except ImportError:
    pass
else:
    __all__ += ("TornadoEventLoop",)

try:
    from .event_loop import GLibEventLoop
except ImportError:
    pass
else:
    __all__ += ("GLibEventLoop",)

try:
    from .event_loop import TwistedEventLoop
except ImportError:
    pass
else:
    __all__ += ("TwistedEventLoop",)

try:
    from .event_loop import TrioEventLoop
except ImportError:
    pass
else:
    __all__ += ("TrioEventLoop",)

# OS Specific
if sys.platform != "win32":
    from .vterm import TermCanvas, TermCharset, Terminal, TermModes

    __all__ += ("TermCanvas", "TermCharset", "TermModes", "Terminal")

    # ZMQEventLoop cause interpreter crash on Windows
    try:
        from .event_loop import ZMQEventLoop
    except ImportError:
        pass
    else:
        __all__ += ("ZMQEventLoop",)

# Backward compatibility
VERSION = __version_tuple__


# Moved modules handling
__locals: dict[str, typing.Any] = locals()  # use mutable access for pure lazy loading

# Backward compatible lazy load with deprecation warnings
_moved_warn: dict[str, str] = {}
# Before DeprecationWarning need to start PendingDeprecationWarning process.


def lazy_import(name: str, package: str | None = None) -> types.ModuleType:
    """Lazy import implementation from Python documentation.

    Useful for cases where no warnings expected for moved modules.
    """
    spec = importlib.util.find_spec(name, package)
    if not spec:
        raise ImportError(f"No module named {name!r}")
    if not spec.loader:
        raise ImportError(f"Module named {name!r} is invalid")

    loader = importlib.util.LazyLoader(spec.loader)
    spec.loader = loader
    module = importlib.util.module_from_spec(spec)
    if not package:
        sys.modules[name] = module
    else:
        sys.modules[f"{package.rstrip('.')}.{name.lstrip('.')}"] = module
    loader.exec_module(module)
    return module


class _MovedModule(types.ModuleType):
    """Special class to handle moved modules.

    PEP-0562 handles moved modules attributes, but unfortunately not handle nested modules access
    like "from xxx.yyy import zzz"
    """

    __slots__ = ("_moved_from", "_moved_to")

    def __init__(self, moved_from: str, moved_to: str) -> None:
        super().__init__(moved_from.join(".")[-1])
        self._moved_from = moved_from
        self._moved_to = moved_to

    def __getattr__(self, name: str) -> typing.Any:
        real_module = importlib.import_module(self._moved_to)
        sys.modules[self._moved_from] = real_module
        return getattr(real_module, name)


display_common = lazy_import("urwid.display.common")
raw_display = lazy_import("urwid.display.raw")
curses_display = lazy_import("urwid.display.curses")
escape = lazy_import("urwid.display.escape")


class _MovedModuleWarn(_MovedModule):
    """Special class to handle moved modules.

    Produce DeprecationWarning messages for imports.
    """

    __slots__ = ()

    def __getattr__(self, name: str) -> typing.Any:
        warnings.warn(
            f"{self._moved_from} is moved to {self._moved_to}",
            DeprecationWarning,
            stacklevel=2,
        )
        return super().__getattr__(name)


for _name, _module in _moved_warn.items():
    _module_path = f"{__name__}.{_name}"
    sys.modules[_module_path] = _MovedModuleWarn(_module_path, _module)


def __getattr__(name: str) -> typing.Any:
    """Get attributes lazy.

    :return: attribute by name
    :raises AttributeError: attribute is not defined for lazy load
    """
    if name in _moved_warn:
        warnings.warn(
            f"{name} is moved to {_moved_warn[name]}",
            DeprecationWarning,
            stacklevel=2,
        )

        mod = importlib.import_module(_moved_warn[name])
        __locals[name] = mod
        return mod
    raise AttributeError(f"{name} not found in {__package__}")
