#include-once

; #INDEX# =======================================================================================================================
; Title .........: RingBuffer
; AutoIt Version : 3.3.14.5
; Language ......: English
; Description ...: Functions for manipulating a FIFO ring buffer with no overflow protection
; Author ........: alpines (autoit.de)
; ===============================================================================================================================

; #CURRENT# =====================================================================================================================
; _RingBuffer_Create
; _RingBuffer_Destroy
; _RingBuffer_GetCapacity
; _RingBuffer_GetCount
; _RingBuffer_Add
; _RingBuffer_Clear
; _RingBuffer_Remove
; _RingBuffer_GetOldest
; _RingBuffer_GetNewest
; _RingBuffer_GetAll
; ===============================================================================================================================

; #FUNCTION# ====================================================================================================================
; Name ..........: _RingBuffer_Create
; Description ...: Returns a FIFO based ring buffer with no overflow protection.
; Syntax ........: _RingBuffer_Create($iCapacity)
; Parameters ....: $iCapacity           - the capacity of the ring buffer
; Return values .: Success	- A ring buffer
;				   Failure	- False
; Author ........: alpines (autoit.de)
; Remarks .......: This RingBuffer uses a FIFO structure which means that the oldest element will be returned.
;				   If the buffer overflows the oldest element will be overwritten and the last item pointer altered.
;				   If the buffer underflows (removing elements from an empty ring buffer) the operation will fail.
; Related .......: _RingBuffer_Destroy
; ===============================================================================================================================
Func _RingBuffer_Create($iCapacity)
	If $iCapacity < 1 Then Return False

	Local $aRingBuffer[$iCapacity + 2]
	$aRingBuffer[0] = Null ;New Item Pointer
	$aRingBuffer[1] = Null ;Last Item Pointer

	Return $aRingBuffer
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _RingBuffer_Destroy
; Description ...: Destroys a ring buffer.
; Syntax ........: _RingBuffer_Destroy(ByRef $aRingBuffer)
; Parameters ....: $aRingBuffer         - [in/out] an existing ring buffer to destroy
; Return values .: True
; Author ........: alpines (autoit.de)
; Related .......: _RingBuffer_Create
; ===============================================================================================================================
Func _RingBuffer_Destroy(ByRef $aRingBuffer)
	$aRingBuffer = ""
	Return True
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _RingBuffer_GetCapacity
; Description ...: Returns the capacity of a ring buffer.
; Syntax ........: _RingBuffer_GetCapacity(ByRef $aRingBuffer)
; Parameters ....: $aRingBuffer         - [in/out] an existing ring buffer
; Return values .: The capacity of the ring buffer
; Author ........: alpines (autoit.de)
; Related .......: _RingBuffer_GetCount
; ===============================================================================================================================
Func _RingBuffer_GetCapacity(ByRef $aRingBuffer)
	Return UBound($aRingBuffer) - 2
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _RingBuffer_GetCount
; Description ...: Returns the count of items in the ring buffer (which may not exceed the initial capacity).
; Syntax ........: _RingBuffer_GetCount(ByRef $aRingBuffer)
; Parameters ....: $aRingBuffer         - [in/out] an existing ring buffer
; Return values .: The count of items inside the ring buffer
; Author ........: alpines (autoit.de)
; Related .......: _RingBuffer_GetCapacity
; ===============================================================================================================================
Func _RingBuffer_GetCount(ByRef $aRingBuffer)
	If $aRingBuffer[0] = Null or $aRingBuffer[1] = Null Then
		Return 0
	ElseIf $aRingBuffer[0] < $aRingBuffer[1] Then
		Return $aRingBuffer[0] + UBound($aRingBuffer) - 2 - $aRingBuffer[1] + 1
	ElseIf $aRingBuffer[0] >= $aRingBuffer[1] Then
		Return $aRingBuffer[0] - $aRingBuffer[1] + 1
	EndIf
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _RingBuffer_Add
; Description ...: Adds an item into a ring buffer.
; Syntax ........: _RingBuffer_Add(ByRef $aRingBuffer, $vItem)
; Parameters ....: $aRingBuffer         - [in/out] a ring buffer
;                  $vItem               - the item to be added
; Return values .: True
; Author ........: alpines (autoit.de)
; Remarks .......: If the buffer overflows the oldest element will be overwritten and the last item pointer altered.
; Related .......: _RingBuffer_Clear _RingBuffer_Remove
; ===============================================================================================================================
Func _RingBuffer_Add(ByRef $aRingBuffer, $vItem)
	If $aRingBuffer[0] = Null Then
		$aRingBuffer[0] = 2
	Else
		$aRingBuffer[0] = Mod($aRingBuffer[0] + 1 - 2, UBound($aRingBuffer) - 2) + 2
	EndIf

	$aRingBuffer[$aRingBuffer[0]] = $vItem

	If $aRingBuffer[1] = Null Then
		$aRingBuffer[1] = 2
	ElseIf $aRingBuffer[0] = $aRingBuffer[1] Then
		;If our new item pointer is on the last item pointer we need to alter the last item pointer further since the item was overwritten.
		$aRingBuffer[1] = Mod($aRingBuffer[1] + 1 - 2, UBound($aRingBuffer) - 2) + 2
	EndIf

	Return True
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _RingBuffer_Clear
; Description ...: Clears a ring buffer and resets the new and last item pointers.
; Syntax ........: _RingBuffer_Clear(ByRef $aRingBuffer)
; Parameters ....: $aRingBuffer         - [in/out] a ring buffer
; Return values .: True
; Author ........: alpines (autoit.de)
; Related .......: _RingBuffer_Add _RingBuffer_Remove
; ===============================================================================================================================
Func _RingBuffer_Clear(ByRef $aRingBuffer)
	$aRingBuffer[0] = Null
	$aRingBuffer[1] = Null

	For $i = 2 To UBound($aRingBuffer) - 1
		$aRingBuffer[$i] = ""
	Next

	Return True
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _RingBuffer_Remove
; Description ...: Returns the oldest item in the ring buffer and removes it from there.
; Syntax ........: _RingBuffer_Remove(ByRef $aRingBuffer)
; Parameters ....: $aRingBuffer         - [in/out] a ring buffer
; Return values .: Success	- Item contained in the ring buffer
;				   Failure	- False
; Author ........: alpines (autoit.de)
; Remarks .......: If the buffer underflows (removing elements from an empty ring buffer) the operation will fail.
; Related .......: _RingBuffer_Add _RingBuffer_Clear
; ===============================================================================================================================
Func _RingBuffer_Remove(ByRef $aRingBuffer)
	If $aRingBuffer[1] = Null Then Return False

	Local $vItem = $aRingBuffer[$aRingBuffer[1]]
	$aRingBuffer[$aRingBuffer[1]] = ""

	If $aRingBuffer[0] = $aRingBuffer[1] Then
		$aRingBuffer[0] = Null
		$aRingBuffer[1] = Null
	Else
		$aRingBuffer[1] = Mod($aRingBuffer[1] + 1 - 2, UBound($aRingBuffer) - 2) + 2
	EndIf

	Return $vItem
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _RingBuffer_GetOldest
; Description ...: Gets the oldest element in the ring buffer.
; Syntax ........: _RingBuffer_GetOldest(ByRef $aRingBuffer)
; Parameters ....: $aRingBuffer         - [in/out] an existing ring buffer
; Return values .: Success	- First Item of the ring buffer
;				   Failure	- False (ring buffer is empty)
; Author ........: alpines (autoit.de)
; Related .......: _RingBuffer_GetNewest _RingBuffer_GetAll
; ===============================================================================================================================
Func _RingBuffer_GetOldest(ByRef $aRingBuffer)
	If $aRingBuffer[1] = Null Then Return False
	Return $aRingBuffer[$aRingBuffer[1]]
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _RingBuffer_GetNewest
; Description ...: Gets the newest element in the ring buffer.
; Syntax ........: _RingBuffer_GetNewest(ByRef $aRingBuffer)
; Parameters ....: $aRingBuffer         - [in/out] an existing ring buffer
; Return values .: Success	- Last item of the ring buffer
;				   Failure	- False (ring buffer is empty)
; Author ........: alpines (autoit.de)
; Related .......: _RingBuffer_GetOldest _RingBuffer_GetAll
; ===============================================================================================================================
Func _RingBuffer_GetNewest(ByRef $aRingBuffer)
	If $aRingBuffer[0] = Null Then Return False
	Return $aRingBuffer[$aRingBuffer[0]]
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _RingBuffer_GetAll
; Description ...: Returns a sorted array of all items in the ring buffer.
; Syntax ........: _RingBuffer_GetAll(ByRef $aRingBuffer[, $bNewestFirst = False])
; Parameters ....: $aRingBuffer         - [in/out] an existing ring buffer
;                  $bNewestFirst        - [optional] if true then the first index contains the newest item
; Return values .: An array containing all ring buffer items (which may not exceed the initial stated capacity)
; Author ........: alpines (autoit.de)
; Related .......: _RingBuffer_GetOldest _RingBuffer_GetNewest
; ===============================================================================================================================
Func _RingBuffer_GetAll(ByRef $aRingBuffer, $bNewestFirst = False)
	If $aRingBuffer[0] = Null or $aRingBuffer[1] = Null Then
		Local $aArray[0]
	ElseIf $aRingBuffer[0] < $aRingBuffer[1] Then
		Local $aArray[$aRingBuffer[0] + UBound($aRingBuffer) - 2 - $aRingBuffer[1] + 1]
	ElseIf $aRingBuffer[0] >= $aRingBuffer[1] Then
		Local $aArray[$aRingBuffer[0] - $aRingBuffer[1] + 1]
	EndIf

	Local $bOutOfBounds = False

	For $i = 0 To UBound($aArray) - 1
		If $bNewestFirst Then
			If $aRingBuffer[0] - $i < 2 Then $bOutOfBounds = True
			Local $iIndex = $bOutOfBounds ? UBound($aRingBuffer) - Abs($aRingBuffer[0] - $i - 2) : $aRingBuffer[0] - $i

			$aArray[$i] = $aRingBuffer[$iIndex]
		Else
			$aArray[$i] = $aRingBuffer[Mod($aRingBuffer[1] + $i - 2, UBound($aRingBuffer) - 2) + 2]
		EndIf
	Next

	Return $aArray
EndFunc