/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

package foam.nanos.http;

import foam.box.Skeleton;
import foam.core.ContextAware;
import foam.core.X;
import foam.dao.DAO;
import foam.dao.DAOSkeleton;
import foam.nanos.boot.NSpec;
import foam.nanos.boot.NSpecAware;
import foam.nanos.logger.Logger;
import foam.nanos.NanoService;
import foam.nanos.pm.PM;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;

public class NanoRouter
  extends HttpServlet
  implements NanoService, ContextAware
{
  protected X x_;

  protected Map<String, HttpServlet> handlerMap_ = new ConcurrentHashMap<>();

  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String      path       = req.getRequestURI();
    String[]    urlParams  = path.split("/");
    String      serviceKey = urlParams[2];
    Object      service    = getX().get(serviceKey);
    DAO         nSpecDAO   = (DAO) getX().get("nSpecDAO");
    NSpec       spec       = (NSpec) nSpecDAO.find(serviceKey);
    HttpServlet serv       = getServlet(spec, service);
    PM          pm         = new PM(this.getClass(), serviceKey);

    try {
      if ( serv == null ) {
        System.err.println("No service found for: " + serviceKey);
      } else {
        serv.service(req, resp);
      }
    } catch (Throwable t) {
      System.err.println("Error serving " + serviceKey + " " + path);
      t.printStackTrace();
    } finally {
      if ( ! serviceKey.equals("static") ) pm.log(x_);
    }
  }

  protected HttpServlet getServlet(NSpec spec, Object service) {
    if ( spec == null ) return null;

    if ( ! handlerMap_.containsKey(spec.getName()) ) {
      handlerMap_.put(spec.getName(), createServlet(spec, service));
    }

    return handlerMap_.get(spec.getName());
  }

  protected HttpServlet createServlet(NSpec spec, Object service) {
    informService(service, spec);

    if ( spec.getServe() ) {
      try {
        Class cls = spec.getBoxClass() != null && spec.getBoxClass().length() > 0 ?
            Class.forName(spec.getBoxClass()) :
            DAOSkeleton.class ;
        Skeleton skeleton = (Skeleton) cls.newInstance();

        // TODO: create using Context, which should do this automatically
        if ( skeleton instanceof ContextAware ) ((ContextAware) skeleton).setX(getX());

        informService(skeleton, spec);

        skeleton.setDelegateObject(service);

        service = new ServiceWebAgent(service, skeleton, spec.getAuthenticate());
        informService(service, spec);
      } catch (IllegalAccessException | InstantiationException | ClassNotFoundException ex) {
        ex.printStackTrace();
        ((Logger) getX().get("logger")).error("Unable to create NSPec servlet: " + spec.getName());
      }
    }

    if ( service instanceof WebAgent ) {
      service = new WebAgentServlet((WebAgent) service);
      informService(service, spec);
    }

    if ( service instanceof HttpServlet ) return (HttpServlet) service;

    Logger logger = (Logger) getX().get("logger");
    logger.error(this.getClass(), spec.getName() + " does not have a HttpServlet.");
    return null;
  }

  protected void informService(Object service, NSpec spec) {
    if ( service instanceof ContextAware ) ((ContextAware) service).setX(getX());
    if ( service instanceof NSpecAware   ) ((NSpecAware) service).setNSpec(spec);
  }

  @Override
  public void start() {

  }

  @Override
  public X getX() {
    return x_;
  }

  @Override
  public void setX(X x) {
    x_ = x;
  }
}

@@ -424,16 +424,44 @@
 .pm.PM;%0A
+import java.io.PrintWriter;%0A
 import j
@@ -851,26 +851,23 @@
 String, 
-HttpServle
+WebAgen
 t%3E handl
@@ -923,24 +923,37 @@
   protected 
+synchronized 
 void service
@@ -1002,16 +1002,20 @@
 se resp)
+%0A   
  throws 
@@ -1043,16 +1043,18 @@
 xception
+%0A 
  %7B%0A    S
@@ -1367,27 +1367,27 @@
 y);%0A    
-HttpServlet
+WebAgent   
  serv   
@@ -1395,22 +1395,23 @@
    = get
-Servle
+WebAgen
 t(spec, 
@@ -1483,24 +1483,63 @@
 rviceKey);%0A%0A
+    resp.setContentType(%22text/html%22);%0A%0A
     try %7B%0A  
@@ -1658,30 +1658,157 @@
     
-serv.service(req, resp
+X y = getX().put(HttpServletRequest.class, req).put(HttpServletResponse.class, resp).put(PrintWriter.class, resp.getWriter());%0A        serv.execute(y
 );%0A 
@@ -2037,29 +2037,27 @@
 ted 
-HttpServlet getServle
+WebAgent getWebAgen
 t(NS
@@ -2221,22 +2221,23 @@
 , create
-Servle
+WebAgen
 t(spec, 
@@ -2319,26 +2319,23 @@
 ted 
-HttpServle
+WebAgen
 t create
 Serv
@@ -2330,22 +2330,23 @@
 t create
-Servle
+WebAgen
 t(NSpec 
@@ -3268,32 +3268,35 @@
 %0A      %7D%0A    %7D%0A%0A
+/*%0A
     if ( service
@@ -3414,24 +3414,31 @@
 spec);%0A    %7D
+%0A    */
 %0A%0A    if ( s
@@ -3455,26 +3455,23 @@
 tanceof 
-HttpServle
+WebAgen
 t ) retu
@@ -3474,26 +3474,23 @@
 return (
-HttpServle
+WebAgen
 t) servi
@@ -3614,26 +3614,23 @@
  have a 
-HttpServle
+WebAgen
 t.%22);%0A  

Switch NanoRouter to be WebAgent rather than Servlet based.