'''
https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-bitblt
https://www.bugs.python.org/issue33656
'''

from os import getpid
from os.path import join
from ctypes import windll, c_int, addressof

from win32gui import GetDesktopWindow, GetWindowDC
from win32api import GetSystemMetrics
from win32ui import CreateDCFromHandle, CreateBitmap
from win32con import (
    SM_CXVIRTUALSCREEN,
    SM_CYVIRTUALSCREEN,
    SM_XVIRTUALSCREEN,
    SM_YVIRTUALSCREEN,
    SRCCOPY
)

from plyer.facades import Screenshot
from plyer.platforms.win.storagepath import WinStoragePath


class WinScreenshot(Screenshot):
    def __init__(self, file_path=None):
        default_path = join(
            WinStoragePath().get_pictures_dir(), 'screenshot.bmp'
        )
        super().__init__(file_path or default_path)

    def _set_dpi_aware(self, value):
        try:
            windll.shcore.SetProcessDpiAwareness(value)
        except (AttributeError, OSError):
            print('Could not set DPI awareness.')

    def _dpi_aware(self):
        # make backup of DPI awareness value in case a user does not want
        # to use it, otherwise we'll cripple user's runtime
        try:
            process_handle = windll.kernel32.OpenProcess(
                0,      # no permissions
                False,  # bInheritHandle
                getpid()
            )
            aware = c_int()
            windll.shcore.GetProcessDpiAwareness(
                process_handle,
                addressof(aware)
            )
        finally:
            # always close the handle!
            windll.kernel32.CloseHandle(process_handle)
        return bool(aware)

    def _capture(self):
        # make sure the process is DPI aware, otherwise the image
        # is only a part of the full monitor content
        # (necessary only on Win 8.1+)
        old_awareness = self._dpi_aware()
        self._set_dpi_aware(True)

        # get width and height of current monitor
        width = GetSystemMetrics(SM_CXVIRTUALSCREEN)
        height = GetSystemMetrics(SM_CYVIRTUALSCREEN)

        # get 'desktop' window handle
        handle_desktop = GetDesktopWindow()

        # get window graphic context handle
        handle_context = GetWindowDC(handle_desktop)

        # create device context
        dev_ctx = CreateDCFromHandle(handle_context)

        # create destination for original device context
        dest_ctx = dev_ctx.CreateCompatibleDC()

        # create bitmap compatible with desktop window device
        bmp = CreateBitmap()
        bmp.CreateCompatibleBitmap(dev_ctx, width, height)

        # select bitmap into destination device
        dest_ctx.SelectObject(bmp)

        # populate selected bitmap in destination device
        dest_ctx.BitBlt(
            (0, 0),           # start point (x, y)
            (width, height),  # size of rectangle
            dev_ctx,          # source device
            (
                GetSystemMetrics(SM_XVIRTUALSCREEN),
                GetSystemMetrics(SM_YVIRTUALSCREEN)
            ),                # source rectangle, can be different monitor
            SRCCOPY           # copy directly without filters
        )

        # save bitmap to file
        bmp.SaveBitmapFile(dest_ctx, self.file_path)

        # return to the original state
        self._set_dpi_aware(old_awareness)


def instance():
    return WinScreenshot()