/*
Copyright (C) 2000-2010  Ministere de la culture et de la communication (France), AJLSM
See LICENCE file
*/
package fr.gouv.culture.oai;

import fr.gouv.culture.oai.util.OAIUtilities;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.cocoon.environment.Request;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import java.util.Enumeration;

public class OAIRequestImpl extends OAIObjectImpl implements OAIRequest {

    /**The full request url*/
    protected String requestURL = null;
    /**The repository's url*/
    protected String repoURL = null;
    /**The <code>int</code> verb action representation*/
    protected int verb = VERB_UNKNOWN;//defaulted
    /**The <code>String</code> verb action representation*/
    protected String verbString = "";
    /**Request param*/
    protected String identifier = null;
    /**Request param*/
    protected String metadataPrefix = null;
    /**Request param*/
	String verbId = null;
    /**Request param*/
    protected boolean useLastHarvestDate = true;
    /**Request param, default granularity is second*/
    protected String granularity = OAIObject.Node.Value.STRING_GRANULARITY_SECOND;
    /**Request param*/
    protected String from = null;
    /**Request param*/
    protected String until = null;
    /**Request param*/
    protected String setIdentifier = null;
    /**Request param*/
    protected String resumptionToken = null;
    /**The request object from cocoon*/
    protected Request cocoonRequest = null;

    /**Retrieves the <code>int</code> representation of the "verb" request paraameter*/
    public int getVerb() {
        return this.verb;
    }


    /**Establishes the <code>int</code> representation of the "verb" request parameter*/
    public void setVerb(int verb) {
        this.verb = verb;
        String verbStr = null;
        switch (this.verb) {
            case VERB_IDENTIFY:
                verbStr = VERB_STRING_IDENTIFY;
                break;
            case VERB_LIST_SETS:
                verbStr = VERB_STRING_LIST_SETS;
                break;
            case VERB_LIST_METADATA_FORMATS:
                verbStr = VERB_STRING_LIST_METADATA_FORMATS;
                break;
            case VERB_LIST_IDENTIFIERS:
                verbStr = VERB_STRING_LIST_IDENTIFIERS;
                break;
            case VERB_LIST_RECORDS:
                verbStr = VERB_STRING_LIST_RECORDS;
                break;
            case VERB_GET_RECORD:
                verbStr = VERB_STRING_GET_RECORD;
                break;
        }

        this.verbString = verbStr;
    }

    /**Retrieves the request param*/
    public String getIdentifier() {
        return identifier;
    }

    /**Establishes the request param*/
    public void setIdentifier(String identifier) {
        this.identifier = identifier;
    }

    /**Retrieves the request param*/
	public String getVerbId() {
        return verbId;
    }

    /**Establishes the request param*/
    public void setVerbId(String verbId) {
        this.verbId = verbId;
    }


    /**Retrieves the request param*/
    public String getMetadataPrefix() {
        return metadataPrefix;
    }

    /**Establishes the request param*/
    public void setMetadataPrefix(String metadataPrefix) {
        this.metadataPrefix = metadataPrefix;
    }

	/**Gets the parameter of the request*/
    public boolean getUseLastHarvestDate(){
    	return useLastHarvestDate;
    }

    /**Sets the parameter of the request*/
    public void setUseLastHarvestDate(boolean useLastHarvestDate){
    	this.useLastHarvestDate = useLastHarvestDate;
    }

    /**Gets the parameter of the request*/
    public String getGranularity(){
    	return granularity;
    }

    /**Sets the parameter of the request*/
    public void setGranularity(String granularity){
    	this.granularity = granularity;
    }

    /**Retrieves the request param*/
    public String getFrom() {
        return from;
    }

    /**Establishes the request param*/
    public void setFrom(String fromDate) {
        this.from = fromDate;
    }

    /**Retrieves the request param*/
    public String getUntil() {
        return until;
    }

    /**Establishes the request param*/
    public void setUntil(String until) {
        this.until = until;
    }

    /**Retrieves the request param*/
    public String getSetIdentifier() {
        return setIdentifier;
    }

    /**Establishes the request param*/
    public void setSetIdentifier(String setIdentifier) {
        this.setIdentifier = setIdentifier;
    }

    /**Retrieves the request param*/
    public String getResumptionToken() {
        return resumptionToken;
    }

    /**Establishes the request param*/
    public void setResumptionToken(String resumptionToken) {
        this.resumptionToken = resumptionToken;
    }


    /**Retrieves the request url*/
    public String getRequestURL() {
        if (OAIUtilities.checkString(this.requestURL))
            return this.requestURL;
        else {
            String rUrl = "";
            Parameters params = this.getParameters();
            if (params != null) {
                if (OAIUtilities.checkString(this.repoURL)) {
                    rUrl += this.repoURL;
                    String[] paramNames = params.getNames();
                    for (int i = 0; i < paramNames.length; i++) {
                        String paramName = paramNames[i];
                        if (OAIUtilities.checkString(paramName)) {
                            String paramVal = params.getParameter(paramName, "");
                            if (OAIUtilities.checkString(paramVal)) {
                                if (i == 0)
                                    rUrl += URL_CHARACTER_QUESTION_MARK + paramName + OAIRequest.URL_CHARACTER_EQUALS + paramVal;
                                else
                                    rUrl += URL_CHARACTER_AMPERSAND + paramName + OAIRequest.URL_CHARACTER_EQUALS + paramVal;

                            }
                        }
                    }
                }
            }

            return rUrl;
        }
    }

    /**Establishes the request url*/
    public void setRequestURL(String requestURL) {
        this.requestURL = requestURL;
    }

    /**Retrieves the <code>String</code> representation of the "verb" request paraameter*/
    public String getVerbString() {
        return verbString;
    }

    /**Establishes the <code>String</code> representation of the "verb" request paraameter*/
    public void setVerbString(String verbString) {
        this.verbString = verbString;

        int verbInt = VERB_UNKNOWN;

        if (VERB_STRING_IDENTIFY.equals(this.verbString))
            verbInt = VERB_IDENTIFY;
        else if (VERB_STRING_LIST_SETS.equals(this.verbString))
            verbInt = VERB_LIST_SETS;
        else if (VERB_STRING_LIST_METADATA_FORMATS.equals(this.verbString))
            verbInt = VERB_LIST_METADATA_FORMATS;
        else if (VERB_STRING_LIST_IDENTIFIERS.equals(this.verbString))
            verbInt = VERB_LIST_IDENTIFIERS;
        else if (VERB_STRING_LIST_RECORDS.equals(this.verbString))
            verbInt = VERB_LIST_RECORDS;
        else if (VERB_STRING_GET_RECORD.equals(this.verbString))
            verbInt = VERB_GET_RECORD;
        else
            verbInt = VERB_UNKNOWN;

        this.verb = verbInt;
    }


    /**Retrieves the repository url*/
    public String getRepositoryURL() {
        return this.repoURL;
    }

    /**Establishes the repository url*/
    public void setRepositoryURL(String url) {
        this.repoURL = url;
    }

    /**Retrives the underlying cocoon request object*/
    public Request getRequest() {
        return this.cocoonRequest;
    }

    /**Establishes the underlying cocoon request object*/
    public void setRequest(Request coocoonRequest) {
        this.cocoonRequest = coocoonRequest;
        if (this.cocoonRequest != null) {
            String repoUrl = cocoonRequest.getScheme() + "://" + cocoonRequest.getServerName() + ":" + cocoonRequest.getServerPort() + cocoonRequest.getContextPath() + cocoonRequest.getServletPath();
            String requestUrl = repoUrl + URL_CHARACTER_QUESTION_MARK + cocoonRequest.getQueryString();
            this.setRepositoryURL(repoUrl);
            this.setRequestURL(requestUrl);
            this.setParameters();//populating the class fields
        }

    }

    /**Sets the request parameter class fields from the cocoon request object*/
    protected void setParameters() {
        if (this.cocoonRequest != null) {
            Enumeration paramNames = this.cocoonRequest.getParameterNames();
            if (paramNames != null) {
                while (paramNames.hasMoreElements()) {
                    String paramName = (String) paramNames.nextElement();
                    String paramVal = this.cocoonRequest.getParameter(paramName);
                    paramName = OAIUtilities.normalizeHttpRequestParameterName(paramName);

                    if (OAIUtilities.checkString(paramName) && OAIUtilities.checkString(paramVal)) {
                        if (URL_PARAM_NAME_IDENTIFIER.equals(paramName))
                            setIdentifier(paramVal);
                        else if (URL_PARAM_NAME_METADATA_PREFIX.equals(paramName))
                            setMetadataPrefix(paramVal);
                        else if (URL_PARAM_NAME_SET.equals(paramName))
                            setSetIdentifier(paramVal);
                        else if (URL_PARAM_NAME_FROM.equals(paramName))
                            setFrom(paramVal);
                        else if (URL_PARAM_NAME_UNTIL.equals(paramName))
                            setUntil(paramVal);
                        else if (URL_PARAM_NAME_RESUMPTION_TOKEN.equals(paramName))
                            setResumptionToken(paramVal);
                        else if (URL_PARAM_NAME_VERB.equals(paramName))
                            setVerbString(paramVal);

                    }
                }
            }
        }
    }


    public void toSAX(ContentHandler contentHandler) throws SAXException {
        this.toSAX(contentHandler, true);
    }

    /**Sends the appropriate SAX event reprensentation
     * of the request for an oai response to the
     * provide handler
     *
     * @param handler   The handler to feed
     * @param sendRequestParams Whether or not the request params should be sent as attributes (according to error state)
     * @throws SAXException
     */
    public void toSAX(ContentHandler handler, boolean sendRequestParams) throws SAXException {
        //something like
        //<request verb="GetRecord" identifier="oai:arXiv.org:hep-th/9901001"
        //        metadataPrefix="oai_dc">http://an.oa.org/OAI-script</request>
        String repoUrl = this.getRepositoryURL();
        if (!OAIUtilities.checkString(repoUrl)) {
            String requestUrl = this.getRequestURL();
            int endIdx = requestUrl.indexOf(OAIRequest.URL_CHARACTER_QUESTION_MARK);
            if (endIdx < 0) endIdx = requestUrl.length();
            repoUrl = requestUrl.substring(0, endIdx);
        }
        else {
            ContentHandler currentHandler = super.contentHandler;
            //changing the handler for the duration of the events
            super.setContentHandler(handler);

            AttributesImpl atts = new AttributesImpl();

            if (sendRequestParams) {//these should not be sent in certain badArgument error cases
                Parameters params = this.getParameters();
                if (params != null) {
                    String[] paramNames = params.getNames();
                    for (int i = 0; i < paramNames.length; i++) {
                        String paramName = paramNames[i];
                        if (OAIUtilities.checkString(paramName)) {
                            String paramVal = params.getParameter(paramName, "");
                            if (OAIUtilities.checkString(paramVal))
                                atts.addAttribute("", paramName, paramName, OAIObject.Node.Type.CDATA, paramVal);
                        }
                    }
                }
            }

            //NOTE: DEBUG XALAN: START
            /*old code*/
            super.startElement(OAIObject.Node.Xmlns.OAI_2_0, OAIObject.Node.Name.REQUEST, OAIObject.Node.Name.REQUEST, atts);
            super.sendElementContent(repoUrl);
            super.endElement(OAIObject.Node.Xmlns.OAI_2_0, OAIObject.Node.Name.REQUEST, OAIObject.Node.Name.REQUEST);
            /*new code
            super.startElement("", OAIObject.Node.Name.REQUEST, OAIObject.Node.Name.REQUEST, atts);
            super.sendElementContent(repoUrl);
            super.endElement("", OAIObject.Node.Name.REQUEST, OAIObject.Node.Name.REQUEST);
            */
            //NOTE: DEBUG XALAN: END

            super.setContentHandler(currentHandler);
        }


    }

    /**Returns the request parameters in a populated Avalon Parameters object*/
    protected Parameters getParameters() {
        Parameters params = new Parameters();
        Request httpRequest = this.cocoonRequest;
        Enumeration paramNames = null;
        if (httpRequest != null)
            paramNames = httpRequest.getParameterNames();

        if (paramNames != null) {
            while (paramNames.hasMoreElements()) {
                String paramName = (String) paramNames.nextElement();
                if (OAIUtilities.checkString(paramName)) {
                    String paramVal = httpRequest.getParameter(paramName);
                    if (OAIUtilities.checkString(paramVal)) {
                        paramName = OAIUtilities.normalizeHttpRequestParameterName(paramName);
                        params.setParameter(paramName, paramVal);
                    }
                }
            }
        } else {
            if (OAIUtilities.checkString(this.verbString))
                params.setParameter(URL_PARAM_NAME_VERB, this.verbString);
            if (OAIUtilities.checkString(this.identifier))
                params.setParameter(URL_PARAM_NAME_IDENTIFIER, this.identifier);
            if (OAIUtilities.checkString(this.metadataPrefix))
                params.setParameter(URL_PARAM_NAME_METADATA_PREFIX, this.metadataPrefix);
            if (OAIUtilities.checkString(this.from))
                params.setParameter(URL_PARAM_NAME_FROM, this.from);
            if (OAIUtilities.checkString(this.until))
                params.setParameter(URL_PARAM_NAME_UNTIL, this.until);
            if (OAIUtilities.checkString(this.setIdentifier))
                params.setParameter(URL_PARAM_NAME_SET, this.setIdentifier);
            if (OAIUtilities.checkString(this.resumptionToken))
                params.setParameter(URL_PARAM_NAME_RESUMPTION_TOKEN, this.resumptionToken);
        }

        return params;
    }


}
