# PyGetWindow
# A cross-platform module to find information about the windows on the screen.

# Work in progress

# Useful info:
# https://stackoverflow.com/questions/373020/finding-the-current-active-window-in-mac-os-x-using-python
# https://stackoverflow.com/questions/7142342/get-window-position-size-with-python


# win32 api and ctypes on Windows
# cocoa api and pyobjc on Mac
# Xlib on linux


# Possible Future Features:
# get/click menu (win32: GetMenuItemCount, GetMenuItemInfo, GetMenuItemID, GetMenu, GetMenuItemRect)


__version__ = "0.0.8"

import sys, collections, pyrect


class PyGetWindowException(Exception):
    """
    Base class for exceptions raised when PyGetWindow functions
    encounter a problem. If PyGetWindow raises an exception that isn't
    this class, that indicates a bug in the module.
    """
    pass


def pointInRect(x, y, left, top, width, height):
    """Returns ``True`` if the ``(x, y)`` point is within the box described
    by ``(left, top, width, height)``."""
    return left < x < left + width and top < y < top + height


# NOTE: `Rect` is a named tuple for use in Python, while structs.RECT represents
# the win32 RECT struct. PyRect's Rect class is used for handling changing
# geometry of rectangular areas.
Rect = collections.namedtuple("Rect", "left top right bottom")
Point = collections.namedtuple("Point", "x y")
Size = collections.namedtuple("Size", "width height")


class BaseWindow:
    def __init__(self):
        pass

    def _setupRectProperties(self):
        def _onRead(attrName):
            r = self._getWindowRect()
            self._rect._left = r.left  # Setting _left directly to skip the onRead.
            self._rect._top = r.top  # Setting _top directly to skip the onRead.
            self._rect._width = r.right - r.left  # Setting _width directly to skip the onRead.
            self._rect._height = r.bottom - r.top  # Setting _height directly to skip the onRead.

        def _onChange(oldBox, newBox):
            self.moveTo(newBox.left, newBox.top)
            self.resizeTo(newBox.width, newBox.height)

        r = self._getWindowRect()
        self._rect = pyrect.Rect(r.left, r.top, r.right - r.left, r.bottom - r.top, onChange=_onChange, onRead=_onRead)

    def _getWindowRect(self):
        raise NotImplementedError

    def __str__(self):
        r = self._getWindowRect(self._hWnd)
        width = r.right - r.left
        height = r.bottom - r.top
        return '<%s left="%s", top="%s", width="%s", height="%s", title="%s">' % (
            self.__class__.__qualname__,
            r.left,
            r.top,
            width,
            height,
            self.title,
        )

    def close(self):
        """Closes this window. This may trigger "Are you sure you want to
        quit?" dialogs or other actions that prevent the window from
        actually closing. This is identical to clicking the X button on the
        window."""
        raise NotImplementedError

    def minimize(self):
        """Minimizes this window."""
        raise NotImplementedError

    def maximize(self):
        """Maximizes this window."""
        raise NotImplementedError

    def restore(self):
        """If maximized or minimized, restores the window to it's normal size."""
        raise NotImplementedError

    def activate(self):
        """Activate this window and make it the foreground window."""
        raise NotImplementedError

    def resizeRel(self, widthOffset, heightOffset):
        """Resizes the window relative to its current size."""
        raise NotImplementedError

    def resizeTo(self, newWidth, newHeight):
        """Resizes the window to a new width and height."""
        raise NotImplementedError

    def moveRel(self, xOffset, yOffset):
        """Moves the window relative to its current position."""
        raise NotImplementedError

    def moveTo(self, newLeft, newTop):
        """Moves the window to new coordinates on the screen."""
        raise NotImplementedError

    @property
    def isMinimized(self):
        """Returns True if the window is currently minimized."""
        raise NotImplementedError

    @property
    def isMaximized(self):
        """Returns True if the window is currently maximized."""
        raise NotImplementedError

    @property
    def isActive(self):
        """Returns True if the window is currently the active, foreground window."""
        raise NotImplementedError

    @property
    def title(self):
        """Returns the window title as a string."""
        raise NotImplementedError

    @property
    def visible(self):
        raise NotImplementedError

    # Wrappers for pyrect.Rect object's properties:
    @property
    def left(self):
        return self._rect.left

    @left.setter
    def left(self, value):
        # import pdb; pdb.set_trace()
        self._rect.left  # Run rect's onRead to update the Rect object.
        self._rect.left = value

    @property
    def right(self):
        return self._rect.right

    @right.setter
    def right(self, value):
        self._rect.right  # Run rect's onRead to update the Rect object.
        self._rect.right = value

    @property
    def top(self):
        return self._rect.top

    @top.setter
    def top(self, value):
        self._rect.top  # Run rect's onRead to update the Rect object.
        self._rect.top = value

    @property
    def bottom(self):
        return self._rect.bottom

    @bottom.setter
    def bottom(self, value):
        self._rect.bottom  # Run rect's onRead to update the Rect object.
        self._rect.bottom = value

    @property
    def topleft(self):
        return self._rect.topleft

    @topleft.setter
    def topleft(self, value):
        self._rect.topleft  # Run rect's onRead to update the Rect object.
        self._rect.topleft = value

    @property
    def topright(self):
        return self._rect.topright

    @topright.setter
    def topright(self, value):
        self._rect.topright  # Run rect's onRead to update the Rect object.
        self._rect.topright = value

    @property
    def bottomleft(self):
        return self._rect.bottomleft

    @bottomleft.setter
    def bottomleft(self, value):
        self._rect.bottomleft  # Run rect's onRead to update the Rect object.
        self._rect.bottomleft = value

    @property
    def bottomright(self):
        return self._rect.bottomright

    @bottomright.setter
    def bottomright(self, value):
        self._rect.bottomright  # Run rect's onRead to update the Rect object.
        self._rect.bottomright = value

    @property
    def midleft(self):
        return self._rect.midleft

    @midleft.setter
    def midleft(self, value):
        self._rect.midleft  # Run rect's onRead to update the Rect object.
        self._rect.midleft = value

    @property
    def midright(self):
        return self._rect.midright

    @midright.setter
    def midright(self, value):
        self._rect.midright  # Run rect's onRead to update the Rect object.
        self._rect.midright = value

    @property
    def midtop(self):
        return self._rect.midtop

    @midtop.setter
    def midtop(self, value):
        self._rect.midtop  # Run rect's onRead to update the Rect object.
        self._rect.midtop = value

    @property
    def midbottom(self):
        return self._rect.midbottom

    @midbottom.setter
    def midbottom(self, value):
        self._rect.midbottom  # Run rect's onRead to update the Rect object.
        self._rect.midbottom = value

    @property
    def center(self):
        return self._rect.center

    @center.setter
    def center(self, value):
        self._rect.center  # Run rect's onRead to update the Rect object.
        self._rect.center = value

    @property
    def centerx(self):
        return self._rect.centerx

    @centerx.setter
    def centerx(self, value):
        self._rect.centerx  # Run rect's onRead to update the Rect object.
        self._rect.centerx = value

    @property
    def centery(self):
        return self._rect.centery

    @centery.setter
    def centery(self, value):
        self._rect.centery  # Run rect's onRead to update the Rect object.
        self._rect.centery = value

    @property
    def width(self):
        return self._rect.width

    @width.setter
    def width(self, value):
        self._rect.width  # Run rect's onRead to update the Rect object.
        self._rect.width = value

    @property
    def height(self):
        return self._rect.height

    @height.setter
    def height(self, value):
        self._rect.height  # Run rect's onRead to update the Rect object.
        self._rect.height = value

    @property
    def size(self):
        return self._rect.size

    @size.setter
    def size(self, value):
        self._rect.size  # Run rect's onRead to update the Rect object.
        self._rect.size = value

    @property
    def area(self):
        return self._rect.area

    @area.setter
    def area(self, value):
        self._rect.area  # Run rect's onRead to update the Rect object.
        self._rect.area = value

    @property
    def box(self):
        return self._rect.box

    @box.setter
    def box(self, value):
        self._rect.box  # Run rect's onRead to update the Rect object.
        self._rect.box = value


if sys.platform == "darwin":
    # raise NotImplementedError('PyGetWindow currently does not support macOS. If you have Appkit/Cocoa knowledge, please contribute! https://github.com/asweigart/pygetwindow') # TODO - implement mac
    from ._pygetwindow_macos import *

    Window = MacOSWindow
elif sys.platform == "win32":
    from ._pygetwindow_win import (
        Win32Window,
        getActiveWindow,
        getActiveWindowTitle,
        getWindowsAt,
        getWindowsWithTitle,
        getAllWindows,
        getAllTitles,
    )

    Window = Win32Window
else:
    raise NotImplementedError(
        "PyGetWindow currently does not support Linux. If you have Xlib knowledge, please contribute! https://github.com/asweigart/pygetwindow"
    )