#include <Array.au3>
#include <WinAPI.au3>
#include <Constants.au3>
#include <WindowsConstants.au3>
#include <WinAPIGdi.au3>
#include <WinAPISys.au3>
#include <SendMessage.au3>
#include <Misc.au3>
#include "includes\BlockInputEx.au3"

#include "includes\_Monitor.au3"
#include "includes\_Subarray.au3"

Opt("WinTitleMatchMode", 4)
Global Const $SC_DRAGMOVE = 0xF012

_hideTaskbar()

$visibleWindows = _getVisibleWindowList()
$tilableWindows = _createTilableWindowList($visibleWindows)

For $i = 0 To UBound($tilableWindows) - 1
	ConsoleWrite(WinGetTitle($tilableWindows[$i]) & " ==> " & _getContainingMonitor($tilableWindows[$i]) & @CRLF)
Next

Local $hControl, $hWin, $hOldWin, $aMousePos
$hOldWin = ""


While Not _IsPressed("1B")
	$aMousePos = MouseGetPos()

	If _IsPressed("5B") And _IsPressed("02") Then
		$hControl = _WindowFromPoint($aMousePos[0],$aMousePos[1])
		; Since _WindowFromPoint() can return 'sub' windows, or control handles, we should seek the owner window
		$hWin = _WinAPI_GetAncestor($hControl, 2)
		$pos = WinGetPos($hWin)
		MouseMove($pos[0] + $pos[2], $pos[1] + $pos[3], 0)
		While _IsPressed("5B") And _IsPressed("02")
			_BlockInputEx ( 1, "", "0x5B|0x5C|{UP}" )
			$mousePos = MouseGetPos()
			WinMove($hWin, "", $pos[0], $pos[1], $mousePos[0] - $pos[0], $mousePos[1] - $pos[1])
		WEnd
		While _IsPressed("5B") Or _IsPressed("02")
			ToolTip("Ater idle loop")
			_BlockInputEx ( 0, "", "0x5B|0x5C|{UP}" )
		WEnd
		_BlockInputEx ( 0, "", "0x5B|0x5C|{UP}" )
	EndIf


	Sleep(1)
WEnd


_showTaskbar()

Func _windowFromPoint($iX,$iY)
    Local $stInt64,$aRet,$stPoint=DllStructCreate("long;long")
    DllStructSetData($stPoint,1,$iX)
    DllStructSetData($stPoint,2,$iY)
    $stInt64=DllStructCreate("int64",DllStructGetPtr($stPoint))
    $aRet=DllCall("user32.dll","hwnd","WindowFromPoint","int64",DllStructGetData($stInt64,1))
    If @error Then Return SetError(2,@error,0)
    If $aRet[0]=0 Then Return SetError(3,0,0)
    Return $aRet[0]
EndFunc

; Simple function to hide the windows taskbar
Func _hideTaskbar()
	WinSetState("[CLASS:Button; TITLE:Start]", "", @SW_HIDE)
	WinSetState("[CLASS:Shell_TrayWnd]", "", @SW_HIDE)
EndFunc

; This just reverts _hideTaskbar()
Func _showTaskbar()
	WinSetState("[CLASS:Button; TITLE:Start]", "", @SW_SHOW)
	WinSetState("[CLASS:Shell_TrayWnd]", "", @SW_SHOW)
EndFunc

Func _toggleBorder($hwnd)
	$iOldStyle = _WinAPI_GetWindowLong($hwnd, $GWL_STYLE)
	$iNewStyle = BitXOR($iOldStyle, $WS_BORDER)
	_WinAPI_SetWindowLong($hwnd, $GWL_STYLE, $iNewStyle)
	_WinAPI_SetWindowPos($hwnd, 0, 0, 0, 0, 0, BitOR($SWP_FRAMECHANGED, $SWP_NOMOVE, $SWP_NOSIZE))
EndFunc

; Returns an array of visible windows.
; All windows, which have the "Window is visible" bit set, count as visible
Func _getVisibleWindowList()
	Local $visibleList[0]
	$plist = ProcessList()
	For $i = 1 To $plist[0][0]
		$hwndList = _ProcessGetWindow($plist[$i][1])
		If IsArray($hwndList) Then
			For $a = 1 To $hwndList[0]
				$hwnd = $hwndList[$a]
				$applState = WinGetState($hwnd)
				$title = WinGetTitle($hwnd)
				If (BitOR($applState, 2) == $applState) And $title <> "" Then
					_ArrayAdd($visibleList, $hwnd)
				EndIf
			Next
		EndIf
	Next

	Return $visibleList
EndFunc

; Returns an array with window styles
; $array[0] = Window styles
; $array[1] = Extended window styles
Func _getStyleOfWindow($hwnd)
	Local $style[0]
	$aStyle = _WinAPI_GetWindowLong($hwnd, $GWL_STYLE)
	If @error == 0 Then
		_ArrayAdd($style, $aStyle)
	Else
		_ArrayAdd($style, -1)
	EndIf
	$aExStyle = _WinAPI_GetWindowLong($hwnd, $GWL_EXSTYLE)
	If @error == 0 Then
		_ArrayAdd($style, $aExStyle)
	Else
		_ArrayAdd($style, -1)
	EndIf
	Return $style
EndFunc

; Iterates over an array of hwnds and returns an array of tilable window hwnds
Func _createTilableWindowList($windowList)
	Local $tilableWindowList[0]
	For $i = 0 To UBound($windowList) - 1
		If _isTilable($windowList[$i]) Then
			_ArrayAdd($tilableWindowList, $windowList[$i])
		EndIf
	Next
	Return $tilableWindowList
EndFunc

; I assume, that a window should be tiles, if it has a thick frame
Func _isTilable($hwnd)
	$styles = _getStyleOfWindow($hwnd)
	If BitAND($styles[1], $WS_EX_DLGMODALFRAME) <> $WS_EX_DLGMODALFRAME Then
		Return True
	Else
		Return False
	EndIf
EndFunc

; #FUNCTION# ============================================================================================================================
; Name...........: _ProcessGetWindow
;
; Description ...: Returns an array of HWNDs containing all windows owned by the process $p_PID, or optionally a single "best guess."
;
; Syntax.........: _ProcessGetWindow( $p_PID [, $p_ReturnBestGuess = False ])
;
; Parameters ....: $p_PID - The PID of the process you want the Window for.
;                  $p_ReturnBestGuess - If True, function will return only 1 reult on a best-guess basis.
;                                           The "Best Guess" is the VISIBLE window owned by $p_PID with the longest title.
;
; Return values .: Success      - Return $_array containing HWND info.
;                                       $_array[0] = Number of results
;                                       $_array[n] = HWND of Window n
;
;                  Failure      - Returns 0
;
;				   Error		- Returns -1 and sets @error
;                                            1 - Requires a non-zero number.
;                                            2 - Process does not exist
;                                            3 - WinList() Error
;
; Author ........: Andrew Bobulsky, contact: RulerOf <at that public email service provided by Google>.
; Remarks .......: The reverse of WinGetProcess()
; =======================================================================================================================================

Func _ProcessGetWindow( $p_PID, $p_ReturnBestGuess = False )
    Local $p_ReturnVal[1] = [0]
    Local $p_WinList = WinList()

    If @error Then ;Some Error handling
        SetError(3)
        Return -1
    EndIf

    If $p_PID = 0 Then ;Some Error handling
        SetError(1)
        Return -1
    EndIf

    If ProcessExists($p_PID) = 0 Then ;Some Error handling
        ConsoleWrite("_ProcessGetWindow: Process " & $p_PID & " doesn't exist!" & @CRLF)
        SetError(2)
        Return -1
    EndIf

    For $i = 1 To $p_WinList[0][0] Step 1
        Local $w_PID = WinGetProcess($p_WinList[$i][1])
        If $w_PID = $p_PID Then
            $p_ReturnVal[0] += 1
            _ArrayAdd($p_ReturnVal, $p_WinList[$i][1])
        EndIf
    Next

    If $p_ReturnVal[0] > 1 Then
        If $p_ReturnBestGuess Then
            Do
                Local $i_State = WinGetState($p_ReturnVal[2])
                Local $i_StateLongest = WinGetState($p_ReturnVal[1])
                Select
                    Case BitAND($i_State, 2) And BitAND($i_StateLongest, 2) ;If they're both visible
                        If StringLen(WinGetTitle($p_ReturnVal[2])) > StringLen(WinGetTitle($p_ReturnVal[1])) Then ;And the new one has a longer title
                            _ArrayDelete($p_ReturnVal, 1) ;Delete the "loser"
                            $p_ReturnVal[0] -= 1 ;Decrement counter
                        Else
                            _ArrayDelete($p_ReturnVal, 2) ;Delete the failed challenger
                            $p_ReturnVal[0] -= 1
                        EndIf
                    Case BitAND($i_State, 2) And Not BitAND($i_StateLongest, 2) ;If the new one's visible and the old one isn't
                        _ArrayDelete($p_ReturnVal, 1) ;Delete the old one
                        $p_ReturnVal[0] -= 1 ;Decrement counter
                    Case Else ;Neither window is visible, let's just keep the first one.
                        _ArrayDelete($p_ReturnVal, 2)
                        $p_ReturnVal[0] -= 1
                EndSelect
            Until $p_ReturnVal[0] = 1
        EndIf
        Return $p_ReturnVal
    ElseIf $p_ReturnVal[0] = 1 Then
		Return $p_ReturnVal ;Only 1 window.
	Else
		Return 0 ;Window not found.
	EndIf
EndFunc