#include <GUIConstantsEx.au3>
#include <ComboConstants.au3>
#include <EditConstants.au3>
#include <MenuConstants.au3>
#include <WindowsConstants.au3>

Opt("MustDeclareVars", 1)

Global Const $GuiTitle = "Function Viewer"
Global Const $SelectAll = "All"
Global Const $ShowScript = " >>> Script <<<"
Global Const $UserInclKey = "HKEY_CURRENT_USER\Software\AutoIt v3\AutoIt"
Global Const $UserInclVal = "Include"
Global Const $Delimiter = " > "
Global Const $GuiW = 190 * 4
Global Const $GuiH = 190 * 3
Global Const $MinW = 600
Global Const $MinH = 400
Global Const $FSize = 10

Global $aInclDirs[10] = [0]
Global $StdDir
Global $ScriptContent = ""
Global $CurrentFolder = $SelectAll
Global $aRet = DllCall("User32.dll", "Int", "GetSystemMetrics", "Int", 15) ; SM_CYMENU = 15
Global $MH = $aRet[0]

; Standardincludeverzeichnis ermitteln
; Raupi -> http://www.autoit.de/index.php?page=Thread&postID=212577#post212577
If @OSArch = "x86" Then
    $StdDir = RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\AutoIt v3\AutoIt", "InstallDir") & "\Include"
Else
    $StdDir = RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\AutoIt v3\AutoIt", "InstallDir") & "\Include"
EndIf
If $StdDir And FileExists($StdDir) Then
    $aInclDirs[0] += 1
    $aInclDirs[$aInclDirs[0]] = $StdDir
Else
    MsgBox(262144 + 48, $GuiTitle, "Das Standardincludeverzeichnis konnte nicht gefunden werden!")
EndIf

; Ggf. in der Registry zusätzlich eingetragene Includeverzeichnisse auslesen
Global $RegDirs = RegRead($UserInclKey, $UserInclVal)
Global $aUserDirs[1] = [0]
If Not @error Then
    $aUserDirs = StringSplit($RegDirs, ";")
    For $I = 1 To $aUserDirs[0]
        If FileExists($aUserDirs[$I]) Then
            $aInclDirs[0] += 1
            $aInclDirs[$aInclDirs[0]] = $aUserDirs[$I]
        EndIf
    Next
EndIf

Opt("GUIOnEventMode", 1)
Global $hGUI = GUICreate($GuiTitle, $GuiW, $GuiH, -1, -1, BitOr($GUI_SS_DEFAULT_GUI, $WS_SIZEBOX, $WS_MAXIMIZEBOX))
GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")
Global $idFileMenu = GUICtrlCreateMenu("&File")
GUICtrlCreateMenuItem("&Open", $idFileMenu)
GUICtrlSetOnEvent(-1, "_Open")
GUICtrlCreateMenuItem("", $idFileMenu)
GUICtrlSetOnEvent(-1, "_Exit")
GUICtrlCreateMenuItem("&Exit", $idFileMenu)
GUICtrlSetOnEvent(-1, "_Exit")
Global $idDirMenu = GUICtrlCreateMenu("&Folder")
GUICtrlCreateMenuItem($SelectAll, $idDirMenu, 0, 1)
GUICtrlSetState(-1, $GUI_CHECKED)
GUICtrlSetOnEvent(-1, "_Select_Folder")
For $I = 1 To $aInclDirs[0]
    GUICtrlCreateMenuItem($I & " > " & $aInclDirs[$I], $idDirMenu, $I, 1)
    GUICtrlSetOnEvent(-1, "_Select_Folder")
Next
Global $idFuncMenu = GUICtrlCreateMenu("&Function")
GUICtrlCreateMenuItem("&Copy to Clipboard", $idFuncMenu)
GUICtrlSetOnEvent(-1, "_CopyToClip")
Global $idScripts = GUICtrlCreateCombo("", 10, 10, ($MinW / 2) - 25, 20, BitOR($CBS_DROPDOWNLIST, $CBS_SORT, $WS_VSCROLL))
GUICtrlSetResizing(-1, $GUI_DOCKALL)
GUICtrlSetOnEvent(-1, "_Script")
Global $idFuncs = GUICtrlCreateCombo("", ($MinW / 2), 10, ($MinW / 2) - 25, 20, BitOR($CBS_DROPDOWNLIST, $CBS_SORT, $WS_VSCROLL))
GUICtrlSetResizing(-1, $GUI_DOCKALL)
GUICtrlSetOnEvent(-1, "_Function")
Global $idEdit = GUICtrlCreateEdit("", 10, 40, $GuiW - 20, $GuiH - 50 - $MH, BitOR($GUI_SS_DEFAULT_EDIT, $ES_READONLY))
GUICtrlSetFont(-1, $FSize, -1, -1, "Courier New")
GUICtrlSetBkColor(-1, 0xFFFFFF)
GUICtrlSetResizing(-1, $GUI_DOCKBORDERS)
_SetTabs($idEdit)
_Read_Folders()
GUIRegisterMsg($WM_GETMINMAXINFO, "_MinInfo")
GUISetState()

While True
    Sleep(1000)
WEnd
; ===============================================================================================================================
Func _Exit()
    Exit
EndFunc
; ===============================================================================================================================
Func _Open()
    If Not ($aInclDirs[0] < 9) Then
        MsgBox(48, $GuiTitle, "Mehr als 9 Verzeichnisse können nicht eingelesen werden!", 0, $hGUI)
        Return
    EndIf
    Local $Folder = FileSelectFolder("Include-Verzeichnis öffnen", "", 2, "", $hGUI)
    If @error Then Return
    For $I = 1 To $aInclDirs[0]
        If $aInclDirs[$I] = $Folder Then
            MsgBox(48, $GuiTitle, "Das Verzeichnis " & $Folder & " ist bereits geöffnet!", 0, $hGUI)
            Return
        EndIf
    Next
    Local $Scripts = _Get_Scripts($Folder, $aInclDirs[0] + 1)
    If $Scripts Then
        $aInclDirs[0] += 1
        $aInclDirs[$aInclDirs[0]] = $Folder
        $CurrentFolder = $aInclDirs[0] & $Delimiter & $aInclDirs[$aInclDirs[0]]
        GUICtrlSetData($idScripts, "|" & $Scripts)
        GUICtrlCreateMenuItem($CurrentFolder, $idDirMenu, $aInclDirs[0], 1)
        GUICtrlSetOnEvent(-1, "_Select_Folder")
        DllCall("User32.dll", "BOOL", "CheckMenuRadioItem", _
                "HWND", GUICtrlGetHandle($idDirMenu), "UINT", 0, "UINT", $aInclDirs[0], "UINT", $aInclDirs[0], "UINT", $MF_BYPOSITION)
        GUICtrlSendMsg($idScripts, $CB_SETCURSEL, 0, 0)
        _Script()
    Else
        MsgBox(48, $GuiTitle, "Das Verzeichnis " & $Folder & " enthält keine AU3-Skripte!", 0, $hGUI)
    EndIf
EndFunc
; ===============================================================================================================================
Func _Read_Folders()
    Global $aInclDirs
    GUICtrlSetData($idEdit, "")
    GUICtrlSetData($idFuncs, "")
    GUICtrlSetData($idScripts, "")
    For $I = 1 To $aInclDirs[0]
        Local $Scripts = _Get_Scripts($aInclDirs[$I], $I)
        If $Scripts Then
            GUICtrlSetData($idScripts, $Scripts)
        EndIf
    Next
    GUICtrlSendMsg($idScripts, $CB_SETCURSEL, 0, 0)
    _Script()
EndFunc
; ===============================================================================================================================
Func _Select_Folder()
    Global $aInclDirs
    Local $Folder = GUICtrlRead(@GUI_CtrlId, 1)
    If $Folder = $CurrentFolder Then Return
    $CurrentFolder = $Folder
    If $Folder = $SelectAll Then
        _Read_Folders()
        Return
    EndIf
    GUICtrlSetData($idEdit, "")
    GUICtrlSetData($idFuncs, "")
    GUICtrlSetData($idScripts, "")
    Local $aSplit = StringSplit($Folder, $Delimiter, 1)
    Local $Scripts = _Get_Scripts($aSplit[2], $aSplit[1])
    GUICtrlSetData($idScripts, $Scripts)
    GUICtrlSendMsg($idScripts, $CB_SETCURSEL, 0, 0)
    _Script()
    Return
EndFunc
; ===============================================================================================================================
Func _CopyToClip()
    ClipPut(GUICtrlRead($idEdit))
EndFunc
; ===============================================================================================================================
Func _SetTabs($idCtrl, $nChars = 4)
    Local Const $BaseUnits = 4
    Local $TABS = DllStructCreate("UINT")
    Local $pTabs = DllStructGetPtr($TABS)
    DllStructSetData($TABS, 1, $BaseUnits * $nChars)
    GUICtrlSendMsg($idCtrl, $EM_SETTABSTOPS, 1, $pTabs)
EndFunc
; ===============================================================================================================================
Func _MinInfo($hWnd, $iMsg, $wParam, $lParam)
    Local $MINMAXINFO = DllStructCreate("LONG[10]", $lParam)
    DllStructSetData($MINMAXINFO, 1, $MinW, 7)
    DllStructSetData($MINMAXINFO, 1, $MinH, 8)
    Return $GUI_RUNDEFMSG
EndFunc
; ===============================================================================================================================
Func _Script()
    Global $ScriptContent
    Local $Script = GUICtrlRead($idScripts)
    Local $aSplit = StringSplit($Script, $Delimiter, 1)
    If @error Then Return False
    Local $File = $aInclDirs[$aSplit[1]] & "\" & $aSplit[2]
    $ScriptContent = FileRead($File)
    GUICtrlSetData($idFuncs, _Get_Functions())
    GUICtrlSendMsg($idFuncs, $CB_SETCURSEL, 0, 0)
    _Function()
    WinSetTitle($hGUI, "", $GuiTitle & " - " & $File)
EndFunc
; ===============================================================================================================================
Func _Function()
    Local $Function = GUICtrlRead($idFuncs)
    If $Function = $ShowScript Then
        GUICtrlSetData($idEdit, $ScriptContent)
        GUICtrlSetState($idFuncMenu, $GUI_DISABLE)
    Else
        GUICtrlSetData($idEdit, _Read_Function($Function))
        GUICtrlSetState($idFuncMenu, $GUI_ENABLE)
    EndIf
    GUICtrlSetState($idEdit, $GUI_FOCUS)
EndFunc
; ===============================================================================================================================
Func _Get_Scripts($Folder, $Index)
    Local $Scripts = ""
    Local $Sep = Opt("GUIDataSeparatorChar")
    Local $Handle = FileFindFirstFile($Folder & "\*.au3")
    If $Handle = -1 Then Return ""
    Local $File = FileFindNextFile($Handle)
    While Not @error
        $Scripts &= $Index & $Delimiter & $File & $Sep
        $File = FileFindNextFile($Handle)
    WEnd
    FileClose($Handle)
    Return StringTrimRight($Scripts, 1)
EndFunc
; ===============================================================================================================================
Func _Get_Functions()
    Global $ScriptContent
    Local $Functions = "|" & $ShowScript & "|"
    Local $Sep = Opt("GUIDataSeparatorChar")
    Local $aRX = StringRegExp($ScriptContent, "(?im)^\s*func\s+(\w+)\s*\(", 3)
    If @error Then Return SetError(1, 0, $Functions)
    For $I = 0 To UBound($aRX) - 1
        $Functions &= $aRX[$I] & $Sep
    Next
    Return StringTrimRight($Functions, 1)
EndFunc
; ===============================================================================================================================
Func _Read_Function($Function)
    Global $ScriptContent
    If @error > 0 Then Return SetError(1, 0, "")
    Local $aRX = StringRegExp($ScriptContent, "(?im)(^;.*\n)*\s*func\s+" & $Function & "(?s).*?endfunc.*?$", 2)
    If @error Then Return SetError(1, 0, "")
    Return StringStripWS($aRX[0], 1) & @LF
EndFunc