import os
import asyncio
import carb.events
import carb.input
import omni.appwindow
import omni.ui as ui


class ExternalDragDrop():
    def __init__(self, window_name: str, drag_drop_fn: callable):
        self._window_name = window_name
        self._drag_drop_fn = drag_drop_fn

        # subscribe to external drag/drop events
        app_window = omni.appwindow.get_default_app_window()
        self._dropsub = app_window.get_window_drop_event_stream().create_subscription_to_pop(
            self._on_drag_drop_external, name="ExternalDragDrop event", order=0
        )

    def destroy(self):
        self._drag_drop_fn = None
        self._dropsub = None

    def get_current_mouse_coords(self):
        app_window = omni.appwindow.get_default_app_window()
        input = carb.input.acquire_input_interface()
        dpi_scale = ui.Workspace.get_dpi_scale()
        pos_x, pos_y = input.get_mouse_coords_pixel(app_window.get_mouse())
        return pos_x / dpi_scale, pos_y / dpi_scale

    def is_window_hovered(self, pos_x, pos_y, window_name):
        window = ui.Workspace.get_window(window_name)
        # if window hasn't been drawn yet docked may not be valid, so check dock_id also
        if not window or not window.visible or ((window.docked or window.dock_id != 0) and not window.is_selected_in_dock()):
            return False

        if (pos_x > window.position_x and
            pos_y > window.position_y and
            pos_x < window.position_x + window.width and
            pos_y < window.position_y + window.height
            ):
            return True
        return False

    def _on_drag_drop_external(self, e: carb.events.IEvent):
        # need to wait until next frame as otherwise mouse coords can be wrong
        async def do_drag_drop():
            await omni.kit.app.get_app().next_update_async()
            mouse_x, mouse_y = self.get_current_mouse_coords()
            if self.is_window_hovered(mouse_x, mouse_y, self._window_name):
                self._drag_drop_fn(self, list(e.payload['paths']))

        asyncio.ensure_future(do_drag_drop())

    def expand_payload(self, payload):
        new_payload = []
        for file_path in payload:
            if os.path.isdir(file_path):
                for root, subdirs, files in os.walk(file_path):
                    for file in files:
                        new_payload.append(os.path.join(root, file))
            else:
                new_payload.append(file_path)
        return new_payload
