#include <WindowsConstants.au3>
#include <GUIConstants.au3>
#include <GDIPlus.au3>
#include <WinAPI.au3>
#include "VectorMath.au3"
#include <Array.au3>

; - Author: name22 (www.autoit.de)

Opt("GUIOnEventMode", 1)

;###-v-SETTINGS-v-###

$iGUIWidth = 600
$iGUIHeight = 400
$iARGB_BG = 0xFFFFFFFF
$nFPS = 50

$iCircles = 10

;###-^-SETTINGS-^-###

$nSleepTime = 1000 / $nFPS

$nFPS_Display = 0

$vNTdll = DllOpen("ntdll.dll")

$tPrecSleep = DllStructCreate("int64 time;")
$pPrecSleep = DllStructGetPtr($tPrecSleep)

$hMain = GUICreate("Example by name22", $iGUIWidth, $iGUIHeight)
GUISetState()

$hDC_Main = _WinAPI_GetDC($hMain)
$hDC_Buffer = _WinAPI_CreateCompatibleDC($hDC_Main)
$hBitmap_Buffer = _WinAPI_CreateCompatibleBitmap($hDC_Main, $iGUIWidth, $iGUIHeight)
_WinAPI_SelectObject($hDC_Buffer, $hBitmap_Buffer)

_GDIPlus_Startup()

$hGraphics = _GDIPlus_GraphicsCreateFromHDC($hDC_Buffer)
_GDIPlus_GraphicsSetSmoothingMode($hGraphics, 2)

$hBrush_Black = _GDIPlus_BrushCreateSolid(0xFF000000)

$hStringFormat = _GDIPlus_StringFormatCreate()
$hFamily_FPS = _GDIPlus_FontFamilyCreate("Arial")
$hFont_FPS = _GDIPlus_FontCreate($hFamily_FPS, 6)

$aMeasure = _GDIPlus_GraphicsMeasureString($hGraphics, "FPS: 000", $hFont_FPS, _GDIPlus_RectFCreate(), $hStringFormat)
$tLayout_FPS = $aMeasure[0]
$aMeasure = ""
DllStructSetData($tLayout_FPS, "X", $iGUIWidth - DllStructGetData($tLayout_FPS, "Width") - 3)
DllStructSetData($tLayout_FPS, "Y", $iGUIHeight - DllStructGetData($tLayout_FPS, "Height"))
DllStructSetData($tLayout_FPS, "Width", DllStructGetData($tLayout_FPS, "Width") + 3)

Global $aCircles[$iCircles + 1][7] = [[$iCircles]]

For $i = 1 To $aCircles[0][0]
	$aCircles[$i][4] = Random(10, 20, 1) ;Circle Radius/Mass
	$aCircles[$i][0] = Random($aCircles[$i][4], $iGUIWidth - $aCircles[$i][4], 1) ;X-Position of Circle Center
	$aCircles[$i][1] = Random($aCircles[$i][4], $iGUIHeight - $aCircles[$i][4], 1) ;Y-Position of Circle Center
	$aCircles[$i][2] = Random(-1, 1) ;X-Velocity
	$aCircles[$i][3] = Random(-1, 1) ;Y-Velocity
	$nRandomSpeed = Random(100, 200, 1) * -1 ^ Random(1, 2, 1)
	$aCircles[$i][2] = $aCircles[$i][2] * $nRandomSpeed
	$aCircles[$i][3] = $aCircles[$i][3] * $nRandomSpeed
	$aCircles[$i][5] = _GDIPlus_BrushCreateSolid("0xFF" & Hex(Random(0x000000, 0xFFFFFF, 1), 6)) ;Brush
Next

GUISetOnEvent($GUI_EVENT_CLOSE, "_Close", $hMain)
OnAutoItExitRegister("_Shutdown")
HotKeySet("i", "_DisplayInfo")

$nT_Sleep = TimerInit() + $nSleepTime
$nT_UpdateFPS = TimerInit()
While True
	DllStructSetData($tPrecSleep, "time", -10000 * ($nSleepTime - TimerDiff($nT_Sleep)))
	DllCall($vNTdll, "dword", "ZwDelayExecution", "int", 0, "ptr", $pPrecSleep)
	$nFrameTime = TimerDiff($nT_Sleep)
	$nT_Sleep = TimerInit()

	$nFPS_Cur = 1000 / $nFrameTime
	If TimerDiff($nT_UpdateFPS) >= 500 Then
	    $nFPS_Display = $nFPS_Cur
		$nT_UpdateFPS = TimerInit()
	EndIf

	_GDIPlus_GraphicsClear($hGraphics, $iARGB_BG)

	For $i1 = 1 To $aCircles[0][0]
		_GDIPlus_GraphicsFillEllipse($hGraphics, $aCircles[$i1][0] - $aCircles[$i1][4], $aCircles[$i1][1] - $aCircles[$i1][4], $aCircles[$i1][4] * 2, $aCircles[$i1][4] * 2, $aCircles[$i1][5])

		$aCircles[$i1][0] += $aCircles[$i1][2] / $nFPS_Cur
		$aCircles[$i1][1] += $aCircles[$i1][3] / $nFPS_Cur

		Switch True
			Case $aCircles[$i1][0] <= $aCircles[$i1][4] And $aCircles[$i1][2] < 0
				$aCircles[$i1][2] *= -1
			Case $aCircles[$i1][0] >= $iGUIWidth - $aCircles[$i1][4] And $aCircles[$i1][2] > 0
				$aCircles[$i1][2] *= -1
		EndSwitch
		Switch True
			Case $aCircles[$i1][1] <= $aCircles[$i1][4] And $aCircles[$i1][3] < 0
				$aCircles[$i1][3] *= -1
			Case $aCircles[$i1][1] >= $iGUIHeight - $aCircles[$i1][4] And $aCircles[$i1][3] > 0
				$aCircles[$i1][3] *= -1
		EndSwitch
		For $i2 = $i1 + 1 To $aCircles[0][0]
			Switch ($aCircles[$i1][0] - $aCircles[$i2][0]) ^ 2 + ($aCircles[$i1][1] - $aCircles[$i2][1]) ^ 2 <= ($aCircles[$i1][4] + $aCircles[$i2][4]) ^ 2
				Case True
					$aV_UN = _Vector_Normalize(_Vector_Create($aCircles[$i1][0] - $aCircles[$i2][0], $aCircles[$i1][1] - $aCircles[$i2][1]))
					$aV_UT = _Vector_Create(-$aV_UN[1], $aV_UN[0])

					$nVel1_N = $aV_UN[0] * $aCircles[$i1][2] + $aV_UN[1] * $aCircles[$i1][3]
					$nVel2_N = $aV_UN[0] * $aCircles[$i2][2] + $aV_UN[1] * $aCircles[$i2][3]
					$nVel1_T = $aV_UT[0] * $aCircles[$i1][2] + $aV_UT[1] * $aCircles[$i1][3]
					$nVel2_T = $aV_UT[0] * $aCircles[$i2][2] + $aV_UT[1] * $aCircles[$i2][3]

					$nVel1_N_New = ($nVel1_N * ($aCircles[$i1][4] - $aCircles[$i2][4]) + 2 * $aCircles[$i2][4] * $nVel2_N) / ($aCircles[$i1][4] + $aCircles[$i2][4])
					$nVel2_N_New = ($nVel2_N * ($aCircles[$i2][4] - $aCircles[$i1][4]) + 2 * $aCircles[$i1][4] * $nVel1_N) / ($aCircles[$i1][4] + $aCircles[$i2][4])

					$aCircles[$i1][2] = $nVel1_N_New * $aV_UN[0] + $nVel1_T * $aV_UT[0]
					$aCircles[$i1][3] = $nVel1_N_New * $aV_UN[1] + $nVel1_T * $aV_UT[1]
					$aCircles[$i2][2] = $nVel2_N_New * $aV_UN[0] + $nVel2_T * $aV_UT[0]
					$aCircles[$i2][3] = $nVel2_N_New * $aV_UN[1] + $nVel2_T * $aV_UT[1]
			EndSwitch
		Next
	Next

	_GDIPlus_GraphicsDrawStringEx($hGraphics, "FPS: " & Round($nFPS_Display, 1), $hFont_FPS, $tLayout_FPS, $hStringFormat, $hBrush_Black)
	_WinAPI_BitBlt($hDC_Main, 0, 0, $iGUIWidth, $iGUIHeight, $hDC_Buffer, 0, 0, $SRCCOPY)
WEnd

Func _DisplayInfo()
	$nTmp = TimerDiff($nT_Sleep)
	_ArrayDisplay($aCircles)
	$nT_Sleep = TimerInit() - $nTmp
EndFunc

Func _Close()
	Exit
EndFunc

Func _Shutdown()
	_WinAPI_ReleaseDC($hMain, $hDC_Main)
	_WinAPI_DeleteDC($hDC_Buffer)
	_WinAPI_DeleteObject($hBitmap_Buffer)

	_GDIPlus_GraphicsDispose($hGraphics)
	_GDIPlus_BrushDispose($hBrush_Black)
	For $i = 1 To $aCircles[0][0]
		_GDIPlus_BrushDispose($aCircles[$i][6])
	Next
	_GDIPlus_StringFormatDispose($hStringFormat)
	_GDIPlus_FontFamilyDispose($hFamily_FPS)
	_GDIPlus_FontDispose($hFont_FPS)
	_GDIPlus_Shutdown()

	DllClose($vNTdll)
EndFunc


; #FUNCTION#;===============================================================================
;
; Name...........: _HighPrecisionSleep()
; Description ...: Sleeps down to 0.1 microseconds
; Syntax.........: _HighPrecisionSleep( $iMicroSeconds, $hDll=False)
; Parameters ....:  $iMicroSeconds      - Amount of microseconds to sleep
;                  $hDll  - Can be supplied so the UDF doesn't have to re-open the dll all the time.
; Return values .: None
; Author ........: Andreas Karlsson (monoceres)
; Modified.......:
; Remarks .......: Even though this has high precision you need to take into consideration that it will take some time for autoit to call the function.
; Related .......:
; Link ..........;
; Example .......; No
;
;;==========================================================================================
Func _HighPrecisionSleep($iMicroSeconds,$hDll=False)
    Local $hStruct, $bLoaded
    If Not $hDll Then
        $hDll=DllOpen("ntdll.dll")
        $bLoaded=True
    EndIf
    $hStruct=DllStructCreate("int64 time;")
    DllStructSetData($hStruct,"time",-1*($iMicroSeconds*10))
    DllCall($hDll,"dword","ZwDelayExecution","int",0,"ptr",DllStructGetPtr($hStruct))
    If $bLoaded Then DllClose($hDll)
EndFunc