
package de.ivu.elect.jee.security;

import java.io.Serializable;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import static de.ivu.elect.jee.security.IpHostname.UNBEKANNT;

/**
 *  on 07.02.2017.
 */
public class LoginIpBestimmer implements Serializable {

  public static final String HEADER_KEY_X_REAL_IP = "X-Real-IP";

  public static final String HEADER_KEY_X_FORWARDED_FOR = "X-Forwarded-For";

  public static final String HEADER_KEY_SYS_PROPERTY = "ip.header.forward.key";

  private static final Logger LOG = LoggerFactory.getLogger(LoginIpBestimmer.class);

  private static final long serialVersionUID = 1L;

  public IpHostname bestimmeIpAdresseAusRequest() {
    return bestimmeIpAdresseAusRequest(false);
  }

  protected IpHostname bestimmeIpAdresseAusRequest(boolean schreibeErmittelteAdresseInLog) {
    try {
      return bestimmeIpAdresse(FacesContext.getCurrentInstance(), schreibeErmittelteAdresseInLog);
    } catch (Exception e) {
      LOG.warn("IP-Adresse kann nicht ermittelt werden");
      return UNBEKANNT;
    }
  }

  public IpHostname bestimmeIpAdresse(FacesContext facesContext, boolean schreibeErmittelteAdresseInLog) {
    try {
      HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest();

      IpHostname result = bestimmeAusForwardParametern(request, schreibeErmittelteAdresseInLog, request.getRemoteAddr(), request.getRemoteHost());
      if (!UNBEKANNT.equals(result)) {
        return result;
      }

      IpHostname ipHostname = new IpHostname(request.getRemoteAddr(), request.getRemoteHost(), request.getRemoteAddr(), request.getRemoteHost());
      if (schreibeErmittelteAdresseInLog) {
        LOG.info("IP-Adresse aus RemoteAddr ermittelt: " + ipHostname);
      }
      return ipHostname;
    } catch (Exception e) {
      LOG.warn("IP-Adresse kann nicht ermittelt werden");
      return UNBEKANNT;
    }
  }

  private IpHostname bestimmeAusForwardParametern(HttpServletRequest request, boolean schreibeErmittelteAdresseInLog, String remoteAddr, String remoteHost) {
    try {
      Map<String, String> map = new HashMap<>();

      Enumeration headerNames = request.getHeaderNames();
      while (headerNames.hasMoreElements()) {
        String key = (String) headerNames.nextElement();
        String value = request.getHeader(key);
        map.put(key, value);
      }

      IpHostname result;

      result = bestimmeAusParameter(map, System.getProperty(HEADER_KEY_SYS_PROPERTY, null), schreibeErmittelteAdresseInLog, remoteAddr, remoteHost);
      if (!UNBEKANNT.equals(result)) {
        return result;
      }

      result = bestimmeAusParameter(map, HEADER_KEY_X_REAL_IP, schreibeErmittelteAdresseInLog, remoteAddr, remoteHost);
      if (!UNBEKANNT.equals(result)) {
        return result;
      }

      result = bestimmeAusParameter(map, HEADER_KEY_X_FORWARDED_FOR, schreibeErmittelteAdresseInLog, remoteAddr, remoteHost);
      if (!UNBEKANNT.equals(result)) {
        return result;
      }

    } catch (Exception e) {
      LOG.warn("IP-Adresse kann nicht aus Parametern ermittelt werden");
    }

    return UNBEKANNT;
  }

  private IpHostname bestimmeAusParameter(Map<String, String> map, String key, boolean schreibeErmittelteAdresseInLog, String remoteAddr, String remoteHost) {
    if (StringUtils.isNotBlank(key) && map.containsKey(key)) {
      String value = map.get(key);
      if (StringUtils.isNotBlank(value)) {
        IpHostname ipHostname = new IpHostname(value, value, remoteAddr, remoteHost);
        if (schreibeErmittelteAdresseInLog) {
          LOG.info("IP-Adresse aus Header-Parameter: '" + key + "' ermittelt: " + ipHostname);
        }
        return ipHostname;
      }
    }

    return UNBEKANNT;
  }
}
