import java.util.*;

/** Base class for customizing REST API handlers.  
    Create a subclass of <code>RestApiModel</code> that defines <em>Handler</em>
    classes (subclasses of <code>RestApiHandler</code>, which have custom 
    handler methods <code>doGet()</code>, <code>doPost()</code>, etc.) to 
    handle the four conventional REST requests:
    <ul>
    <li><code>POST</code> for a <em>Create</em> service</li>
    <li><code>GET</code> for a <em>Read</em> service</li>
    <li><code>PATCH</code> for an <em>Update</em> service</li>
    <li><code>DELETE</code> for a <em>Delete</em> service</li>
    </ul> 
    We refer to the  <em>Model</em> classes, since they typically 
    interact with database tables (the term "Model" refers to the 
    Model/View/Control pattern for UI programming).  <em>Locate database 
    interactions for API calls in model and handler objects</em>;  other 
    concerns such as network communications, details of parsing requests and 
    constructing replies, finding the right handler method for an HTTP request, 
    etc., are implemented elsewhere. */


public class RestApiModel {
  /** if non-null, print debug output prefixed by <code>DEBUG</code> */
  private static String DEBUG = "DEVEL ";

  /** data structure of all Handler objects.  See <code>addHandler()</code> 
      to add a handler to this data structure. */
  public Hashtable<String, RestApiHandler> handlers = initHandlers();
    
  /** helper method for constructing a new <code>RestApiModel</code> object 
      @return An initial value for <code>handlers</code> */
  private Hashtable<String, RestApiHandler> initHandlers() {
    Hashtable<String, RestApiHandler> tmp = 
      new Hashtable<String, RestApiHandler>();
    RestApiHandler h = new RestApiHandler();
    h.recordApi("", false);
    tmp.put("", h);
    return tmp;
  }

  /** Respond to a parsed REST-style HTTP request.  
      @param p Parser object <em>after</em> parsing a new HTTP request
      @return HTTP reply message for that request, according to the API */
  public String handle(HttpParser p) {
    RestApiHandler h = 
      getHandler(makeKey(p.getURLBase(), !p.getURLId().equals("")));
    if (DEBUG != null) {
      System.out.println(DEBUG + "handle():  " + h);
      System.out.println(DEBUG + "handle():  " + p.getMethod());
      System.out.println(DEBUG + "handle() urlId:  \"" + p.getURLId()+ "\"");
    }
    if (p.getMethod().equals("POST"))
      return h.doPost(p);
    else if (p.getMethod().equals("GET"))
      return h.doGet(p);
    else if (p.getMethod().equals("PATCH"))
      return h.doPatch(p);
    else if (p.getMethod().equals("DELETE"))
      return h.doDelete(p);
    else // method not supported
      return p.makeReply(501, "Unsupported HTTP method " + p.getMethod() + ".");
  }

  /** helper method for generating Hashtable keys for <code>handlers</code> 
  data structure */
  private String makeKey(String url, boolean id) {
    return new String(url + (id ? " #" : ""));
  }

  /** helper method for generating Hashtable keys for <code>handlers</code> 
  data structure */
  private String makeKey(String url) {
    return url;
  }

  /** Add or replace a handler for a new API component.  
      @param url Base URL for the new API component
      @param id True if API component expects a final integer path component 
      (typically representing a record ID number), false if no such final 
      integer should be used 
      @param handlr Handler object providing methods such as 
      <code>doGet()</code>, <code>doPost()</code>, etc., for implementing a 
      REST-style API.  <code>handlr</code> is typically an object in a subclass 
      of <code>RestApiHandler</code> that implements some or all of those 
      four <code>doX()</code> methods.  */  

  public void addHandler(String url, boolean id, RestApiHandler handlr) {
    handlr.recordApi(url, id);
    handlers.put(makeKey(url,id), handlr);
  }

  /** Add or replace a handler for a new API component without a final integer
      in the URL.  
      @param url Base URL for the new API component
      @param handlr Handler object providing methods such as 
      <code>doGet()</code>, <code>doPost()</code>, etc., for implementing a 
      REST-style API.  <code>handlr</code> is typically an object in a subclass 
      of <code>RestApiHandler</code> that implements some or all of those 
      four <code>doX()</code> methods.  */  

  public void addHandler(String url, RestApiHandler handlr) {
    addHandler(url, false, handlr);
  }

  /** Retrieve a handler object for a particular API URL, with or without
      a final integer ID component
      @param url Base URL for an API request
      @param id True if API component expects a final integer path component 
      (typically representing a record ID number), false if no such final 
      integer should be used 
      @return Handler object for that <code>url</code>/<code>id</code> 
      combination, or default handler (no services implemented) if no such 
      handler exists in <code>handlers</code> data structure */

  public RestApiHandler getHandler(String url, boolean id) {
    RestApiHandler h = handlers.get(makeKey(url,id));
    if (h == null) {
      System.out.println("Warning: REST API handler for URL \"" + url +
			 (id ? "/#" : "") + "\"\n" + 
			 "not found, using default handler");
      h = handlers.get("");
    }
    return h;
  }
  
  /** Retrieve a handler object for a particular API URL, with no final 
      integer ID component
      @param url Base URL for an API request
      @return Handler object for that <code>url</code> and no final integer ID 
      component, or default handler (no services implemented) if no such 
      handler exists in <code>handlers</code> data structure */

  public RestApiHandler getHandler(String url) {
    return getHandler(url, false);
  }

  /** retrieve all keys in <code>handlers</code> data structure.
      @return A space-separated list of keys in the <code>handlers</code> 
      data structure.  If an API url expects a final
      integer component in its path, the base URL is followed by a space and 
      the character <code>#</code> in this return string.  */

  public String getHandlerKeys() {
    String ret = new String();
    for (Enumeration<String> e = handlers.keys(); 
	 e.hasMoreElements(); ) {
      ret += " " + e.nextElement();
    }
    
    return ret;
  }

  /** Retrieve String representations of all handler values stored in 
      <code>handlers</code> data structure.
      @return A space-separated list of string representations of the handlers
      in the <code>handlers</code> data structure.  */

  public String getHandlerValues() {
    String ret = new String();
    for (Enumeration<RestApiHandler> e = handlers.elements(); 
	 e.hasMoreElements(); ) {
      ret += " " + e.nextElement();
    }
    
    return ret;
  }
}