/*
 * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
 *
 * The contents of this file are subject to the CCM Public
 * License (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the
 * License at http://www.redhat.com/licenses/ccmpl.html.
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language
 * governing rights and limitations under the License.
 *
 */
package com.arsdigita.webdevsupport;

import com.arsdigita.developersupport.DeveloperSupportListener;
import com.arsdigita.dispatcher.RequestEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ListIterator;


/**
 * WebDevSupport
 *   DeveloperSupportListener for Web Development Support package.
 *  <p>
 *    
 *  </p>
 * @author Joseph A. Bank (jbank@alum.mit.edu)
 * @version 1.0
 **/
public class WebDevSupport extends DeveloperSupportListener {
    public static final String versionId = "$Id: //core-platform/dev/src/com/arsdigita/webdevsupport/WebDevSupport.java#10 $ by $Author: dennis $, $DateTime: 2004/04/07 16:07:11 $";

    private static WebDevSupport s_instance;
    public static synchronized WebDevSupport getInstance() {
        if (s_instance == null) {
            s_instance = new WebDevSupport();
        }
        return s_instance;
    }

    private WebDevSupport() {
        //empty for now
    }

    private static int s_max_requests = 100;
    public void setMaxRequests(int max_requests) {
        s_max_requests = max_requests;
    }

    public int getMaxRequests() {
        return s_max_requests;
    }

    //We store a HashTable that maps Threads to their most recent
    //request object.  This gets cleaned up when requests end
    private HashMap m_threadRequestMap = new HashMap();
    private ArrayList m_requests = new ArrayList();

    private synchronized void registerNewRequest(RequestInfo ri) {
        m_threadRequestMap.put(Thread.currentThread(), ri);
        m_requests.add(ri);
        if (s_max_requests != -1) {
            int to_remove = m_requests.size() - s_max_requests;
            //this is kindof expensive, but usually just one at a time
            for (int i = 0; i<to_remove; i++) {
                m_requests.remove(0);
            }
        }
    }

    synchronized void clearRequestHistory() {
        m_requests.clear();
    }

    private RequestInfo getCurrentRequest() {
        return (RequestInfo)m_threadRequestMap.get(Thread.currentThread());
    }

    private synchronized void unRegisterRequest() {
        m_threadRequestMap.remove(Thread.currentThread());
    }

    public ListIterator getRequestsReverse() {
        //need to copy the requests to allow the iterator
        //to work in spite of concurrent modifications
        ArrayList lst = (ArrayList)m_requests.clone();
        return lst.listIterator(lst.size());
    }

    public ListIterator getRequests() {
        //need to copy the requests to allow the iterator
        //to work in spite of concurrent modifications
        ArrayList lst = (ArrayList)m_requests.clone();
        return lst.listIterator();
    }

    public RequestInfo getRequest(int id) {
        Iterator iter = m_requests.iterator();
        while (iter.hasNext()) {
            RequestInfo ri = (RequestInfo)iter.next();
            if (ri.getID() == id) {
                return ri;
            }
        }
        return null;
    }


    /**
     * requestStart
     * Callback indicating a new request has started.
     * Request is an opaque pointer for now for linkage purposes (don't want
     * to have dependencies on the dispatcher here) and for making this
     * infrastructure more general.
     */
    public void requestStart(Object request) {
        if (request instanceof RequestEvent && getCurrentRequest() == null) {
            registerNewRequest(new RequestInfo((RequestEvent)request));
        }
    }

    /**
     * requestAddProperty
     * Add a new property about this request.
     */
    public void requestAddProperty(Object request, String property, Object value) {
        RequestInfo ri = getCurrentRequest();
        if (ri != null) {
            ri.addProperty(property, value);
        }
    }

    /**
     * requestEnd
     * Callback indicating the request ended
     */
    public void requestEnd(Object request) {
        RequestInfo ri = getCurrentRequest();
        if (ri != null) {
            ri.finish();
        }
        unRegisterRequest();
    }

    /**
     * logQuery
     * Callback logging a database query
     */
    public void logQuery(int connection_id,
                         String type,
                         String query,
                         HashMap bindvars,
                         long time,
                         java.sql.SQLException sqle) {
        RequestInfo ri = getCurrentRequest();
        if (ri != null) {
            ri.logQuery(new QueryInfo(ri.numQueries()+1,
                                      connection_id,
                                      type,
                                      query,
                                      bindvars,
                                      time,
                                      sqle));
        }
    }

    /**
     * logComment
     * Log a generic comment
     */
    public void logComment(String comment) {
        RequestInfo ri = getCurrentRequest();
        if (ri != null) {
            ri.logComment(comment);
        }
    }

    /**
     * startStage
     * Callback indicating a new stage has started.
     * Stages can be used to log help mark the time
     * taken to perform various parts of requests.
     */
    public void startStage(String stagename) {
        RequestInfo ri = getCurrentRequest();
        if (ri != null) {
            ri.startStage(stagename);
        }
    }

    /**
     * endStage
     * Callback indicating a stage has ended.
     * Stages can be used to log help mark the time
     * taken to perform various parts of requests.
     */
    public void endStage(String stagename) {
        RequestInfo ri = getCurrentRequest();
        if (ri != null) {
            ri.endStage(stagename);
        }
    }

}
