#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseUpx=n
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
; ===============================================================================================================================
#include <GuiConstantsEx.au3>
#include <EditConstants.au3>
#include <WindowsConstants.au3>
; ===============================================================================================================================
Global Const $INI = @ScriptDir & "\Tail.ini"
Global Const $W = 750
Global Const $H = 500
Global Const $MinW = 600
Global Const $MinH = 400
; ===============================================================================================================================
Global $LogFile = ""
Global $LogPath = IniRead($INI, "Tail", "Path", "")
Global $MaxBytes = 1024
$aM = DllCall("User32.dll", "Int", "GetSystemMetrics", "Int", 15) ; SM_CYMENU = 15
$DH = 20 + $aM[0]
; ===============================================================================================================================
Opt("GUIOnEventMode", 1)
Opt("GUICloseOnESC", 0)
Opt("GUIResizeMode", BitOR($GUI_DOCKSIZE, $GUI_DOCKLEFT, $GUI_DOCKBOTTOM))
$hGUI = GUICreate("Tail", $W, $H, -1, -1, BitOr($GUI_SS_DEFAULT_GUI, $WS_SIZEBOX, $WS_MAXIMIZEBOX))
GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")
$idFileMenu = GUICtrlCreateMenu("&File")
$idOpen = GUICtrlCreateMenuItem("&Open", $idFileMenu)
GUICtrlSetOnEvent(-1, "_Open")
$idRead = GUICtrlCreateMenuItem("&Read", $idFileMenu)
GUICtrlSetOnEvent(-1, "_Read")
GUICtrlSetState(-1, $GUI_DISABLE)
GUICtrlCreateMenuItem("", $idFileMenu)
$idExit = GUICtrlCreateMenuItem("&Exit", $idFileMenu)
GUICtrlSetOnEvent(-1, "_Exit")
$idEditMenu = GUICtrlCreateMenu("&Edit")
$idClear = GUICtrlCreateMenuItem("&Clear", $idEditMenu)
GUICtrlSetState(-1, $GUI_DISABLE)
GUICtrlSetOnEvent(-1, "_Clear")
$idSave = GUICtrlCreateMenuItem("&Save", $idEditMenu)
GUICtrlSetState(-1, $GUI_DISABLE)
GUICtrlSetOnEvent(-1, "_Save")
$idTailMenu = GUICtrlCreateMenu("&Tail")
$idStart = GUICtrlCreateMenuItem("&Start", $idTailMenu)
GUICtrlSetState(-1, $GUI_DISABLE)
GUICtrlSetOnEvent(-1, "_StartStop")
$idBytesMenu = GUICtrlCreateMenu("&Bytes")
$idBytes = GUICtrlCreateMenuItem($MaxBytes, $idBytesMenu)
GUICtrlSetOnEvent(-1, "_Bytes")
$idEdit = GUICtrlCreateEdit("", 0, 0, $W, $H - $DH, BitOR($GUI_SS_DEFAULT_EDIT, $ES_READONLY))
GUICtrlSetResizing(-1, $GUI_DOCKBORDERS)
GUICtrlSetColor(-1, 0x000080)
GUICtrlSetBkColor(-1, 0xFFFFFF)
GUICtrlSetFont(-1, 9, -1, -1, "Courier New")
GUICtrlSendMsg(-1, $EM_LIMITTEXT, 0, 0)
$idStatus = GUICtrlCreateInput("", 0, $H - $DH, $W, 20, $ES_READONLY)
GUICtrlSetResizing(-1, BitOR($GUI_DOCKLEFT, $GUI_DOCKRIGHT, $GUI_DOCKBOTTOM, $GUI_DOCKHEIGHT))
$idDummy = GUICtrlCreateDummy()
GUICtrlSetOnEvent(-1, "_Update")
GUIRegisterMsg($WM_GETMINMAXINFO, "_MinInfo")
GUISetState()
While True
    Sleep(1000)
WEnd
; ===============================================================================================================================
; GUI & menu functions
; ===============================================================================================================================
Func _Exit()
    AdlibUnRegister("_Tail_AdLib")
    GUIDelete()
    Sleep(250)
    Exit
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 _Update()
    Local $Tail = _Tail_Content()
    GUICtrlSendMsg($idEdit, $EM_SETSEL, -2, -1)
    GUICtrlSetData($idEdit, $Tail, 1)
    GUICtrlSetData($idStatus, StringLen($Tail) & " bytes read")
EndFunc
; ===============================================================================================================================
Func _Open()
    GUICtrlSetData($idStatus, "")
    Local $File, $Pos
    $File = FileOpenDialog( "Tail - Open File", $LogPath, "Text (*.*)", 3, "" , $hGUI)
    If @error Then Return
    $Pos = StringInStr($File, "\", 0, -1)
    $LogPath = StringLeft($File, $Pos)
    IniWrite($INI, "Tail", "Path", $LogPath)
    _Clear()
    GUICtrlSetState($idRead, $GUI_ENABLE)
    GUICtrlSetState($idClear, $GUI_ENABLE)
    GUICtrlSetState($idSave, $GUI_ENABLE)
    GUICtrlSetState($idStart, $GUI_ENABLE)
    WinSetTitle($hGUI, "", "Tail - " & $File)
    $LogFile = $File
    _Tail($LogFile, $MaxBytes)
EndFunc
; ===============================================================================================================================
Func _Read()
    _Clear()
    _Tail($LogFile, $MaxBytes)
EndFunc
; ===============================================================================================================================
Func _Clear()
    GUICtrlSetData($idStatus, "")
    GUICtrlSetData($idEdit, "")
EndFunc
; ===============================================================================================================================
Func _Save()
    GUICtrlSetData($idStatus, "")
    Local $Sel, $File, $Handle, $Now
    $Sel = ControlCommand($hGUI, "", $idEdit, "GetSelected")
    If Not $Sel Then Return GUICtrlSetData($idStatus, "ERROR: Save - No text selected!")
    $File = FileSaveDialog("Tail - Append Selection to File", @ScriptDir, "Text (*.txt)", 0, "Log.txt", $hGUI)
    If @error Then Return
    $Handle = FileOpen($File, 1)
    If $Handle = -1 Then Return GUICtrlSetData($idStatus, "ERROR: Save - Cannot open file for writing!")
    $Now = StringFormat("%04d-%02d-%02d %02d:%02d:%02d > ", @YEAR, @MON, @MDAY, @HOUR, @MIN, @SEC)
    FileWrite($Handle, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" & @CRLF)
    FileWrite($Handle, $Now & $LogFile & @CRLF)
    FileWrite($Handle, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" & @CRLF)
    FileWrite($Handle, $Sel & @CRLF)
    FileClose($Handle)
    GUICtrlSetData($idStatus, "Save: " & StringLen($Sel) & " chars saved to file " & $File)
EndFunc
; ===============================================================================================================================
Func _StartStop()
    GUICtrlSetData($idStatus, "")
    If GUICtrlRead($idStart, 1) = "&Start" Then
        AdlibRegister("_Tail_AdLib", 500)
        GUICtrlSetData($idStart, "&Stop")
        $State = $GUI_DISABLE
    Else
        AdlibUnRegister("_Tail_AdLib")
        GUICtrlSetData($idStart, "&Start")
        $State = $GUI_ENABLE
    EndIf
    GUICtrlSetState($idOpen, $State)
    GUICtrlSetState($idRead, $State)
    GUICtrlSetState($idExit, $State)
    GUICtrlSetState($idClear, $State)
    GUICtrlSetState($idSave, $State)
    GUICtrlSetState($idBytes, $State)
EndFunc
; ===============================================================================================================================
Func _Bytes()
    Opt("GUIOnEventMode", 0)
    GUISetState(@SW_DISABLE, $hGUI)
    Local $hBytesGUI = Guicreate("Tail - Bytes", 300, 125, -1, -1, -1, -1, $hGUI)
    GUICtrlCreateLabel("Initial number of bytes (max. 999999):", 20, 20, 260, 20)
    Local $idInp = GUICtrlCreateInput($MaxBytes, 20, 40, 260, 20, $ES_NUMBER)
    GUICtrlSetLimit(-1, 6)
    Local $idBtn = GUICtrlCreateButton("OK", 20, 80, 260, 25)
    GUICtrlSetState(-1, $GUI_DEFBUTTON)
    GUISetState()
    While True
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
            Case $idBtn
                $MaxBytes = GUICtrlRead($idInp)
                GUICtrlSetData($idBytes, $MaxBytes)
                ExitLoop
        EndSwitch
    WEnd
    Opt("GUIOnEventMode", 1)
    GUISetState(@SW_ENABLE, $hGUI)
    GUIDelete($hBytesGui)
EndFunc
; ===============================================================================================================================
; Tail functions
; ===============================================================================================================================
Func _Tail_AdLib()
    _Tail()
EndFunc
; ===============================================================================================================================
Func _Tail_Content($bSet = False, $sNew = "")
    Local Static $Tail = ""
    If $bSet Then $Tail = $sNew
    Return $Tail
EndFunc
; ===============================================================================================================================
Func _Tail($sFile = "", $nBytes = 1024)
    Local Static $TailPos = 0
    Local Static $TailFile = ""
    Local $TailNew = ""
    If $sFile Then
        $TailFile = $sFile
        $TailPos = 0
    EndIf
    Local $Handle = FileOpen($TailFile)
    If $Handle = -1 Then Return
    FileSetPos($Handle, 0, 2)
    Local $Size = FileGetPos($Handle)
    If $TailPos = 0 Then
        If $Size > $nBytes Then
            $TailPos = $Size - $nBytes
        EndIf
    ElseIf $Size < $TailPos Then
        $TailPos = $Size
    EndIf
    If $Size > $TailPos Then
        FileSetPos($Handle, $TailPos, 0)
        $TailNew = FileRead($Handle)
        $TailPos = FileGetPos($Handle)
    EndIf
    FileClose($Handle)
    If $TailNew Then
        _Tail_Content(True, $TailNew)
        GUICtrlSendToDummy($idDummy)
    EndIf
EndFunc