/* ------------------------------------------------------------------
 * Copyright (C) 1998-2010 PacketVideo
 *
 * 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.
 * -------------------------------------------------------------------
 */
#ifndef HTTP_COMPOSER_H_
#define HTTP_COMPOSER_H_


///////////////////////////////////////////////////////////////////////////////////////

#include "oscl_base.h"
#include "oscl_mem.h"
#include "oscl_str_ptr_len.h"

#define SIGNINTMAXVALUE    0x7fffffff

enum HTTPVersion
{
    HTTP_V1_0 = 0,
    HTTP_V1_1
};

// HTTPMethod enum
enum HTTPMethod
{
    HTTP_METHOD_GET = 0,            // both HTTP 1.0 and HTTP 1.1
    HTTP_METHOD_HEAD,           // both HTTP 1.0 and HTTP 1.1
    HTTP_METHOD_POST,           // both HTTP 1.0 and HTTP 1.1
    HTTP_METHOD_DELETE,         // both HTTP 1.0 and HTTP 1.1
    HTTP_METHOD_LINK,           // HTTP 1.0 only
    HTTP_METHOD_UNLINK,         // HTTP 1.0 only
    HTTP_METHOD_OPTIONS,        // HTTP 1.1 only
    HTTP_METHOD_PUT,            // HTTP 1.1 only
    HTTP_METHOD_TRACE,          // HTTP 1.1 only
    HTTP_METHOD_CONNECT,        // HTTP 1.1 only
};

// forward declaration
struct HTTPMemoryFragment;
class StringKeyValueStore;

class HTTPComposer
{
    public:

        /**
         * HTTP request line:  Method SP Request-URI SP HTTP-Version CRLF
         * set three basic elements for a http request: method, uri and version
         * uri must be set; if URI is set, but method or version is not set, then
         * the method or version will default to GET or version 1.1
         **/
        void setMethod(const HTTPMethod aMethod)
        {
            iMethod = aMethod;
        }
        char getMethod()
        {
            return iMethod;
        }
        void setVersion(const HTTPVersion aVersion)
        {
            iVersion = aVersion;
        }
        /**
         * Set URI. Note that composer doesn't do any memory allocation and memory copy for the input URI, and just save the pointer
         * for the URI string. So it is composer user's reponsiblity to keep URI valid during request composing.
         */
        OSCL_IMPORT_REF void setURI(const StrPtrLen aURI);

        /**
         * set/remove a standard field or add a new extension field for the headers
         * if aNewFieldValue=NULL, that means removing the existing field with the field key specified by aNewFieldName
         * The flag aNeedReplaceOldValue tells the library whether to replace the old value with the new value for the same field, if there is,
         * For HTTP, there is a typical situation where multiple same fields can be set at the same time, for example, field "Pragma". The
         * user can set all the implementation specifics for one "Pragma", or multiple "Pragma". So by default, if we run into the same
         * field, we won't replace the old value with the new value unless the user requires to do it.       *
         * @param aNewFieldName, input field name
         * @param aNewFieldValue, input field value name
         * @param aNeedReplaceOldValue, flag of whether the new value replaces the old value
         * @return true for success
         **/
        OSCL_IMPORT_REF bool setField(const StrCSumPtrLen &aNewFieldName, const StrPtrLen *aNewFieldValue, const bool aNeedReplaceOldValue = false);
        OSCL_IMPORT_REF bool setField(const StrCSumPtrLen &aNewFieldName, const char *aNewFieldValue, const bool aNeedReplaceOldValue = false);

        /**
         * get the current total length based on the current settings, and the length includes the length for
         * request-line + general header + request header + entity header. Note that this length doesn't include entity body length,
         * since entity body is controlled by the user.
         * This API is used for the user to get the length after setting everything done (using above set APIs), and help the user
         * provide the buffer with an accurate size to hold the HTTP request with optional entity body.
         * Note that this API can get called in multiple times, and any time.
         * @param usingAbsoluteURI, flag to show absolute uri is used in the first request line. For MS http streaming, it uses relative uri + Host field to
         *        identify the exact source
         * @return the current total length or COMPOSE_BAD_URI from the following COMPOSE_RETURN_CODES.
         **/
        OSCL_IMPORT_REF int32 getCurrentRequestLength(const bool usingAbsoluteURI = true);

        /**
         * compose a HTTP request with the given message buffer, and the optional entity body.
         * The reason for the request buffer provided by the user is, a http request could contain entity body (e.g. POST method),
         * which is controlled by the user. And the entity body can be anything. So it doesn't make much sense for the composer to
         * allocate a buffer for holding a HTTP request, since in general, the composer has no idea about how big a request is.
         * To avoid extra memory copy, if the HTTP request does have entity body, the input buffer should already have the entity
         * body in place, i.e. the entity body is copied to the input buffer from an offset, which is header length, retrieved from
         * the above getCurrentRequestLength() function. The following "aEntityBodyLength" argument just shows whether there is an
         * enity body put into the input buffer, and whether this length matches the version from "Content-Length" (which has to be
         * be set)
         * @param aNewFielaComposedMessageBuffer, input message buffer
         * @param aEntityBodyLength, entity body length, 0 means no entity body
         * @return following enum codes
         **/
        OSCL_IMPORT_REF int32 compose(OsclMemoryFragment &aComposedMessageBuffer, const bool usingAbsoluteURI = true, const uint32 aEntityBodyLength = 0);

        enum COMPOSE_RETURN_CODES
        {
            COMPOSE_SUCCESS = 0,

            // error
            COMPOSE_GENERAL_ERROR    = -1,
            COMPOSE_BUFFER_TOO_SMALL = -2,
            COMPOSE_URI_NOT_SET      = -3,
            COMPOSE_CONTENT_TYPE_NOT_SET_FOR_ENTITY_BODY   = -4,
            COMPOSE_CONTENT_LENGTH_NOT_SET_FOR_ENTITY_BODY = -5,
            COMPOSE_CONTENT_LENGTH_NOT_MATCH_ENTITY_BODY_LENGTH = -6,
            COMPOSE_BAD_URI = -7  // input uri is relative uri, but request using absolute uri, or
            // request using relative uri, but relative uri isn't able to obtained from input uri
        };

        /**
         * reset the composer in order to compose a new HTTP request. Currently there are two cases in consideration.
         * One is reset the everything in the composer to compose brand-new HTTP request with probably new header fields,
         * or new method, etc. In this case, all the previous settings (method, version, uri, and all header fields) get
         * reset. Another useful case is the new HTTP request only has a change on URI, which is highly likely, and so
         * method, version and all header fields are kept the same. So in this case, we don't need to reset everything.
         * This offers a possiblity for fast composing a new HTTP request.
         * @param aKeepAllSettingsExceptURI, flag to show whether to keep all settings except URI
         **/
        OSCL_IMPORT_REF void reset(const bool aKeepAllSettingsExceptURI = false);

        // factory method
        OSCL_IMPORT_REF static HTTPComposer *create();

        OSCL_IMPORT_REF void setMaxLineSizeForMultiLineRequest(const int32 &aSizeLimit = SIGNINTMAXVALUE)
        {
            iMaxLineSizeForMultiLineRequest = aSizeLimit;
        }

        // destructor
        OSCL_IMPORT_REF ~HTTPComposer();

    private:
        // sanity check for compose API
        int32 santityCheckForCompose(HTTPMemoryFragment &aComposedMessageBuffer, const bool usingAbsoluteURI = true, const uint32 aEntityBodyLength = 0);
        // compose the first request/status line of a HTTP request
        void composeFirstLine(HTTPMemoryFragment &aComposedMessageBuffer, const bool usingAbsoluteURI = true);
        // compose all headers of a HTTP request
        bool composeHeaders(HTTPMemoryFragment &aComposedMessageBuffer);

        bool createMultiHeaderLine(char* &ptr, StrPtrLen &keyList, StrPtrLen &valueList);
        // called by create(), construct the composer
        bool construct();

        // constructor
        HTTPComposer() : iMaxLineSizeForMultiLineRequest(SIGNINTMAXVALUE), iKeyValueStore(NULL)
        {
            ;
        }

    private:
        HTTPMethod  iMethod;
        HTTPVersion iVersion;
        StrPtrLen   iURI;
        StrPtrLen   iRelativeURI;

        uint32 iHeaderLength;
        uint32 iFirstLineLength; // length of the request/response line in HTTP header
        uint32 iEntityBodyLength;
        int32 iMaxLineSizeForMultiLineRequest;
        // field key-value store to handle key-value operations
        StringKeyValueStore *iKeyValueStore;
};

#endif // HTTP_COMPOSER_H_

