from __future__ import annotations

import abc
import types
import typing

from nion.swift import Undo
from nion.swift.model import DisplayItem
from nion.swift.model import DocumentModel
from nion.swift.model import Graphics
from nion.swift.model import Persistence
from nion.ui import CanvasItem
from nion.ui import UserInterface
from nion.ui import Window
from nion.utils import Geometry


class InteractiveTask:
    def __init__(self) -> None:
        pass

    def __enter__(self) -> InteractiveTask:
        return self

    def __exit__(self, exception_type: typing.Optional[typing.Type[BaseException]], value: typing.Optional[BaseException], traceback: typing.Optional[types.TracebackType]) -> typing.Optional[bool]:
        self.close()
        return None

    def close(self) -> None:
        self._close()

    def commit(self) -> None:
        self._commit()

    def _close(self) -> None: ...
    def _commit(self) -> None: ...


class DisplayCanvasItemDelegate(typing.Protocol):

    @property
    def tool_mode(self) -> str: raise NotImplementedError()

    @tool_mode.setter
    def tool_mode(self, value: str) -> None: ...

    def begin_mouse_tracking(self) -> None: ...
    def end_mouse_tracking(self, undo_command: typing.Optional[Undo.UndoableCommand]) -> None: ...
    def delete_key_pressed(self) -> None: ...
    def enter_key_pressed(self) -> None: ...
    def cursor_changed(self, pos: typing.Optional[typing.Tuple[int, ...]]) -> None: ...
    def update_display_properties(self, display_properties: Persistence.PersistentDictType) -> None: ...
    def update_display_data_channel_properties(self, display_data_channel_properties: Persistence.PersistentDictType) -> None: ...
    def create_change_display_command(self, *, command_id: typing.Optional[str] = None, is_mergeable: bool = False) -> Undo.UndoableCommand: ...
    def create_change_graphics_command(self) -> Undo.UndoableCommand: ...
    def create_insert_graphics_command(self, graphics: typing.Sequence[Graphics.Graphic]) -> Undo.UndoableCommand: ...
    def create_move_display_layer_command(self, display_item: DisplayItem.DisplayItem, src_index: int, target_index: int) -> Undo.UndoableCommand: ...
    def push_undo_command(self, command: Undo.UndoableCommand) -> None: ...
    def create_change_display_properties_task(self) -> InteractiveTask: ...
    def create_create_graphic_task(self, graphic_type: str, start_position: Geometry.FloatPoint) -> InteractiveTask: ...
    def create_change_graphics_task(self) -> InteractiveTask: ...
    def add_index_to_selection(self, index: int) -> None: ...
    def remove_index_from_selection(self, index: int) -> None: ...
    def set_selection(self, index: int) -> None: ...
    def clear_selection(self) -> None: ...
    def add_and_select_region(self, region: Graphics.Graphic) -> Undo.UndoableCommand: ...
    def nudge_selected_graphics(self, mapping: Graphics.CoordinateMappingLike, delta: Geometry.FloatSize) -> None: ...
    def nudge_slice(self, delta: int) -> None: ...
    def drag_graphics(self, graphics: typing.Sequence[Graphics.Graphic]) -> None: ...
    def adjust_graphics(self, widget_mapping: Graphics.CoordinateMappingLike, graphic_drag_items: typing.Sequence[Graphics.Graphic], graphic_drag_part: str, graphic_part_data: typing.Dict[int, Graphics.DragPartData], graphic_drag_start_pos: Geometry.FloatPoint, pos: Geometry.FloatPoint, modifiers: UserInterface.KeyboardModifiers) -> None: ...
    def display_clicked(self, modifiers: UserInterface.KeyboardModifiers) -> bool: ...
    def image_clicked(self, image_position: Geometry.FloatPoint, modifiers: UserInterface.KeyboardModifiers) -> bool: ...
    def image_mouse_pressed(self, image_position: Geometry.FloatPoint, modifiers: UserInterface.KeyboardModifiers) -> bool: ...
    def image_mouse_released(self, image_position: Geometry.FloatPoint, modifiers: UserInterface.KeyboardModifiers) -> bool: ...
    def image_mouse_position_changed(self, image_position: Geometry.FloatPoint, modifiers: UserInterface.KeyboardModifiers) -> bool: ...
    def show_display_context_menu(self, gx: int, gy: int) -> bool: ...
    def get_document_model(self) -> DocumentModel.DocumentModel: ...
    def create_rectangle(self, pos: Geometry.FloatPoint) -> Graphics.RectangleGraphic: ...
    def create_ellipse(self, pos: Geometry.FloatPoint) -> Graphics.EllipseGraphic: ...
    def create_line(self, pos: Geometry.FloatPoint) -> Graphics.LineGraphic: ...
    def create_point(self, pos: Geometry.FloatPoint) -> Graphics.PointGraphic: ...
    def create_line_profile(self, pos: Geometry.FloatPoint) -> Graphics.LineProfileGraphic: ...
    def create_spot(self, pos: Geometry.FloatPoint) -> Graphics.SpotGraphic: ...
    def create_wedge(self, angle: float) -> Graphics.WedgeGraphic: ...
    def create_ring(self, radius: float) -> Graphics.RingGraphic: ...
    def create_lattice(self, u_pos: Geometry.FloatSize) -> Graphics.LatticeGraphic: ...


class DisplayCanvasItem(CanvasItem.CanvasItemComposition):

    @property
    def default_aspect_ratio(self) -> float:
        return 1.0

    @property
    def key_contexts(self) -> typing.Sequence[str]:
        """Return key contexts.

        Key contexts provide an ordered list of contexts that are used to determine
        which actions are valid at a given time. The contexts are checked in reverse
        order (i.e. last added have highest precedence).
        """
        return list()

    @property
    def mouse_mapping(self) -> Graphics.CoordinateMappingLike: raise NotImplementedError()

    @abc.abstractmethod
    def add_display_control(self, display_control_canvas_item: CanvasItem.AbstractCanvasItem, role: typing.Optional[str] = None) -> None: ...

    @abc.abstractmethod
    def handle_auto_display(self) -> bool: ...

    def update_display_data_delta(self, display_data_delta: DisplayItem.DisplayDataDelta) -> None: ...
