package ca.uhn.fhir.rest.server.messaging.json;

import ca.uhn.fhir.model.api.IModelJson;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * This class is for holding headers for BaseJsonMessages. Any serializable data can be thrown into
 * the header map. There are also three special headers, defined by the constants in this class, which are for use
 * in message handling retrying. There are also matching helper functions for fetching those special variables; however
 * they can also be accessed in standard map fashion with a `get` on the map.
 */
public class HapiMessageHeaders implements Map<String, Object>, IModelJson {
    public static String RETRY_COUNT_KEY = "retryCount";
    public static String FIRST_FAILURE_KEY = "firstFailure";
    public static String LAST_FAILURE_KEY = "lastFailure";

    private final Map<String, Object> headers;

    public HapiMessageHeaders(Map<String, Object> theHeaders) {
        headers = theHeaders;
    }

    public HapiMessageHeaders() {
        headers = new HashMap<>();
    }


    @Override
    public int size() {
        return this.headers.size();
    }

    @Override
    public boolean isEmpty() {
        return this.headers.isEmpty();
    }

    @Override
    public boolean containsKey(Object key) {
        return this.headers.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return this.headers.containsValue(value);
    }

    @Override
    public Object get(Object key) {
         return this.headers.get(key);
    }

    @Override
    public Object put(String key, Object value) {
        return this.headers.put(key, value);
    }

    @Override
    public Object remove(Object key) {
        return this.headers.remove(key);
    }

    @Override
    public void putAll(Map<? extends String, ?> m) {
        this.headers.putAll(m);
    }

    @Override
    public void clear() {
        this.headers.clear();
    }

    @Override
    public Set<String> keySet() {
        return this.headers.keySet();
    }

    @Override
    public Collection<Object> values() {
        return this.headers.values();
    }

    @Override
    public Set<Entry<String, Object>> entrySet() {
        return this.headers.entrySet();
    }

    public Integer getRetryCount() {
        return (Integer)this.getHeaders().get(RETRY_COUNT_KEY);
    }

    public Long getFirstFailureDate() {
    	return (Long)this.getHeaders().get(FIRST_FAILURE_KEY);
    }

    public Long getLastFailureDate() {
    	return (Long)this.getHeaders().get(LAST_FAILURE_KEY);
    }

    public void setRetryCount(Integer theRetryCount) {
    	this.getHeaders().put(RETRY_COUNT_KEY, theRetryCount);
	 }

    public void setLastFailureDate(Long theLastFailureDate) {
    	this.getHeaders().put(LAST_FAILURE_KEY, theLastFailureDate);
	 }
	public void setFirstFailureDate(Long theFirstFailureDate) {
    	this.getHeaders().put(FIRST_FAILURE_KEY, theFirstFailureDate);
	 }
    public Map<String, Object> getHeaders() {
        return this.headers;
    }

	/**
	 * Sets deffault values for the special headers that HAPI cares about during retry.
	 */
	public void initializeDefaultRetryValues() {
        headers.put(RETRY_COUNT_KEY, 0);
        headers.put(FIRST_FAILURE_KEY, null);
        headers.put(LAST_FAILURE_KEY, null);
    }
}

@@ -860,16 +860,128 @@
 lure%22;%0A%0A
+    private Integer retryCount;%0A    private Long firstFailureTimestamp;%0A    private Long lastFailureTimestamp;%0A%0A
     priv
@@ -2436,24 +2436,55 @@
 ryCount() %7B%0A
+    %09return this.retryCount;%0A//
         retu
@@ -2586,32 +2586,74 @@
 ) %7B%0A    %09return 
+this.firstFailureTimestamp;%0A    %09//return 
 (Long)this.getHe
@@ -2731,24 +2731,26 @@
 te() %7B%0A    %09
+//
 return (Long
@@ -2783,32 +2783,71 @@
 T_FAILURE_KEY);%0A
+    %09return this.lastFailureTimestamp;%0A
     %7D%0A%0A    publi
@@ -2952,16 +2952,54 @@
 Count);%0A
+    %09this.retryCount = theRetryCount;%0A
 %09 %7D%0A%0A   
@@ -3119,24 +3119,77 @@
 ilureDate);%0A
+    %09this.lastFailureTimestamp = theLastFailureDate;%0A
 %09 %7D%0A%09public 
@@ -3309,16 +3309,71 @@
 eDate);%0A
+    %09this.firstFailureTimestamp = theFirstFailureDate;%0A
 %09 %7D%0A    

add attributes to HapiHeaders. Turns out deserialization makes a best guess, and doesnt respect types in Map<String,Object>