#include-once
#include <WinAPI.au3>
#include <Array.au3>
#include <GUIButton.au3>
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <GUIListView.au3>
#include <ListViewConstants.au3>
#include <GUIEdit.au3>
#include <WindowsConstants.au3>
#include <StaticConstants.au3>
Global $_user32 = DllOpen("user32.dll"), $_Block, $_OnEventMode, $_CurrentGUI, $_ScriptFullPath, $_ident, $_notifyFun, $_commandFun, $_hSci, $_stepMode, $_BreakFun, $_BreakLine, $_timer
Global $_LineFun[1], $_lnr, $_FunVars[1][2], $_FunVarsOrg[1][2], $_LastFun, $_Stack, $_PrevVar
Global $frmDBUG, $_txtCommand, $_btnRun, $_btnRunCursor, $_btnStep, $_btnStepOver, $_txtResult, $_txtBreakPoint, $_btnClear, $_ListView, $_lblStat, $_btnEdit, $_chkSetOnTop
$_ScriptFullPath = @ScriptFullPath
$iTimer = TimerInit()
$OldTime = 0
If $CmdLine[0] = 0 Then
	_CreateAndRun()
Else
	$_ScriptFullPath = $CmdLine[2] & '\' & $CmdLine[1]
EndIf
$Stahp = 'Started with ' & $CmdLine[0] & ' arguments.'
_Analyse($_ScriptFullPath) ;get variables for each function
$_stepMode = True ;default mode
Opt('WinTitleMatchMode', 1)
$_hSci = ControlGetHandle($_ScriptFullPath, "", "[CLASS:Scintilla;INSTANCE:1]") ;Scintilla handle
$frmDBUG = GUICreate("AutoIt3 Debugger [Heron, minx]", 681, 500, @DesktopWidth - 700, 40)
$_btnStep = GUICtrlCreateButton("Step", 0, 0, 55, 25)
$_btnStepOver = GUICtrlCreateButton("Step Over", 55, 0, 70, 25)
$_btnRunCursor = GUICtrlCreateButton("Run to Cursor", 125, 0, 80, 25)
$_btnRun = GUICtrlCreateButton("Run", 215, 0, 45, 25)
$btnBreak = GUICtrlCreateButton("Stop", 260, 0, 50, 25)
$btnExit = GUICtrlCreateButton("Exit all", 681 - 65, 0, 65, 25)
$_ListView = GUICtrlCreateListView("Var|Scope|Type|Value", 0, 25, 680, 150)
GUICtrlSendMsg(-1, $LVM_SETCOLUMNWIDTH, 0, 50)
GUICtrlSendMsg(-1, $LVM_SETCOLUMNWIDTH, 1, 50)
GUICtrlSendMsg(-1, $LVM_SETCOLUMNWIDTH, 2, 50)
GUICtrlSendMsg(-1, $LVM_SETCOLUMNWIDTH, 3, 50)
$Label1 = GUICtrlCreateLabel("Execute at Breakpoint: ", 5, 180, 115, 17)
$_txtCommand = GUICtrlCreateInput("", 120, 177, 510, 21)
$btnExecute = GUICtrlCreateButton("GO", 631, 177, 50, 21)
$_lblStat = GUICtrlCreateLabel("", 5, 205, 666, 17, $SS_SUNKEN)
$CPUlbl = GUICtrlCreateLabel($Stahp, 5, 225, 666, 17, $SS_SUNKEN)
$Clbl = GUICtrlCreateLabel("", 5, 245, 666, 17, $SS_SUNKEN)
$TimeLbl = GUICtrlCreateLabel("", 5, 265, 666, 17, $SS_SUNKEN)
$Result = GUICtrlCreateEdit("", 5, 285, 666, 200)
GUICtrlSetFont(-1, -1, -1, -1, "Courier")
$btnDump = GUICtrlCreateButton("Copy", 681 - 65 - 50, 0, 50, 25)
GUISetIcon("wmploc.DLL", 78)
GUISetState(@SW_SHOW)
WinSetOnTop("AutoIt3 Debugger", "", 1)
Func Dbug($lnr = @ScriptLineNumber, $case = -5, $exp = 0, $exp2 = 0) ;main function
	Local $Msg, $brk, $sel, $editActive, $in, $out, $var, $val, $hEdit, $items, $fx, $CurExpr, $max, $item, $scope, $vname, $breaknow
	Switch $case
		Case -9 ;loop
			While True
				Opt('GUIOnEventMode', 0)
				$_Block = True
				$Msg = GUIGetMsg()
				If ($OldTime + 250) <= TimerDiff($iTimer) Then
					$OldTime = TimerDiff($iTimer)
					GUICtrlSetData($TimeLbl, ms_to_time($OldTime))
				EndIf
				Switch $Msg
					Case $_btnRun, $_btnStep, $_btnRunCursor, $_btnStepOver ;handle debug command
						$sel = _SCISendMessage($_hSci, 2009) ;get anchor
						$sel = _SCISendMessage($_hSci, 2166, $sel) + 1 ;linefromposition
						$_stepMode = False ;set run/step mode things
						$_BreakFun = ''
						$_BreakLine = 0
						$_LineFun[0] = 0
						If $Msg = $_btnStep Then $_stepMode = True
						If $Msg = $_btnStepOver Then $_BreakFun = $_LineFun[$lnr]
						If $Msg = $_btnRunCursor Then $_BreakLine = $sel
						GUICtrlSetData($Clbl, 'Running...') ;set GUI things
						For $val = 1 To 20 ;disable buttons
							$text = ControlGetText($frmDBUG, "", "[CLASS:Button;INSTANCE:" & $val & "]")
							If $text <> "Stop" And $text <> "Exit all" Then ControlDisable($frmDBUG, "", "[CLASS:Button;INSTANCE:" & $val & "]")
						Next
						_SCISendMessage($_hSci, 2045, 3) ;delete markers
						Opt('GUIOnEventMode', $_OnEventMode) ;restore previous OnEventMode
						GUISwitch($_CurrentGUI) ;restore previous GUI
						$_Block = False ;release just before return
						Return
					Case $btnDump
						ClipPut(_GUICtrlListView_GetItemTextString($_ListView))
						GUICtrlSetData($CPUlbl, "AutoIt3 Debugger", "Selected variable dump was copied to clipboard!")
					Case $btnExecute ;execute expression
						$in = _StringEscape(GUICtrlRead($_txtCommand)) ;deal with escaping quotes
						If StringInStr($in, "=") > 0 Then ;do assignment
							$var = StringRegExp($in, "(.*?)=", 1)
							$val = StringRegExp($in, "=(.*)", 1)
							Return StringFormat('Execute(Dbug(%s, -3, Execute("_Set(%s, %s)"), @error))', $lnr, $var[0], $val[0])
						Else ;do expression
							Return StringFormat('Execute(Dbug(%s, -6, Execute("%s"), @error))', $lnr, $in)
						EndIf
					Case $btnExit
						DBGExit()
				EndSwitch ;$Msg
				If TimerDiff($_timer) > 250 Then ;update calling function display every 250ms
					$_timer = TimerInit()
					$var = _SCIGetCurWord()
					If $var <> $_PrevVar Then
						$_PrevVar = $var
						If StringLeft($var, 1) = "$" Then ;display variable
							Return StringFormat('Execute(Dbug(%s, -7, Execute("%s"), IsDeclared("%s")))', $lnr, $var, StringTrimLeft($var, 1))
						ElseIf StringLeft($var, 1) = "@" Then ;display macro
							Return StringFormat('Execute(Dbug(%s, -7, Execute("%s"), -2))', $lnr, $var)
						ElseIf StringRegExp($var, "^[-+x0-9a-fA-F]+$") Then ;display number
							$val = Number($var)
							If VarGetType($val) = "Int32" Then ToolTip(StringFormat("Int\t%s\r\nHex\t0x%s", Int($val), Hex($val, 4)))
							If VarGetType($val) = "Int64" Then ToolTip(StringFormat("Int\t%s\r\nHex\t0x%s", Int($val), Hex($val, 8)))
						Else
							ToolTip("")
						EndIf
					EndIf
				EndIf
			WEnd ;loop
		Case -7 ;dynamic variable display
			ToolTip(_Scope($exp2) & " " & _Type($exp) & ' = ' & _Value($exp, True))
			Return StringFormat('Execute(Dbug(%s, -9))', $lnr) ;do loop
		Case -6 ;get expression result
			$Msg = StringFormat('-@%s ', $exp2)
			$out = $Msg & _Type($exp) & ' = ' & _Value($exp, True)
			GUICtrlSetData($Result, "======= Return from Expression =======" & @CRLF & @CRLF & $out)
			If $exp = "" And $exp2 <> 0 Then GUICtrlSetData($Result, "Executed command return:" & @CRLF & "Error: " & $exp2)
			Return StringFormat('Execute(Dbug(%s,-1))', $lnr) ;do refresh
		Case -3 ;get assignment result
			$Msg = StringFormat('=@%s ', $exp2)
			$out = $Msg & _Type($exp) & ' = ' & _Value($exp, True)
			GUICtrlSetData($Result, "======= Return from Assignment =======" & @CRLF & @CRLF & $out)
			If $exp <> '' Or $exp2 = 0 Then Return StringFormat('Execute(Dbug(%s,-1))', $lnr) ;assignment succesfull -> do refresh
			$in = GUICtrlRead($_txtCommand)
			$in = _StringEscape($in) ;deal with escaping quotes
			Return StringFormat('Execute(Dbug(%s, -6, Execute("%s"), @error))', $lnr, $in) ;assignment failed -> do expression
		Case -5 ;set breakpoint check
			$_lnr = $lnr
			Static $LastFun
			If $_LineFun[$lnr] <> $LastFun Then
				$LastFun = $_LineFun[$lnr]
				GUICtrlSetData($Clbl, 'Debug(' & @ScriptLineNumber & ') : $_LineFun[$lnr] = ' & $_LineFun[$lnr] & "(" & $lnr & ")" & @CRLF & '>Error code: ' & @error) ;### Debug Console
			EndIf
			$_LineFun[0] += 1 ;counter
			If TimerDiff($_timer) > 250 Then ;update calling function display every 250ms
				GUICtrlSetData($_lblStat, StringFormat('Call #%s. Breakpoint calling was %s', $_LineFun[0], $lnr))
				$_timer = TimerInit()
				If BitAND(_GUICtrlButton_GetState($btnBreak), $BST_PUSHED) Then $breaknow = True
				If BitAND(_GUICtrlButton_GetState($btnExit), $BST_PUSHED) Then Exit
				$OldTime = TimerDiff($iTimer)
				GUICtrlSetData($TimeLbl, ms_to_time($OldTime))
			EndIf
			If $_Block Then Return ;no re-entry
			$val = _SCISendMessage($_hSci, 2046, $lnr - 1) ;markerget
			If $CmdLine[0] = 0 Or BitAND($val, 0x0002) = 2 Or $_stepMode Or $_LineFun[$lnr] = $_BreakFun Or $lnr = $_BreakLine Or $breaknow Then
				$brk = True ;(debug line mode) or (breakpoint) or (STEP) or (STEP OVER) or (RUN to CURSOR)
			Else
				$brk = GUICtrlRead($_txtBreakPoint) ;test conditional breakpoint
			EndIf
			Return 'Execute(Dbug(' & $lnr & ', -2, Execute("' & $brk & '"), 0))' ;do init
		Case -3 ;get assignment result
			$Msg = StringFormat('=@%s ', $exp2)
			$out = $Msg & _Type($exp) & ' = ' & _Value($exp, True)
			GUICtrlSetData($_txtResult, $out)
			If $exp <> '' Or $exp2 = 0 Then Return StringFormat('Execute(Dbug(%s,-1))', $lnr) ;assignment succesfull -> do refresh
			$in = GUICtrlRead($_txtCommand)
			$in = _StringEscape($in) ;deal with escaping quotes
			Return StringFormat('Execute(Dbug(%s, -6, Execute("%s"), @error))', $lnr, $in) ;assignment failed -> do expression
		Case -2 ;init
			If Not $exp Then Return ;no breakpoint
			$_Block = True ;prevent re-entry
			$_OnEventMode = Opt('GUIOnEventMode') ;save GUIOnEventMode
			$_CurrentGUI = GUISwitch($frmDBUG) ;save current GUI
			$_PrevVar = "" ;reset dynamic variable display
			_SCISendMessage($_hSci, 2024, $lnr - 1) ;gotoline
			_SCISendMessage($_hSci, 2045, 3) ;delete markers
			_SCISendMessage($_hSci, 2043, $lnr - 1, 3) ;add marker
			GUICtrlSetData($_lblStat, '') ;set GUI things
			For $val = 1 To 20 ;enable buttons
				ControlEnable($frmDBUG, "", "[CLASS:Button;INSTANCE:" & $val & "]")
			Next
			If Not WinActive($frmDBUG) Then WinActivate($frmDBUG)
			GUICtrlSetData($Clbl, StringFormat('Breakpoint %s', $lnr))
			PopulateListView($lnr)
			Return StringFormat('Execute(Dbug(%s, -1))', $lnr) ;do refresh
		Case -1 To 1000 ;read
			_GUICtrlListView_SetItemText($_ListView, $case, _Scope($exp2), 1)
			If $case >= 0 Then ;read expression
				If _GUICtrlListView_GetItemText($_ListView, $case) <> '' And $exp2 Then
					_GUICtrlListView_SetItemText($_ListView, $case, _Type($exp), 2)
					_GUICtrlListView_SetItemText($_ListView, $case, _Value($exp), 3)
				Else
					_GUICtrlListView_SetItemText($_ListView, $case, '', 2)
					_GUICtrlListView_SetItemText($_ListView, $case, '', 3)
				EndIf
			EndIf
			If $case >= _GUICtrlListView_GetItemCount($_ListView) Then ;all expressions read
				For $c = 0 To _GUICtrlListView_GetColumnCount($_ListView) - 1
					_GUICtrlListView_SetColumnWidth($_ListView, $c, $LVSCW_AUTOSIZE_USEHEADER)
				Next
				_GUICtrlListView_EndUpdate($_ListView)
				Return StringFormat('Execute(Dbug(%s, -9))', $lnr) ;do loop
			EndIf
			$case += 1
			$var = _GUICtrlListView_GetItemText($_ListView, $case)
			$var = StringReplace($var, '"', '''') ;escape "
			$scope = -2
			$vname = StringRegExp($var, '\$(\w{1,50})', 1)
			If IsArray($vname) Then $scope = StringFormat('IsDeclared("%s")', $vname[0])
			$exp = StringFormat('Execute("%s")', $var)
			Return 'Execute(Dbug(' & $lnr & ', ' & $case & ', ' & $exp & ', ' & $scope & '))' ;do read
	EndSwitch
EndFunc   ;==>Dbug
Func _Set(ByRef $x, $y) ;variable assignment
	$x = $y
	Return $x
EndFunc   ;==>_Set
Func _Analyse($script) ;scan source script and assign global arrays (functions/line and variables/function)
	Local $code, $aCode, $f, $line, $funcheader, $var
	Local $func = "Global"
	Local $gVars[1], $fVars[1]
	$code = FileRead($script)
	$aCode = StringSplit($code, @CRLF, 1)
	ReDim $_LineFun[$aCode[0] + 1]
	$f = 0
	For $l = 1 To $aCode[0]
		$line = StringRegExpReplace($aCode[$l], ";.*", "") ;strip comment from line
		$funcheader = StringRegExp($line, "(?i)^ *func +(\w+)", 1)
		If IsArray($funcheader) Then $func = $funcheader[0] ;get function header
		$_LineFun[$l] = $func
		If StringRegExp($line, "(?i)^\s*EndFunc") Then ;function end
			$_FunVars[$f][0] = $func
			$_FunVars[$f][1] = _ArrayToArgs($fVars)
			$f += 1
			ReDim $_FunVars[$f + 1][2] ;save function variables
			$func = "Global"
			Local $fVars[1]
		EndIf
		$var = StringRegExp($line, "(?i)([\$\@]\w{1,50})", 3) ;get variables
		If IsArray($var) Then
			If $func = "Global" Then
				_ArrayConcatenate($gVars, $var)
			Else
				_ArrayConcatenate($fVars, $var)
			EndIf
		EndIf
	Next
	$_FunVars[$f][0] = "Global" ;save global variables
	$_FunVars[$f][1] = _ArrayToArgs($gVars)
	$_FunVarsOrg = $_FunVars
EndFunc   ;==>_Analyse

Func _ArrayToArgs(ByRef $arr) ;make $arr unique and add dummy parameters ($0) if nessecary
	If Not IsArray($arr) Then Dim $arr[20]
	_ArrayDelete($arr, 0)
	$arr = _ArrayUnique($arr)
	_ArrayDelete($arr, 0)
	Return _ArrayToString($arr, ",")
EndFunc   ;==>_ArrayToArgs
Func _Scope($res) ;return scope indication
	Switch $res
		Case -1
			Return "Local" ;local
		Case 0
			Return "Not defined" ;not defined
		Case 1
			Return "Global" ;global
		Case -2
			Return "Fixed" ;fixed (macro or literal)
	EndSwitch
EndFunc   ;==>_Scope
Func _Type($var) ;return type of $var
	Local $type, $size
	$type = VarGetType($var)
	Switch $type
		Case 'String'
			$type &= '[' & StringLen($var) & ']'
		Case 'Binary'
			$type &= '[' & BinaryLen($var) & ']'
		Case 'Array'
			For $i = 1 To UBound($var, 0)
				$size &= "[" & UBound($var, $i) & "]"
			Next
			$type &= $size
		Case 'DllStruct'
			$type &= '[' & DllStructGetSize($var) & ']'
		Case 'Ptr'
			If IsHWnd($var) Then $type = 'hWnd'
	EndSwitch
	Return $type
EndFunc   ;==>_Type
Func _Value($var, $ext = False) ;return value of $var ($ext=true gives extended information)
	Local $val, $res
	If $ext And (IsArray($var) Or IsDllStruct($var) Or IsObj($var)) Then $_ident &= '    '
	Select
		Case IsString($var)
			$val = "'" & $var & "'"
		Case IsArray($var)
			If Not $ext Then Return $var
			_DispArr($var, $val)
		Case IsDllStruct($var)
			$val = '*' & DllStructGetPtr($var)
			If Not $ext Then Return $val
			For $e = 1 To 200 ;max elements, should be enough
				$res = DllStructGetData($var, $e)
				If @error Then ExitLoop
				$val &= @CRLF & StringTrimLeft($_ident, 4) & '[' & $e & '] '
				If IsString($res) Or IsBinary($res) Then
					$val &= _Type($res) & @TAB & _Value($res)
				Else
					For $i = 1 To 1000 ;max indexes, should be enough
						$res = DllStructGetData($var, $e, $i)
						If @error Then
							ExitLoop
						ElseIf $i = 1 Then
							$val &= _Type($res) & ' = ' & @TAB & _Value($res)
						Else
							$val &= ', ' & _Value($res)
						EndIf
					Next
				EndIf
			Next
		Case IsObj($var)
			If ObjName($var, 1) Then $val &= ObjName($var, 1)
			If Not $ext Then Return $val
			If ObjName($var, 2) Then $val &= @CRLF & StringTrimLeft($_ident, 4) & "Desc: " & ObjName($var, 2)
			If ObjName($var, 3) Then $val &= @CRLF & StringTrimLeft($_ident, 4) & "ID  : " & ObjName($var, 3)
			If ObjName($var, 4) Then $val &= @CRLF & StringTrimLeft($_ident, 4) & "DLL : " & ObjName($var, 4)
			If ObjName($var, 5) Then $val &= @CRLF & StringTrimLeft($_ident, 4) & "Icon: " & ObjName($var, 5)
		Case IsHWnd($var) Or IsPtr($var)
			$val = '*' & $var
		Case Else
			$val = $var
	EndSelect
	If $ext And (IsArray($var) Or IsDllStruct($var) Or IsObj($var)) Then $_ident = StringTrimLeft($_ident, 4)
	Return $val
EndFunc   ;==>_Value

Func _DispArr(ByRef $ar, ByRef $out, $d = 0, $cnt = 0) ;display values of n-dimension array
	Local $res
	If $cnt = 0 Then Dim $cnt[UBound($ar, 0)]
	For $i = 0 To UBound($ar, $d + 1)
		If $i = UBound($ar, $d + 1) Then Return $d - 1
		$cnt[$d] = $i
		If $d < UBound($ar, 0) - 1 Then $d = _DispArr($ar, $out, $d + 1, $cnt) ;recursive call
		If $d = UBound($ar, 0) - 1 Then
			$res = Execute("$ar[" & _ArrayToString($cnt, "][") & "]")
			$out &= @CRLF & StringTrimLeft($_ident, 4) & "[" & _ArrayToString($cnt, "][") & "] " & _Type($res) & ' = ' & @TAB & _Value($res, True)
		EndIf
	Next
EndFunc   ;==>_DispArr

Func _SCISendMessage($h_hWnd, $i_msg, $wParam = 0, $lParam = 0, $s_t1 = "int", $s_t2 = "int") ;function and idea stolen from Martin
	Local $ret
	$ret = DllCall($_user32, "long", "SendMessageA", "long", $h_hWnd, "int", $i_msg, $s_t1, $wParam, $s_t2, $lParam)
	Return $ret[0]
EndFunc   ;==>_SCISendMessage

Func _SCIGetCurWord() ;get current word under cursor from Scite
	Local $pos[2], $line, $sta, $end, $text
	Local $tpoint ;= DllStructCreate("int X;int Y")
	$tpoint = _WinAPI_GetMousePos()
	If _WinAPI_WindowFromPoint($tpoint) <> $_hSci Then Return ""
	$tpoint = _WinAPI_GetMousePos(True, $_hSci)
	$pos[0] = DllStructGetData($tpoint, "X")
	$pos[1] = DllStructGetData($tpoint, "Y")
	$pos = _SCISendMessage($_hSci, 2022, $pos[0], $pos[1])
	$line = _SCISendMessage($_hSci, 2166, $pos)
	$sta = _SCISendMessage($_hSci, 2167, $line)
	$end = _SCISendMessage($_hSci, 2136, $line)
	$text = ""
	For $c = $pos To $sta Step -1
		$char = Chr(_SCISendMessage($_hSci, 2007, $c, 0))
		If Not StringRegExp($char, "[-@$_a-zA-Z0-9\[\]]") Then ExitLoop
		$text = $char & $text
	Next
	For $c = $pos + 1 To $end
		$char = Chr(_SCISendMessage($_hSci, 2007, $c, 0))
		If Not StringRegExp($char, "[-@$_a-zA-Z0-9\[\]]") Then ExitLoop
		$text &= $char
	Next
	Return $text
EndFunc   ;==>_SCIGetCurWord

Func SaveListItems($lnr) ;save expression list for current line
	$items = ''
	For $i = 0 To _GUICtrlListView_GetItemCount($_ListView) - 1
		$items &= _GUICtrlListView_GetItemText($_ListView, $i) & ","
	Next
	$fx = _ArraySearch($_FunVars, $_LineFun[$lnr])
	$_FunVars[$fx][1] = StringTrimRight($items, 1)
EndFunc   ;==>SaveListItems

Func PopulateListView($lnr) ;populate listview
	Local $fx, $CurExpr, $i
	_GUICtrlListView_BeginUpdate($_ListView)
	$fx = _ArraySearch($_FunVars, $_LineFun[$lnr])
	$CurExpr = StringSplit($_FunVars[$fx][1], ',', 2)
	_GUICtrlListView_DeleteAllItems(GUICtrlGetHandle($_ListView))
	For $i = 0 To UBound($CurExpr) - 1
		_GUICtrlListView_AddItem($_ListView, $CurExpr[$i])
	Next
EndFunc   ;==>PopulateListView

Func _StringEscape($in) ;try to get the quotes right
	Local $res
	$res = StringRegExp($in, '"|''', 2)
	If IsArray($res) Then
		If $res[0] = '"' Then
			$in = StringReplace($in, "'", "''")
			$in = StringReplace($in, '"', "'")
		Else
			$in = StringReplace($in, '"', "''")
		EndIf
	EndIf
	Return $in
EndFunc   ;==>_StringEscape

Func _NotifyHook($hWnd, $Msg, $wParam, $lParam) ;check if WM_NOTIFY not from dbug GUI
	Local $func
	GUICtrlSetData($Clbl, '@@ ._NotifyHook(' & @ScriptLineNumber & ') : $hWnd = ' & $hWnd & @CRLF & '>Error code: ' & @error) ;### Debug Console
	If $hWnd = $frmDBUG Then Return $GUI_RUNDEFMSG ;notify from Dbug -> ignore
	$func = $CmdLine[3]
	Call($func, $hWnd, $Msg, $wParam, $lParam) ;re-post notify message
	If @error <> 0xDEAD Or @extended <> 0xBEEF Then Return
	GUICtrlSetData($Clbl, '@@ ._NotifyHook(' & @ScriptLineNumber & ') : Function: ' & $func & ' not found or wrong number of parameters') ;### Debug Console
EndFunc   ;==>_NotifyHook

Func _CommandHook($hWnd, $Msg, $wParam, $lParam) ;check if WM_COMMAND not from dbug GUI
	Local $func
	GUICtrlSetData($Clbl, '@@ ._CommandHook(' & @ScriptLineNumber & ') : $hWnd = ' & $hWnd & @CRLF & '>Error code: ' & @error) ;### Debug Console
	If $hWnd = $frmDBUG Then Return $GUI_RUNDEFMSG ;notify from Dbug -> ignore
	$func = $CmdLine[4]
	Call($func, $hWnd, $Msg, $wParam, $lParam) ;re-post notify message
	If @error <> 0xDEAD Or @extended <> 0xBEEF Then Return
	GUICtrlSetData($Clbl, '@@ ._CommandHook(' & @ScriptLineNumber & ') : Function: ' & $func & ' not found or wrong number of parameters') ;### Debug Console
EndFunc   ;==>_CommandHook


Func _CreateAndRun() ;generate ._tmp.au3
	Local $lnr, $line, $res, $continued, $SKIPDBUG, $SKIPCASE, $fHnd, $fOut, $AutoItDir, $run
	Local $REG_WM_NOTIFY = '(?i)^\s*GUIRegisterMsG\(.*\$WM_NOTIFY.*,.*?(\w+)' ; GUIRegisterMsg($WM_NOTIFY,( "function"  ))
	Local $REG_WM_COMMAND = '(?i)^\s*GUIRegisterMsG\(.*\$WM_COMMAND.*,.*?(\w+)' ; GUIRegisterMsg($WM_COMMAND,( "function"  ))
	$fHnd = FileOpen(@ScriptFullPath, 0) ;open source script
	$fOut = FileOpen(@ScriptDir & '\._tmp.au3', 2) ;open shadow script
	While True
		$line = FileReadLine($fHnd)
		$lnr += 1
		If @error = -1 Then ExitLoop
		If StringInStr($line, 'Debugger_Stop') Then $SKIPDBUG = True ;exclude debugging
		If StringInStr($line, 'Debugger_Start') Then $SKIPDBUG = False
		$res = StringRegExpReplace($line, '\s*;.*', '') ;strip comment and whitespace
		If StringRegExp($res, '(?i)Execute\(.*dbug\(.*\).*\)') Then Return ;Execute(dbug()) statement found -> debug line mode
		If StringRegExp($res, '(?i)^\s*select\s*$|^\s*switch ') Then $SKIPCASE = True ;no statements allowed between 'Select|Switch' and 'Case'
		If $res = '' Or StringRegExp($res, '^\s*#') Or $continued Or StringInStr($line, '@error') Or $SKIPDBUG Or $SKIPCASE Then ;empty, include, line-continued, @error, exclude or Case
		Else
			FileWriteLine($fOut, 'Execute(Dbug(' & $lnr & '))') ;write debug line
		EndIf
		If StringRegExp($res, '(?i)case ') Then $SKIPCASE = False ;statement allowed after Case
		$continued = StringRegExp($res, '.* _$') ;line is contineud
		$res = StringRegExp($line, $REG_WM_NOTIFY, 1)
		If IsArray($res) Then ;if WM_NOTIFY messages are registered, replace by NotifyHook and save original function name
			$_notifyFun = $res[0]
			$line = 'GUIRegisterMsg($WM_NOTIFY, "_NotifyHook")'
		EndIf
		$res = StringRegExp($line, $REG_WM_COMMAND, 1)
		If IsArray($res) Then ;if WM_COMMAND messages are registered, replace by CommandHook and save original function name
			$_commandFun = $res[0]
			$line = 'GUIRegisterMsg($WM_COMMAND, "_CommandHook")'
		EndIf
		FileWriteLine($fOut, $line) ;write original line
	WEnd
	FileClose($fHnd)
	FileClose($fOut)
	$AutoItDir = RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\AutoIt v3\AutoIt", "InstallDir")
	$run = StringFormat('%s\\AutoIt3.exe ._tmp.au3 "%s" "%s" "%s" "%s"', $AutoItDir, @ScriptName, @ScriptDir, $_notifyFun, $_commandFun)
	$res = Run($run, @ScriptDir, Default, 0x4) ;run the shadow script (with scriptname and -directory passed as arguments)
	Exit
EndFunc   ;==>_CreateAndRun

Func DBGExit() ;seen enough now
	_SCISendMessage($_hSci, 2045, 3) ;delete markers
	FileDelete("._tmp.au3")
	Exit
EndFunc   ;==>DBGExit

Func ms_to_time($ms)
	$Seconds = Round($ms / 1000)
	$Mins = Int($Seconds / 60)
	$Seconds -= $Mins * 60
	$Seconds = Round($Seconds)
	If StringLen(String($Seconds)) = 1 Then $Seconds = "0" & $Seconds
	If StringLen(String($Mins)) = 1 Then $Mins = "0" & $Mins
	Return $Mins & ":" & $Seconds
EndFunc   ;==>ms_to_time

Execute(dbug(1)) ;break at line 1, outcomment if it annoys you
