%mapHinzu(alte Map, Key, Value, neue Map mit neuem/ersetzen Eintrag)
mapHinzu([],Key, Val, [[Key, Val]]).
mapHinzu([[Key, _] | T], Key, Val, [[Key, Val] | T]).

mapHinzu([[Key0, Val0] | T], Key, Val,[[Key0, Val0] | T1]) :- 
  not(Key0 = Key), 
  mapHinzu(T, Key, Val, T1).

mapVal([[Key, Val] | _], Key, Val).
mapVal([[Key0, _] | T], Key, Val) :- 
  not(Key0 = Key), 
  mapVal(T, Key, Val).
  
generiereMap([],[]).
generiereMap([K| T],[[K, null]| Rest]) :- 
  generiereMap(T, Rest). 

% Beispielklasse
klasse(person, [minr, name]).

% Speicher fuer alle existierenden Objekte, kann auch direkt mapHinzu genutzt werden
store(Store, Var, Objekt, NewStore) :- mapHinzu(Store, Var, Objekt, NewStore). 
 
% konstruktor(Klasse, lokale Variable, alter Speicher, neuer Speicher) 
konstruktor(Klasse, Var, Store, NewStore) :- 
  klasse(Klasse, Objektvariablen),
  generiereMap(Objektvariablen, Objekt0),
  mapHinzu(Objekt0, class, Klasse, Objekt), % Klasse zusaetzlich merken
  store(Store, Var, Objekt, NewStore).
  
% aktuell keine Typpruefung, waere weiterer Parameter 
% setVar(lokale Variable, deren Objektvariable, alter, neue Speicher)
setVar(Var, Attribut, Wert, Store, NewStore):-
  mapVal(Store, Var, Alt), % hol das Objekt
  mapHinzu(Alt, Attribut, Wert, NeuesObjekt), % aktualisiere Objekt
  mapHinzu(Store, Var, NeuesObjekt, NewStore).
  
% getVar(lokale Variable, deren Objektvariable, aktueller Wert, Speicher)  
getVar(Var, Attribut, Wert, Store) :-
  mapVal(Store, Var, Objekt),
  mapVal(Objekt, Attribut, Wert).     
  
/* equals(Klasse, Variable1, Variable2, aktueller Objektspeicher) */  
%Typpruefung leicht einbaubar
equals(Klasse,Var1,Var2,Store):-
  klasse(Klasse, Attribute),
  mapVal(Store,Var1,Objekt1),
  mapVal(Store,Var2,Objekt2),
  equals(Attribute,Objekt1,Objekt2).  
  
/* equals(Objektvariablenliste, Objekt1, Objekt2) */  
equals([] ,_ ,_).  

equals([Att|T], Objekt1, Objekt2) :-
  mapVal(Objekt1, Att, Wert1),
  mapVal(Objekt2, Att, Wert2),
  Wert1 = Wert2,  % Unifikation
  equals(T, Objekt1, Objekt2).    
  
testKonstruktortGetSet:-
  konstruktor(person,x,[],St),
  setVar(x, minr, 42, St, St2),
  setVar(x, name, 'xena', St2, St3),
  getVar(x, minr, Val, St3),
  konstruktor(person,y,St3, St4),
  Val2 is Val + 1,
  setVar(y, minr, Val2, St4, St5),
  setVar(y, name, 'conan', St5, NSt),
  getVar(y, minr, Val3, NSt),
  NSt = [[x, [[minr, 42], [name, xena], [class, person]]], 
        [y, [[minr, 43], [name, conan], [class, person]]]],
  Val3 = 43, !.
    
testEqualsTrue :-
  konstruktor(person, x, [], St),
  setVar(x, minr, 42, St, St2),
  setVar(x, name, 'xena', St2, St3),
  konstruktor(person, y, St3, St4),
  setVar(y, minr, 42, St4, St5),
  setVar(y, name, 'xena', St5, NSt),
  equals(person, x, y, NSt),
  equals(person, y, x, NSt), !.  
  
testEqualsFalse :-
  konstruktor(person, x, [], St),
  setVar(x, minr, 42, St, St2),
  setVar(x, name, 'xena', St2, St3),
  konstruktor(person, y, St3, St4),
  setVar(y, minr, 42, St4, St5),
  setVar(y, name, 'xeni', St5, NSt),
  not(equals(person, x, y, NSt)),
  not(equals(person, y, x, NSt)), !.   
  
% Methode: Erhoehe minr um uebergebenen Wert
% hier mit Typpruefung  
% addMinr(Variable, Parameter, alter Speicher, neuer Speicher)
addMinr(Variable, Parameter, Old, NewStore) :-
  passenderTyp(person, Variable, Old),
  getVar(Variable, minr, AlterWert, Old),
  Neu is AlterWert + Parameter,
  setVar(Variable, minr, Neu, Old, NewStore).
  	
% passenderTyp(Klasse, hat diese Variable die Klasse, Speicher)  	
passenderTyp(Klasse, Variable, Store):-	
  mapVal(Store, Variable, Objekt),
  mapVal(Objekt, class, Class),
  Klasse = Class.  % passender Typ	
  
testAddMinr:-
  konstruktor(person, x, [], St),
  setVar(x, minr, 42, St, St2),
  setVar(x, name, 'xena', St2, St3),  
  addMinr(x, 43, St3, St4), 
  getVar(x, minr, 85, St4), !.
  
testSuite :-
  testKonstruktortGetSet,
  write('KonstruktorGetSet ok'),
  nl,
  testEqualsFalse,
  write('EqualsFalse ok'), nl,
  testEqualsTrue,
  write('EqualsTrue ok'), nl,
  testAddMinr,
  write('AddMinr ok'), nl.