/*
 *   Copyright 2003-2004 The Apache Software Foundation.
 *
 *   Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 *
 * @author Susantha Kumara (susantha@opensource.lk, skumara@virtusa.com)
 *
 */

#include "Apache2Transport.h"

#define AXIS_URI_EXTENSION "/axis"

/* Following is the character that should be used to separate the method name in
 * the SOAPAction header value. Ex: "http://localhost:80/axis/Calculator#Add"
 */
#define SOAPACTION_METHODNAME_SEPARATOR "#"

Apache2Transport::Apache2Transport(void* pContext)
{
    m_eProtocolType = APTHTTP1_1;
	m_pContext = pContext;
#ifndef CHUNCKED_DATA_SUPPORTED
    m_pBuffers = new BufferInfo[NO_OF_SERIALIZE_BUFFERS];
	memset((void*)m_pBuffers, 0, sizeof(BufferInfo)*NO_OF_SERIALIZE_BUFFERS);
#endif

	
}

Apache2Transport::~Apache2Transport()
{
	if(m_pBuffers)
	{
		delete [] m_pBuffers;
		m_pBuffers = NULL;
	}

}

AXIS_TRANSPORT_STATUS Apache2Transport::sendBytes(const char* pcSendBuffer, const void* pBufferId)
{
#ifndef CHUNCKED_DATA_SUPPORTED
    int index;
#endif

    if (!pBufferId) /* temporary buffers should always sent immediately */
    {
		ap_rputs(pcSendBuffer, (request_rec*)m_pContext);
        return TRANSPORT_FINISHED;
    }

#ifdef CHUNCKED_DATA_SUPPORTED
    // Do we need to send any headers and length of this chunk ? 
	ap_rputs(pcSendBuffer, (request_rec*)m_pContext);
    /* Do we need to send any indication to mark the end of this 
     * chunk ?
     */ 
    return TRANSPORT_FINISHED;
#else
    for (index = 0; index < NO_OF_SERIALIZE_BUFFERS; index++)
    {
        if (!m_pBuffers[index].pcBuffer)
        {
            m_pBuffers[index].pcBuffer = pcSendBuffer;
            m_pBuffers[index].pBufferId = pBufferId;
            break;
        }
    }
    return TRANSPORT_IN_PROGRESS;
#endif	
}

int Apache2Transport::setTransportProperty(AXIS_TRANSPORT_INFORMATION_TYPE type, const char* value)
{
    const char* key = NULL;
    switch (type)
    {
        case SOAPACTION_HEADER:  /* needed only in the client side ? */
            break;
        case SERVICE_URI:        /* need to set ? */
            break;
        case OPERATION_NAME:     /* need to set ? */
            break;
        case SOAP_MESSAGE_LENGTH:
		/* This is apache module and transport is http so the key */ 
            key = "Content-Length"; 
            break;
/*	case CONTENT_TYPE:
	    ((request_rec*)m_pContext)->content_type = value;
	    break; */
        default:;
    }
    if (key)
    {
		ap_table_set(((request_rec*)m_pContext)->headers_out, key, value);
#ifdef CHUNCKED_DATA_SUPPORTED
		ap_send_http_header((request_rec*)m_pContext);
		/* Should we remove the sent headers ? */
#endif
	}
    return 0;
}

AXIS_TRANSPORT_STATUS Apache2Transport::flushOutput()
{
#ifndef CHUNCKED_DATA_SUPPORTED
    int contentLength = 0;
    int index;
    char strtonum[8];
#endif
#ifdef CHUNCKED_DATA_SUPPORTED
    /* headers have already been sent. see set_transport_information
     * http body too have been sent
     * Do we need to send any indication to mark end of chuncked 
     * data ?  
     */
#else
    /* Calculate Content-Length and set header */
    for (index = 0; index < NO_OF_SERIALIZE_BUFFERS; index++)
    {
        if (!m_pBuffers[index].pcBuffer)
            break;
        contentLength += strlen(m_pBuffers[index].pcBuffer);
    }
    if (contentLength != 0)     /* do only if the http body is not empty. */
    {
        sprintf(strtonum, "%d", contentLength);
        setTransportProperty(SOAP_MESSAGE_LENGTH, strtonum);
        ap_send_http_header((request_rec*)m_pContext);
        /* Send all buffers */
        for (index = 0; index < NO_OF_SERIALIZE_BUFFERS; index++)
        {
            if (!m_pBuffers[index].pcBuffer)
                break;
            ap_rputs(m_pBuffers[index].pcBuffer, (request_rec*)m_pContext);
            /* Let Axis know that the buffer is no longer in use */
            m_pReleaseBufferCallback(m_pBuffers[index].pcBuffer, 
                m_pBuffers[index].pBufferId);
        }
    }
#endif
	return TRANSPORT_FINISHED;
}

AXIS_TRANSPORT_STATUS Apache2Transport::getBytes(char* pBuffer, int* piSize)
{
	
	int nBufSize = *piSize;
    int len_read;
    len_read = ap_get_client_block((request_rec*) m_pContext, pBuffer, *piSize);
	
 	if (strstr(pBuffer, "Content-Id")) {
		pAttachmentHelper = new AttachmentHelper();
		//char *pAttachBuffer = (char*)malloc(1000);
		//char *mimeBoundary = (char*)malloc(1000);		

		pAttachmentHelper->extract_Attachment(pBuffer);		
		pAttachmentHelper->extract_SOAPMimeHeaders(pBuffer);	
		pAttachmentHelper->extract_Soap(pBuffer);
				
	}

    *piSize = len_read;

    if (len_read < nBufSize)
    {
        pBuffer[len_read] = '\0';

        return TRANSPORT_FINISHED;
    }
    else
        return TRANSPORT_IN_PROGRESS;
}

const char* Apache2Transport::getTransportProperty(AXIS_TRANSPORT_INFORMATION_TYPE eType)
{
    const char* pcValue;
	/* the member, "path" of "parsed_uri" contains the uri of the
       request (i.e "/abc/xyz" part of http://somehost/abc/xyz) */
    const char* pcUriPath =  ((request_rec*)m_pContext)->parsed_uri.path;

    switch (eType)
    {
        case SOAPACTION_HEADER:
			return getTransportProperty("SOAPAction");
        case SERVICE_URI:
            if (strstr(pcUriPath, AXIS_URI_EXTENSION))
            {
                return strstr(pcUriPath, AXIS_URI_EXTENSION) +
                    strlen(AXIS_URI_EXTENSION) + 1;
            }
            else
            {
                return pcUriPath;
            }
        case OPERATION_NAME:
            pcValue = getTransportProperty("SOAPAction");
            if (pcValue)
            {
                if (strstr(pcValue, SOAPACTION_METHODNAME_SEPARATOR))
                {
                    return strstr(pcValue, SOAPACTION_METHODNAME_SEPARATOR) +
                          strlen(SOAPACTION_METHODNAME_SEPARATOR);
                }
                else
                {
                    return pcValue;
                }
            }
        case SOAP_MESSAGE_LENGTH:
			return getTransportProperty("Content-Length");
	    /* this is apache module and transport is http so the key */
        default:;
    }
    return NULL;
}

int Apache2Transport::setTransportProperty(const char* pcKey, const char* pcValue)
{
	/*ap_table_set(((request_rec*)m_pContext)->headers_out, pcKey, pcValue);*/
	//ap_send_http_header((request_rec*)m_pContext);
    return 0;
}

const char* Apache2Transport::getTransportProperty(const char* pcKey, bool response)
{
    /* ap_table_elts returns an array_header struct. The nelts element of that 
     * struct contains the number of input header elements. Finally assigns that
     * to the axis soap data structure. */
    const apr_array_header_t* arr;
	int headercount = ap_table_elts(((request_rec*)m_pContext)->headers_in)->nelts;
	apr_table_entry_t* pHeaders = 0;
    /* casting req_rec->headers_in to axis header struct and assigning that to 
     * the axis soap structure. Hope this is ok 
     */

    /* obtain the array_header from the headers_in table and assign it to the 
     * axis soap structure 
     */
    arr = ap_table_elts(((request_rec*)m_pContext)->headers_in);
	pHeaders = (apr_table_entry_t*)arr->elts;
	for (int ix=0; ix<headercount; ix++)
	{
		if (!strcmp(pHeaders->key, pcKey))
			return pHeaders->val;
		pHeaders++;
	}
	return 0;
}

void Apache2Transport::setSessionId(const char* pcSessionId)
{
}

const char* Apache2Transport::getSessionId()
{
	return "this is temporary session id"; //TODO
}

const char* Apache2Transport::getServiceName()
{
	return 0; //TODO
}

AXIS_PROTOCOL_TYPE Apache2Transport::getProtocol()
{
	return m_eProtocolType;
}
 
int Apache2Transport::setProtocol(AXIS_PROTOCOL_TYPE eProtocol)
{
    if( eProtocol == APTHTTP1_1 || eProtocol == APTHTTP1_0 )
    {
       m_eProtocolType = eProtocol;
       return AXIS_SUCCESS;
    }
    else
        return AXIS_FAIL;
}


int Apache2Transport::getSubProtocol()
{
    /*Determine the http method and assign it to the axis soap structure */
    switch (((request_rec*)m_pContext)->method_number)
    {
        case M_GET:
            return AXIS_HTTP_GET;
        case M_POST:
            return AXIS_HTTP_POST;
        default:
            return AXIS_HTTP_UNSUPPORTED;
    }
}

ISoapAttachment*  Apache2Transport::getAttachment(const char* pcAttachmentid)
{		
	ISoapAttachment* pAttch = pAttachmentHelper->getAttachment(pcAttachmentid);
	return pAttch;
//	return NULL;
};

char* Apache2Transport::getIncomingSOAPMimeHeaders()
{
	return pAttachmentHelper->getIncomingSOAPMimeHeaders();
//	return NULL;
}

ISoapAttachment**  Apache2Transport::getAllAttachments(int *pAttchArraySize)
{		
	ISoapAttachment** pAttachments = pAttachmentHelper->getAllAttachments(pAttchArraySize);
	return pAttachments;
//	return NULL;
};
