package Hashmap;

import java.util.ArrayList;

import java.util.ArrayList;
import java.util.LinkedList;

public class MyGenericHashMap<K, V> {
    // Konstantenwert für die Größe der HashMap (Anzahl der Buckets).
    final int SIZE = 10;

    // Array, das die Einträge (key-value Paare) der HashMap speichert.
    Entry<K,V>[] table;

    // Konstruktor für die MyGenericHashMap-Klasse.
    public MyGenericHashMap() {
        // Initialisiert das Array mit der angegebenen Größe.
        table = new Entry[SIZE];
    }

    // Innere Klasse, die ein einzelnes key-value Paar darstellt.
    class Entry<K, V> {
        K key; // Der Schlüssel des Eintrags.
        V value; // Der Wert, der dem Schlüssel zugeordnet ist.
        Entry<K,V> next;
        
        // Konstruktor für ein Entry-Objekt.
        public Entry(K key, V value) {
            this.key = key; // Initialisiert den Schlüssel.
            this.value = value; // Initialisiert den Wert.
        }
        public Entry() {
        	this.key = null;
        }

		public K getKey() {
			return key;
		}

		public V getValue() {
			return value;
		}

		public void setValue(V value) {
			this.value = value;
		}
        
    }
    //Fügt ein Schlüssel-Wert-Paar in die HashMap ein. 
    //Wenn der Schlüssel bereits existiert, wird der alte Wert durch den neuen ersetzt.
    public Entry<K, V> put(K key, V value) {
        int hash = key.hashCode() % SIZE;
        Entry<K,V> e = table[hash];
        
        if (e == null) {
            table[hash] = new Entry<K,V>(key, value);
            return table[hash]; // Gibt den neuen Eintrag zurück
        } else {
            while (e != null) {
                if (e.getKey().equals(key)) {
                    e.setValue(value);
                    return e; // Gibt den aktualisierten Eintrag zurück
                }
                if (e.next == null) {
                    break;
                }
                e = e.next;
            }
            e.next = new Entry<K,V>(key, value);
            return e.next; // Gibt den neuen Eintrag zurück
        }
    }
    
    //Gibt den Wert zurück, der dem angegebenen Schlüssel zugeordnet ist. 
    //Wenn der Schlüssel nicht existiert, wird null zurückgegeben.
    public V getValue(K key) {
    	int hash = key.hashCode() % SIZE;
    	//fange beim Head Index an
    	Entry<K,V> temp = table[hash];
    	while (temp != null) {
    		if (temp.getKey().equals(key)) {
    			return temp.getValue();
    		}
    		temp = temp.next;
    	}
    	
    	return null;
    }
    
    //Entfernt das Schlüssel-Wert-Paar für den angegebenen Schlüssel aus der HashMap.
    //Gibt den Wert zurück, der entfernt wurde, oder null, wenn der Schlüssel nicht existiert.
    public V removeValue(K key) {
    	
    	int hash = key.hashCode() % SIZE;
    	Entry<K,V> temp = table[hash];
    	if (temp == null)
    		return null;
    	// speicher das prevs Knote von temp
    	Entry<K,V> prevs = null; 
    	while(temp != null) {
    		V merker = temp.getValue();
    		// falls der Key gefunden wäre
    		if (temp.getKey().equals(key)) {
    			//falls ja, dann ist meine Knote gleich der Head
    			// und es gibt keine weiter Knoten
    			if (prevs == null)
    				table[hash] = temp.next;
    			else
    				prevs.next = temp.next;
    			return merker;
    		}
    		prevs = temp;
    		temp =temp.next;
    	}
    	return null;
    	
    }
   
    //Ausgabe der Elemente
    public void printAll() {
    	for (Entry<K,V> temp : table) {
    		if (temp != null) {
    			System.out.println("[" + temp.key + "] :" + temp.value + " ");
    			while(temp.next != null) {
    				temp = temp.next;
    				System.out.println("[" + temp.key + "] :" + temp.value + " ");
    			}
    		}
    			
    	}
    		
    }
    
   //Überprüft, ob der angegebene Schlüssel in der HashMap vorhanden ist. 
   //Gibt true zurück, wenn der Schlüssel existiert, andernfalls false.
    public boolean containsKey(K key) {
    	// hashCode(): gibt den Wert als ganzzahl zurück
    		//Beispiel: 'a' => 97
    	int  hash = key.hashCode() % SIZE;
    	Entry<K,V> temp = table[hash];
    	while (temp != null) {
    		if (temp.getKey().equals(key))
    			return true;
    		
    		temp = temp.next;
    	}
    	
    	return false;
    }
    
    //Gibt die Anzahl der Schlüssel-Wert-Paare in der HashMap zurück.
    public int size() {
    	int index = 0;
    	for (int i = 0; i < SIZE; i++) {
    		Entry<K,V> temp = table[i];
    		if (temp != null) {
    			index++;
    			while(temp.next != null) {
    				index++;
    				temp = temp.next;
    			}
    		}
    	}
    	return index;
    }
    
    //Überprüft, ob die HashMap leer ist (keine Einträge enthält). Gibt true zurück, 
    //wenn sie leer ist, andernfalls false.
    public boolean isEmpty() {
    	for (int i = 0 ; i < SIZE; i++) {
    		Entry<K,V> temp = table[i];
    		if (temp != null)
    			return true;
    	}
    	
    	return false;
    }
    public static void main(String[] args) {
        MyGenericHashMap<Integer, String> t1 = new MyGenericHashMap<>();
       t1.put(3009594, "obai");
       t1.put(3129594, "omar");
       t1.put(3129592, "abd");
       t1.put(3129392, "basel");
      
       t1.printAll();
    	
    
    }
    
    
}
