package org.chenyang.http.impl.cookie;

import org.chenyang.http.annotation.Contract;
import org.chenyang.http.annotation.ThreadingBehavior;
import org.chenyang.http.cookie.ClientCookie;
import org.chenyang.http.cookie.CommonCookieAttributeHandler;
import org.chenyang.http.cookie.Cookie;
import org.chenyang.http.cookie.CookieOrigin;
import org.chenyang.http.cookie.CookieRestrictionViolationException;
import org.chenyang.http.cookie.MalformedCookieException;
import org.chenyang.http.cookie.SetCookie;
import org.chenyang.http.cookie.SetCookie2;
import org.chenyang.http.util.Args;
import java.util.StringTokenizer;

@Contract(
   threading = ThreadingBehavior.IMMUTABLE
)
public class RFC2965PortAttributeHandler implements CommonCookieAttributeHandler {
   private static int[] parsePortAttribute(String portValue) throws MalformedCookieException {
      StringTokenizer st = new StringTokenizer(portValue, ",");
      int[] ports = new int[st.countTokens()];

      try {
         for(int i = 0; st.hasMoreTokens(); ++i) {
            ports[i] = Integer.parseInt(st.nextToken().trim());
            if (ports[i] < 0) {
               throw new MalformedCookieException("Invalid Port attribute.");
            }
         }

         return ports;
      } catch (NumberFormatException e) {
         throw new MalformedCookieException("Invalid Port attribute: " + e.getMessage());
      }
   }

   private static boolean portMatch(int port, int[] ports) {
      boolean portInList = false;

      for(int port2 : ports) {
         if (port == port2) {
            portInList = true;
            break;
         }
      }

      return portInList;
   }

   public void parse(SetCookie cookie, String portValue) throws MalformedCookieException {
      Args.notNull(cookie, "Cookie");
      if (cookie instanceof SetCookie2) {
         SetCookie2 cookie2 = (SetCookie2)cookie;
         if (portValue != null && !portValue.trim().isEmpty()) {
            int[] ports = parsePortAttribute(portValue);
            cookie2.setPorts(ports);
         }
      }

   }

   public void validate(Cookie cookie, CookieOrigin origin) throws MalformedCookieException {
      Args.notNull(cookie, "Cookie");
      Args.notNull(origin, "Cookie origin");
      int port = origin.getPort();
      if (cookie instanceof ClientCookie && ((ClientCookie)cookie).containsAttribute("port") && !portMatch(port, cookie.getPorts())) {
         throw new CookieRestrictionViolationException("Port attribute violates RFC 2965: Request port not found in cookie's port list.");
      }
   }

   public boolean match(Cookie cookie, CookieOrigin origin) {
      Args.notNull(cookie, "Cookie");
      Args.notNull(origin, "Cookie origin");
      int port = origin.getPort();
      if (cookie instanceof ClientCookie && ((ClientCookie)cookie).containsAttribute("port")) {
         if (cookie.getPorts() == null) {
            return false;
         }

         if (!portMatch(port, cookie.getPorts())) {
            return false;
         }
      }

      return true;
   }

   public String getAttributeName() {
      return "port";
   }
}
