Imports System
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Imports DigitalData.Modules.Base.NativeMethods

Public Class ScreenEx
    Public Const DEFAULT_WINDOW_HEIGHT = 480
    Public Const DEFAULT_WINDOW_WIDTH = 640

    Friend Const MONITORINFOF_PRIMARY As Integer = &H1
    Friend Const MONITOR_DEFAULTTONEAREST As Integer = &H2
    Friend Const MONITOR_DEFAULTTONULL As Integer = &H0
    Friend Const MONITOR_DEFAULTTOPRIMARY As Integer = &H1

    Friend Enum Monitor_DPI_Type As Integer
        MDT_Effective_DPI = 0
        MDT_Angular_DPI = 1
        MDT_Raw_DPI = 2
        MDT_Default = MDT_Effective_DPI
    End Enum

    Private Enum DeviceCap
        VERTRES = 10
        DESKTOPVERTRES = 117
    End Enum

    Public Shared Function GetLocationWithinScreen(pLocation As Point) As Point?
        For Each screen As Screen In Screen.AllScreens
            If screen.Bounds.Contains(pLocation) Then
                Return New Point(pLocation.X - screen.Bounds.Left, pLocation.Y - screen.Bounds.Top)
            End If
        Next

        Return Nothing
    End Function

    Public Shared Sub RestoreFormPosition(pForm As Form, pPosition As Point)
        Dim oLocationWithinScreen As Point? = GetLocationWithinScreen(pPosition)

        If oLocationWithinScreen Is Nothing Then
            Dim oPrimaryScreen = Screen.PrimaryScreen
            pForm.StartPosition = FormStartPosition.CenterScreen
        Else
            pForm.StartPosition = FormStartPosition.Manual
            pForm.Location = pPosition
        End If
    End Sub

    Public Shared Sub RestoreFormState(pForm As Form, pFormState As FormWindowState)
        If pFormState = FormWindowState.Maximized Then
            pForm.WindowState = FormWindowState.Normal
            pForm.WindowState = FormWindowState.Maximized
        ElseIf pFormState = FormWindowState.Minimized Then
            pForm.WindowState = FormWindowState.Normal
            pForm.WindowState = FormWindowState.Minimized
        Else
            pForm.WindowState = FormWindowState.Normal
        End If
    End Sub

    Public Shared Sub RestoreFormState(pForm As Form, pFormState As String)
        Dim oFormState As FormWindowState
        If Not [Enum].TryParse(pFormState, oFormState) Then
            oFormState = FormWindowState.Normal
        End If

        RestoreFormState(pForm, oFormState)
    End Sub

    Public Shared Sub RestoreFormSize(pForm As Form, pFormSize As Size)
        Dim oFormSize As Size
        If pFormSize.Height < 1 Or pFormSize.Width < 1 Or pFormSize.IsEmpty Then
            oFormSize = New Size(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)
        Else
            oFormSize = pFormSize
        End If

        pForm.Size = oFormSize
    End Sub

    ''' <summary>
    ''' Checks if a point is Visible on any screen
    ''' </summary>
    Public Shared Function IsVisibleOnAnyScreen(Location As Point) As Boolean
        Try
            Dim oRect As New Rectangle(Location, New Size(0, 0))

            For Each oScreen In Screen.AllScreens
                If oScreen.WorkingArea.IntersectsWith(oRect) Then
                    Return True
                End If
            Next

            Return False
        Catch ex As Exception
            Return False
        End Try
    End Function

    ''' <summary>
    ''' Checks if Size is not negative
    ''' </summary>
    Public Shared Function SizeIsVisible(Size As Size) As Boolean
        If Size.Width >= 0 And Size.Height >= 0 Then
            Return True
        End If

        Return False
    End Function

    ''' <summary>
    ''' Checks if Location is not negative
    ''' </summary>
    Public Shared Function LocationIsVisible(Location As Point) As Boolean
        If Location.X >= 0 And Location.Y >= 0 Then
            Return True
        End If

        Return False
    End Function

    Public Function GetScreenScaling(Form As Form) As Single
        Dim oHandle As IntPtr = Form.Handle
        Dim oFactor1, oFactor2 As Single

        oFactor1 = GetFactorFromDeviceCaps(oHandle)
        oFactor2 = GetDPIFromMonitor(oHandle)

        If oFactor1 > 1 Then
            Return oFactor1
        Else
            Return oFactor2
        End If
    End Function

    Private Function GetFactorFromDeviceCaps(Handle As IntPtr) As Single
        Dim g As Graphics = Graphics.FromHwnd(Handle)
        Dim desktop As IntPtr = g.GetHdc()
        Dim LogicalScreenHeight As Integer = GetDeviceCaps(desktop, DeviceCap.VERTRES)
        Dim PhysicalScreenHeight As Integer = GetDeviceCaps(desktop, DeviceCap.DESKTOPVERTRES)
        Dim oScreenScalingFactor As Single = CSng(PhysicalScreenHeight) / CSng(LogicalScreenHeight)
        Return oScreenScalingFactor
    End Function

    Private Function GetDPIFromMonitor(Handle As IntPtr) As Single
        'Get handle to monitor that contains this window.
        Dim monitorHandle As IntPtr = MonitorFromWindow(Handle, MONITOR_DEFAULTTONEAREST)

        'Get DPI (If the OS is not Windows 8.1 or newer, calling GetDpiForMonitor will cause exception).
        Dim dpiX As UInteger
        Dim dpiY As UInteger

        Dim result As Integer = GetDpiForMonitor(monitorHandle, Monitor_DPI_Type.MDT_Default, dpiX, dpiY)


        If (result = 0) Then 'If S_OK (= 0)
            Return dpiX / 96.0F
        Else
            Return -1
        End If
    End Function
End Class
