package com.jamonapi.http;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;


/**
 *  
 *    ?test  - use ms. units for otehr methods like response.getStatus().value.ms
 *    webapp
- documentation adn sample xml
- check resultsetconverter to work with new bufferlistener
 *  - comment any method that returns an Object and takes no args can be passed.

 */

/** Base class that monitors a httpServletRequest, and HttpServletResponse by returning an HttpMonRequest object per page request.  Although this
 * class can be used directly it will more often be used transparently by classes such as JAMonTomcat55Valve, JAMonServletFilter and JAMonJettyHandler.
 * To get a list of many possible (though not all) HttpServletRequest and HttpServletResponse labels pass 'demo' to the valve, handler or servlet filter.  
 * You can also see these same possibiliteis by calling HttpMonFactory.getDemoLabels().  Note jetty and tomcat handler and valve respectively 
 * are based on objects that inherit from HttpServletRequest and HttpServletResponse respectively and all methods that these subclasses 
 * implement are available too.  
 * 
 * <br> representative values returned from response methods
 * <br> response.getBufferSize()=8192
   <br> response.getCharacterEncoding()=utf-8
   <br> response.getContentCount()=985
   <br> response.getContentType()=text/html;charset=utf-8
   <br> response.getLocale()=en_US
    
 <br><br>representative values returned from request methods
            <br> request.getAuthType()=null
            <br> request.getCharacterEncoding()=null
            <br>  request.getContentLength()=-1
            <br>  request.getCharacterEncoding()=null
            <br>  request.getContentType()=null
            <br>  request.getContextPath()=/jamon
            <br>  request.getLocalAddr()=127.0.0.1
            <br>  request.getLocale()=en_US
            <br>  request.getLocalName()=localhost
            <br>  request.getLocalPort()=8080
            <br>  request.getMethod()=GET
            <br>  request.getPathInfo()=null
            <br>  request.getPathTranslated()=null
            <br>  request.getProtocol()=HTTP/1.1
            <br>  request.getQueryString()=name=steve%20souza&id=9898
            <br>  request.getRemoteAddr()=127.0.0.1
            <br>  request.getRemoteHost()=127.0.0.1
            <br>  request.getRemotePort()=1454
            <br>  request.getRemoteUser()=null
            <br>  request.getRequestedSessionId()=670BFE2B4A7C7C77D9825EFA753D2058
            <br>  request.getRequestURI()=/jamon/ZZZZ
            <br>  request.getRequestURL()=http://localhost:8080/jamon/ZZZZ
            <br>  request.getScheme()=http
            <br>  request.getServerName()=localhost
            <br>  request.getServerPort()=8080
            <br>  request.getServletPath()=/ZZZZ
            <br>  request.getUserPrincipal()=null
            <br>  request.isRequestedSessionIdFromCookie()=true
            <br>  request.isRequestedSessionIdFromURL()=false
            <br>  request.isRequestedSessionIdValid()=false
            <br>  request.isSecure()=false
   
 * 
 * @author steve souza
 */
public class HttpMonFactory implements HttpMonManage {

    private static final String DEFAULT_SUMMARY="request.getRequestURI().ms as allPages, request.getRequestURI().value.ms as page";
    private String jamonSummaryLabels="default";// will do the above monitors if the word default is used in this variable.
    private Collection httpMonItemsHolder=new ArrayList();// Holds HttpMonItems
    private boolean ignoreHttpParams=true;// ignore http params if getRequestURI, or getRequestURL are called.  This done to primarily to prevent
                                            // jsessionid from becoming part of a jamon label.  By default params are removed (i.e. true)
    private String labelPrefix; // prefix used for jamon labels
    private boolean enabled=true; //Enable/Disable httpMonitoring. By default it is on 
    private int numTimeMons=0;// The Number of monitors that are time based ones.
    // The size value will not allow any more http stats to be put into jamon if the total number of jamon entries exceeds 5000 entries.
    // This value may be changed to anything.  Note jamon entries can still be added via standard jamon calls (might want to add this feature 
    // there too).  This is to prevent a buffer overflow should someone keep submitting invalid pages when a record is created for each page.
    private int size=5000;
    private static final HttpMon NULL_HTTP_MON=new HttpMonNull();// used when monitoring is disabled.
    
    /** Create an HttpMonFactory by passing in text that occurs at the beginning of all jamon labels. ex com.jamonapi.http.JAMonTomcatValve */
    public HttpMonFactory(String labelPrefix) {
        this.labelPrefix=labelPrefix;
    }
    
    
    
    
    /** Pass a series of things (HttpServletRequest/HttpServletResponse methods) to monitor.  If the word 'default' is passed then the default
     * values will be used.  If 'demo' is passed you will see a list of possibilities (This should not be done in production as too much data would be
     * generated).  You can add to the default by doing passing "default, request.getStatus().httpStatus". Each time this method is called any previously
     * set items that were being monitored will not longer be monitored.  See http://www.jamonapi.com for more examples.
     */
    public void setSummaryLabels(String jamonSummaryLabels) {
        this.jamonSummaryLabels="";
        this.httpMonItemsHolder=new ArrayList();
        this.numTimeMons=0;
        
        if (jamonSummaryLabels==null)
          return;
 
        // replace the word 'demo' with a good sampling of possibilities
        jamonSummaryLabels=jamonSummaryLabels.replaceAll("(?i)demo", getDemoLabels());
 
        // replace string 'default' with the actual default values
        jamonSummaryLabels=replaceDefault(jamonSummaryLabels, DEFAULT_SUMMARY);
        
        // tokenize the string and add each of the HttpMonItems
        String[] summaryLabelsArr=split(jamonSummaryLabels);
        for (int i=0;i<summaryLabelsArr.length;i++) {
            addSummaryLabel(summaryLabelsArr[i]);
        }

    }
    
   
    public static String getDemoLabels() {
         
        String demoStr="";
        
        // response methods
        demoStr+=getDemoLabel("response.getBufferSize()","bytes");
        demoStr+=getDemoLabel("response.getCharacterEncoding()","charEncoding");
        demoStr+=getDemoLabel("response.getContentCount()","bytes");
        demoStr+=getDemoLabel("response.getContentType()","contentType");
        demoStr+=getDemoLabel("response.getLocale()","locale");
        demoStr+=getDemoLabel("request.getAuthType()","authType");
        
        demoStr+=getDemoLabel("request.getCharacterEncoding()","charEncoding");
        demoStr+=getDemoLabel("request.getContentLength()","bytes");
        demoStr+=getDemoLabel("request.getContentType()","contentType");
        demoStr+=getDemoLabel("request.getContextPath()","path");
        demoStr+="request.getContextPath().value.ms, ";
        demoStr+=getDemoLabel("request.getLocalAddr()","ip");
        demoStr+=getDemoLabel("request.getLocale()","locale");
        demoStr+=getDemoLabel("request.getLocalName()","localName");
        demoStr+=getDemoLabel("request.getLocalPort()","port");
        demoStr+=getDemoLabel("request.getMethod()","httpMethod");
        demoStr+=getDemoLabel("request.getPathInfo()","path");
        demoStr+=getDemoLabel("request.getPathTranslated()","path");
        demoStr+=getDemoLabel("request.getProtocol()","protocol");
        demoStr+=getDemoLabel("request.getQueryString()","queryStr");
        demoStr+=getDemoLabel("request.getRemoteAddr()","ip");
        demoStr+=getDemoLabel("request.getRemoteHost()","ip");
        demoStr+=getDemoLabel("request.getRemotePort()","port");
        demoStr+=getDemoLabel("request.getRemoteUser()","user");
        demoStr+=getDemoLabel("request.getRequestedSessionId()","sessionid");
        demoStr+=getDemoLabel("request.getRequestURI()","ms");
        demoStr+=getDemoLabel("request.getRequestURL()","ms");
        demoStr+=getDemoLabel("request.getScheme()","scheme");
        demoStr+=getDemoLabel("request.getServerName()","serverName");
        demoStr+=getDemoLabel("request.getServerPort()","port");
        demoStr+=getDemoLabel("request.getServletPath()","path");
        demoStr+=getDemoLabel("request.getUserPrincipal()","user");
        demoStr+=getDemoLabel("request.isRequestedSessionIdFromCookie()","sessionCookie");
        demoStr+=getDemoLabel("request.isRequestedSessionIdValid()","sessionValid");
        demoStr+=getDemoLabel("request.isSecure()","secure");
        demoStr+="request.getScheme().ms as schemeAlias";
          
        return demoStr;

    }
    
    private static String getDemoLabel(String label, String units) {
        String str=label+"."+units;// response.getBufferSize().bytes
        str+=", "+label+".value."+units;
        str+=", "+label+".contextpath.value."+units;
        str+=", "+label+".value.contextpath."+units;
        str+=", "+label+".value.url."+units+",";
        return str;
        
    }
   
    // if passed summaryLabel has 'default' in it replace it with defaultString and then
    // call this classes setSummaryLabels method.  This allows each implementing class to have
    // different defaults.
    public void setSummaryLabels(String summaryLabel, String defaultString) {
        summaryLabel=replaceDefault(summaryLabel,defaultString);
        setSummaryLabels(summaryLabel);
    }
    
    // replace case insensitive 'default' string with passed in string.
    static String replaceDefault(String summaryLabel, String defaultString) {
       return summaryLabel.replaceAll("(?i)default",defaultString);
    }
    
    /** Pass a String that has an HttpServletRequest/HttpServletResponse method such as response.getStatus() and then this class
     * will monitor that method call.
     */
    public void addSummaryLabel(String jamonSummaryLabel) {
        if (httpMonItemsHolder!=null) {
            HttpMonItem monItem=createHttpMonItem(jamonSummaryLabel.trim());
            httpMonItemsHolder.add(monItem); 
            // concatenate each individual summary label to build a bigger string.  i.e response.getStatus(), request.getRequestURI()
            // Put a comma before every label added but the first.
            if (!"".equals(jamonSummaryLabels)) {
                jamonSummaryLabels+=", ";
            }
            
            jamonSummaryLabels+=jamonSummaryLabel;
            if (monItem.isTimeMon())// track how many time monitors there are
              numTimeMons++;  
        }
    }
    
    // Return the number of HttpMonItems that are being monitored for each request
    int getNumRows() {
        return httpMonItemsHolder.size();
    }
    
    // return the number of time monitors (ms.)
    int getNumTimeMons() {
        return numTimeMons;     
    }
    
    // allow iteration of HttpMonItems.  Used to start/stop all monitors.
    Iterator iter() {
        return httpMonItemsHolder.iterator(); 
    }
    
    /** Get the passed in summaryLabels. */
    public String getSummaryLabels() {
        return jamonSummaryLabels;
    }

    
    
    
    /** Determine if http params are ignored when creating a jamon label for request.getRequestURI(), and request.getRequestURL().  This is important
     * for jsessionid can make every label passed to jamon nonunique if not enabled.   By default this value is true (ignore params).
     */
    public boolean getIgnoreHttpParams() {
        return ignoreHttpParams;
    }


    /** Set if http params are ignored when creating a jamon label for request.getRequestURI(), and request.getRequestURL().  This is important
     * for jsessionid can make every label passed to jamon nonunique if not enabled.   By default this value is true (ignore params).
     */
    public void setIgnoreHttpParams(boolean ignoreHttpParams) {
        this.ignoreHttpParams=ignoreHttpParams;
        
    }
    

    /** Enable/disable http monitoring */
    public void setEnabled(boolean enable) {
        this.enabled=enable;
        
    }

    /** Determin if http monitoring is enabled */
    public boolean getEnabled() {
        return enabled;
   }



    /** Get the max number of possible HttpMonitors.  By default this is 5000 */
    public int getSize() {
        return size;
    }



    /** Set the max number of possible HttpMonitors.  By default this is 5000.  A value <=0 means there is no limit on the number
     * of jamon records that can be creating due to monitoring.  */
    public void setSize(int size) {
        this.size=size;
    }
    
    public String getLabelPrefix() {
        return labelPrefix;
    }

    private String[] split(String str) {
        return (str==null) ? null : str.split(",");
    }
    
    /* Note request will probably implement HttpServletRequest and response HttpServletResponse though this is not strictly required.  This method is
     * called to monitor the request.  usually via another class like the jamon tomcat valve.
     * 
     */

    public HttpMon getMon(Object request, Object response) {
        if (!enabled || jamonSummaryLabels==null)
            return NULL_HTTP_MON;
        else
            return new HttpMonRequest(request, response, this);
        
    }
    
    
    /* Method called to start monitoring a request. 
     * 
     */

    public HttpMon start(Object request, Object response) {
         return getMon(request, response).start();        
    }
    


    /* can be overridden in this package to create a different type of httpMonItem (jetty classes do this).  If it is required in the future 
     * access could change to protected for this method.
     */
    HttpMonItem createHttpMonItem(String label) {
        return new HttpMonItem(label, this);
        
    }
    




    /** Simple test code */
    public static void main(String[] argas) {
        String str="hello world, dEfault, request.getRequestURI()";
        System.out.println(str.replaceAll("(?i)default","steven, thomas, souza"));
        String[] strArr=str.split("(?i)default *,");
        for (int i=0;i<strArr.length;i++)
            System.out.println(strArr[i]);
        
        System.out.println(getDemoLabels());
        
    }


}
