#Region - TimeStamp
; 2012-04-23 15:53:28   v 0.2
#EndRegion - TimeStamp

#include "BasicAutoItObject.au3"

; =========================================================================================================================================
;    Einfaches Bsp. für Vererbung
; =========================================================================================================================================
; == Klasse Körper erstellen
; == gemeinsames Merkmal aller Körper: 3 Dimensionen
$Koerper = _BAO_Class_Create('Koerper')
_BAO_Class_PropSet($Koerper, 'dimension', 3)

; == Klasse Quader als Unterklasse der Körper erstellen, dazu wird als zweiter Parameter der Klassenname der 'Vaterklasse' übergeben
; == Merkmal von Quader: 6 Seiten
; == ererbt von Körper:  3 Dimensionen
$Quader = _BAO_Class_Create('Quader', 'Koerper')
_BAO_Class_PropSet($Quader, 'seiten', 6)

; == Ausgeben der Properties "dimension" und "seiten" der Klasse "Quader"
ConsoleWrite('Dimensionen (ererbt) von ' & _BAO_Class_ObjGet($Quader, 'name') & ': ' & _BAO_Class_ObjGet($Quader, 'dimension') & @CRLF)
ConsoleWrite('Seiten von '               & _BAO_Class_ObjGet($Quader, 'name') & ': ' & _BAO_Class_ObjGet($Quader, 'seiten') & @CRLF & @CRLF)

; == Klasse Würfel als Unterklasse von Quader
; == Merkmal von Würfel: Kantenlänge identisch
; == ererbt von Quader:  6 Seiten
; == (bereits ererbt von Körper:  3 Dimensionen)
$Wuerfel = _BAO_Class_Create('Wuerfel', 'Quader')
_BAO_Class_PropSet($Wuerfel, 'kantenlaenge', 'identisch')

; == Ausgeben der Properties "dimension", "seiten" und "kantenlaenge" der Klasse "Wuerfel"
ConsoleWrite('Dimensionen (ererbt) von ' & _BAO_Class_ObjGet($Wuerfel, 'name') & ': ' & _BAO_Class_ObjGet($Wuerfel, 'dimension') & @CRLF)
ConsoleWrite('Seiten (ererbt) von '      & _BAO_Class_ObjGet($Wuerfel, 'name') & ': ' & _BAO_Class_ObjGet($Wuerfel, 'seiten') & @CRLF)
ConsoleWrite('Kantenlänge von '          & _BAO_Class_ObjGet($Wuerfel, 'name') & ': ' & _BAO_Class_ObjGet($Wuerfel, 'kantenlaenge') & @CRLF & @CRLF)

; == allen Würfeln eine gemeinsame Methode zur Verfügung stellen
Func Wuerfeln($_self) ; == $_self ist immer als erster Parameter einer Methode anzugeben! An diesen Parameter wird beim Aufruf das aufrufende Objekt übergeben
	Return '"' & _BAO_Class_ObjGet($_self, 'name') & '" würfelt eine: ' & Random(1,6,1)
EndFunc

_BAO_Class_MethodSet($Wuerfel, 'Wuerfeln')

; == Objekte der Klasse Würfel erstellen
$Wuerfel_Holz = _BAO_Class_ObjCreate($Wuerfel, 'Wuerfel_Holz')
; == nur für dieses Objekt gültige Eigenschaft setzen
_BAO_Class_ObjPropSet($Wuerfel_Holz, 'material', 'Holz')

$Wuerfel_Alu = _BAO_Class_ObjCreate($Wuerfel, 'Wuerfel_Alu')
; == nur für dieses Objekt gültige Eigenschaft setzen
_BAO_Class_ObjPropSet($Wuerfel_Alu, 'material', 'Aluminium')

$Wuerfel_Plast = _BAO_Class_ObjCreate($Wuerfel, 'Wuerfel_Plast')
; == nur für dieses Objekt gültige Eigenschaft setzen
_BAO_Class_ObjPropSet($Wuerfel_Plast, 'material', 'Plastik')

; == Material aller Würfel-Objekte abfragen
$aMember = _BAO_Class_ObjGet($Wuerfel, 'members')  ; == es kann auch ein Objekt der Klasse Würfel zur Abfrage verwendet werden, da die Gruppe 'members' identisch ist
If $aMember[0] > 0 Then
	For $i = 1 To $aMember[0]
		ConsoleWrite('Material von ' & _BAO_Class_ObjGet($aMember[$i], 'name') & ': ' & _BAO_Class_ObjGet($aMember[$i], 'material') & @CRLF)
	Next
EndIf
ConsoleWrite(@CRLF)

; == die Methode "Wuerfeln" auf alle Würfel-Objekte anwenden, die bereits abgefragte Memberauflistung verwenden
For $i = 1 To $aMember[0]
	ConsoleWrite( _BAO_Class_ObjMethodCall($aMember[$i], 'Wuerfeln') & @CRLF)
Next

; =========================================================================================================================================


; =========================================================================================================================================
;    Lange Funktionsnamen sind unhandlich, aber in der UDF erforderlich.
;    Lösung: Für das Skript eigene Aufrufe schaffen.
; =========================================================================================================================================

; == Ich schaffe mir jetzt ausschließlich für mein Skript einen Funktionsraum, der auf der UDF basiert aber nur ganz kurze Funktionsnamen aufweist
#region - Funktionskonvertierung
; == Notation: C = Class; CO = ClassObject; M = Method; P = Property

; == _BAO_Class_Create => C_Create
Func C_Create($_1, $_2='self')
	Return _BAO_Class_Create($_1, $_2)
EndFunc

; == _BAO_Class_MethodSet => C_Mset
Func C_Mset(ByRef $_1, $_2, $_3=0)
	Return _BAO_Class_MethodSet($_1, $_2, $_3)
EndFunc

; == _BAO_Class_PropSet => C_Pset
Func C_Pset(ByRef $_1, $_2, $_3='', $_4=0)
	Return _BAO_Class_PropSet($_1, $_2, $_3, $_4)
EndFunc

; == _BAO_Class_ObjCreate => CO_Create
Func CO_Create(ByRef $_1, $_2, $_3=0)
	Return _BAO_Class_ObjCreate($_1, $_2, $_3)
EndFunc

; == _BAO_Class_ObjMethodSet => CO_Mset
Func CO_Mset(ByRef $_1, $_2, $_3=0)
	Return _BAO_Class_ObjMethodSet($_1, $_2, $_3)
EndFunc

; == _BAO_Class_ObjPropSet => CO_Pset
Func CO_Pset(ByRef $_1, $_2, $_3='', $_4=0)
	Return _BAO_Class_ObjPropSet($_1, $_2, $_3, $_4)
EndFunc

; == _BAO_Class_ObjGet => Get   <== wird am häufigsten verwendet auch mehrfach in einer Befehlszeile, sollte deshalb besonders kurz und prägnant sein
Func Get($_1, $_2, $_3=-1)
	Return _BAO_Class_ObjGet($_1, $_2, $_3)
EndFunc

; == _BAO_Class_ObjMethodCall => M_Call
Func M_Call($_1, $_2, $_3='')
	Return _BAO_Class_ObjMethodCall($_1, $_2, $_3)
EndFunc
#endregion

; =========================================================================================================================================
; == nun verwende ich nur noch die konvertierten Aufrufe
; =========================================================================================================================================


; =========================================================================================================================================
;         Beispiel für Überladen
; =========================================================================================================================================
#cs
   Was ist Überladen?
   Eine Klasse wird mit Methoden und Eigenschaften ausgestattet, die für alle Mitglieder der Klasse identisch sind.
   Oft ist es aber sinnvoll für einzelne Mitglieder der Klasse diese Methoden/Eigenschaften zu individualisieren, evtl. auch nur vorübergehend.
   D.h. dem einzelnen Mitglied der Klasse wird angewiesen, eine Methode anders auszuführen als die anderen Klassenmitglieder.
   Da ich in AutoIt keine Lokalen Funktionen erstellen kann (Funktionen sind immer Global), muß ich mir hier anders behelfen.
   Ich registriere die bereits für die Klasse registrierte Methode auch für das Klassenmitglied. Beim Aufruf der Methode durch das Klassenmitglied
   wird zuerst geprüft ob die Methode für das Klassenmitglied selbst (also Lokal) registriert wurde. Ist das der Fall, wird dem Aufruf der Methode
   zusätzlich ein Parameter übergeben, der signalisiert: Achtung! Abweichend vom Standard behandeln.
   In der Methode wird dann auf diesen Parameter geprüft, ist er vorhanden erfolgt eine abweichende, "überladene" Abarbeitung der Methode.
#ce
; == Wir erstellen eine Klasse "Schüler"
$Schueler = C_Create('Schueler')

; == diese Klasse bekommt 2 Methoden
Func SagGutenMorgen($_self, $_aParam=0)                    ; == $_aParam enthält weiter Parameter, sofern übergeben; Bei Lokalem Aufruf wird $_aParam IMMER übergeben und bei Überladen ist $_aParam[0]=1
	Local $fOverLoad = False
	If IsArray($_aParam) Then                              ; == min. ein weiterer Parameter wurde übergebn
		If $_aParam[0] = 1 Then $fOverLoad = True          ; == die Methode wurde durch das aufrufende Mitglied überladen
	EndIf
	If $fOverLoad Then                                     ; == Behandlung bei Überladen (wenn Überladen durch mehrere Mitglieder vorgesehen, einfach Switch mit $_self.name )
		Local $Status = Get($_self, 'status_DE')           ; == bisheriger Lernstatus
		If $Status > 0 And $Status < 40 Then               ; == der aktuelle Lernstatus muss 40 erreichen um keine extra Deutschstunde ableisten zu müssen, >0 da ohne Property -1 zurückgegeben wird
			MsgBox(0, Get($_self, 'name'), 'Good Morning!'); == das normale Verhalten wird jetzt "Überladen" mit dem Mitglied-spezifischen Verhalten
			Return M_Call($_self, 'LerneDeutsch')          ; == eine extra Deutschstunde wird angeordnet und deren Ergebnis (neuer Status) an den Methodenaufruf zurückgegeben
		EndIf
	EndIf
	MsgBox(0, Get($_self, 'name'), 'Guten Morgen!')        ; == das ist das normale Verhalten der Methode
	Return 0                                               ; == im Normallfall Rückgabe von "0", da Rückgabe des Lernerfolgs (bei Überladen) immer ">0"
EndFunc

Func LerneDeutsch($_self)
	Local $Status = Get($_self, 'status_DE')
	If $Status = -1 Then Return 100                        ; == diese Eigenschaft ist für das Mitglied nicht gesetzt, als Kenntnisstand wird "100" zurückgegeben
	Local $Lernfortschritt = Random(3,8,1)                 ; == Fortschritt des Lernens
	Return $Status + $Lernfortschritt                      ; == Rückgabe neuer Status
EndFunc

; == die Methoden in der Klasse registrieren
C_Mset($Schueler, 'LerneDeutsch')
C_Mset($Schueler, 'SagGutenMorgen')

; == die allgemeingültige Eigenschaft
C_Pset($Schueler, 'religion', 'katholisch')

; == Jetzt brauchen wir Schüler, also Objekte dieser Klasse
$Peter = CO_Create($Schueler, 'Peter')
$Julia = CO_Create($Schueler, 'Julia')
$Mary  = CO_Create($Schueler, 'Mary')

#cs
   Nun wollen wir auch zusätzlich noch das Überladen von Eigenschaften mit betrachten. Die Klasse "Schüler" hat also z.B. die
   Eigenschaft "Religion=katholisch", da die Schule von einer kath. Einrichtung betrieben wird.
   Inzwischen hat sich die Schule aber weltoffen gestaltet und erlaubt auch den Zugang für Nichtkatholiken, allerdings ist dies nur vereinzelt
   der Fall. Als Klasseneigenschaft wird also "Religion=katholisch" vorbesetzt, hat ein Schüler nicht diese Religion, erhält er die nur für ihn
   gültige Eigenschaft "Religion=evangelisch" od. "Religion=ohne" etc. Die Eigenschaft "Religion" gibt es somit als individuelle und als
   allgemeingültige Eigenschaft. Die individuelle (Lokale) Eigenschaft genießt eine höhere Priorität und "überlädt" somit die allgemeingültige (Globale) Eigenschaft.
#ce

; == Mary ist unsere Gastschülerin aus England - sie ist evangelisch und ihre Deutschkenntnisse lassen noch zu wünschen übrig
CO_Pset($Mary, 'religion', 'evangelisch')   ; == hier überladen wir die Property "religion"
CO_Pset($Mary, 'status_DE', 30)             ; == in dieser Property speichern wir Marys Deutschkenntnisse

; == Und da es anfangs mit der Sprache bei Mary hakt, müssen wir die Methode "SagGutenMorgen" überladen
CO_Mset($Mary, 'SagGutenMorgen')            ; == Mary bekommt diese Methode separat zugewiesen, sie wird somit anders ausgeführt als beim Aufruf durch andere Klassenmitglieder

; == Nun wollen wir mal schauen, ob die Religion auch überladen wurde
; == Wir geben ein Array aller Mitglieder der Klasse Schüler aus und fragen für jedes Mitglied die Religion ab
$aMember = Get($Schueler, 'members')
For $i = 1 To $aMember[0]
	ConsoleWrite('Religion von ' & Get($aMember[$i], 'name') & ': ' & Get($aMember[$i], 'religion') & @CRLF)
Next
; == OK, das funkltioniert

#cs
   Nun noch das Überladen der Methode testen.
   Die Methode "SagGutenMorgen" reagiert in Abhängigkeit zum aufrufenden Klassenmitglied. Von der Herangehensweise ist es nicht absolut
   identisch mit einem tatsächlichen Überladen, aber der Effekt ist derselbe - und darauf kommt es an.
   Unser Gast aus England ist am Anfang noch etwas schwach in Deutsch. Solange ihre Kenntnisse nicht mindestens einen Punktwert von 40 haben,
   wird sie uns in englisch begrüßen und muß eine extra Stunde Deutschunterricht ableisten.
   Erst wenn sie den Punktwert erreicht hat, braucht sie kein Deutsch zusätzlich lernen und die dann überflüssige Eigenschaft 'status_DE' wird
   gelöscht.
#ce

; == Wir lassen uns erst mal von allen Mitgliedern der Klasse begrüßen
While 1
	For $i = 1 To $aMember[0]
		$ret = M_Call($aMember[$i], 'SagGutenMorgen')
		If $ret > 0 Then                               ; == betrifft nur Mary
			ConsoleWrite('Mary war zum Deutsch-Unterricht, neuer Lern-Status: ' & $ret & @CRLF)
			If $ret > 39 Then                          ; == der erforderliche Lernstatus wurde erreicht
				CO_Pset($Mary, 'status_DE', '', 1)     ; == die Eigenschaft 'status_DE' wird nicht mehr benötigt und daher gelöscht
				ExitLoop(2)
			EndIf
			CO_Pset($Mary, 'status_DE', $ret)          ; == neuen Status in die Property eintragen
		EndIf
	Next
	; == das wird solange wiederholt, bis Mary min. 40 Wissenspunkte hat
WEnd

; == Nun sollten uns alle Schüler auf Deutsch begrüßen
For $i = 1 To $aMember[0]
	$ret = M_Call($aMember[$i], 'SagGutenMorgen')
Next

; == Wir haben also gesehen, dass sowohl Properties als auch Methoden überladen werden können.

#cs
   Die bisherige Lösung hat allerdings einen Schwachpunkt, den ich nicht verschweigen möchte:
   Der Aufruf der Methoden wird mit dem AutoIt-Befehl "Call" veranlaßt. Dieser Befehl erlaubt aber nicht, Parameter ByRef zu übergeben.
   Das ist ein großes Handicap. Dadurch kann ich über Methoden keine Veränderung von Properties vornehmen, da ich dazu einen referenzierten
   Zugriff auf das entsprechende Klassenobjekt benötige.
   Bsp.:
  		 Die Methoden führen als ersten Parameter "$_self". Dieser Parameter wird von der Funktion zum MethodenCall an die Methode übergeben.
  		 Da eine ByRef-Übergabe nicht möglich ist, wird dieser Parameter ByVal übergeben - also als Kopie. Somit kann ich die aktuellen Werte
  		 des Klassenobjektes zwar auslesen, aber nicht ändern. :(
		 Dazu muß ich immer direkt die Original-Variable des Klassenobjekts verwenden.
#ce

