
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */

#include <axiom_output.h>
#include <stdarg.h>
#include <axutil_string.h>
#include <axiom_xml_writer.h>
#include <axiom_text.h>
#include <axiom_soap_const.h>
#include <axutil_array_list.h>
#include <axutil_uuid_gen.h>
#include <axiom_mime_part.h>

#define AXIS2_DEFAULT_CHAR_SET_ENCODING  "UTF-8"

/** also defined in axiom_soap.h */

/** max args for om_output_write function */
#define MAX_ARGS  4

struct axiom_output
{

    /** axiom_xml_writer. any xml writer which
        implemet axiom_xml_writer.h interface  */
    axiom_xml_writer_t *xml_writer;

    axis2_bool_t do_optimize;

    axis2_char_t *mime_boundary;

    axis2_char_t *root_content_id;

    int next_id;

    axis2_char_t *next_content_id;

    axis2_bool_t is_soap11;

    axis2_char_t *char_set_encoding;

    axis2_char_t *xml_version;

    axis2_bool_t ignore_xml_declaration;

    axutil_array_list_t *binary_node_list;

    axis2_char_t *mime_boundry;

    axis2_char_t *content_type;

    axutil_array_list_t *mime_parts;

};

AXIS2_EXTERN axiom_output_t *AXIS2_CALL
axiom_output_create(
    const axutil_env_t * env,
    axiom_xml_writer_t * xml_writer)
{
    axiom_output_t *om_output = NULL;
    AXIS2_ENV_CHECK(env, NULL);

    om_output = (axiom_output_t *) AXIS2_MALLOC(env->allocator,
                                                sizeof(axiom_output_t));

    if (!om_output)
    {
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);
        return NULL;
    }

    om_output->xml_writer = xml_writer;
    om_output->do_optimize = AXIS2_TRUE;
    om_output->mime_boundary = NULL;
    om_output->root_content_id = NULL;
    om_output->next_content_id = NULL;
    om_output->next_id = 0;
    om_output->is_soap11 = AXIS2_TRUE;
    om_output->char_set_encoding = AXIS2_DEFAULT_CHAR_SET_ENCODING;
    om_output->xml_version = NULL;
    om_output->ignore_xml_declaration = AXIS2_TRUE;
    om_output->binary_node_list = NULL;
    om_output->mime_boundry = NULL;
    om_output->content_type = NULL;
    om_output->mime_parts = NULL;

    return om_output;
}

AXIS2_EXTERN void AXIS2_CALL
axiom_output_free(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    AXIS2_ENV_CHECK(env, void);

    if (om_output->xml_version)
    {
        AXIS2_FREE(env->allocator, om_output->xml_version);
    }
    if (om_output->mime_boundary)
    {
        AXIS2_FREE(env->allocator, om_output->mime_boundary);
    }
    if (om_output->next_content_id)
    {
        AXIS2_FREE(env->allocator, om_output->next_content_id);
    }
    if (om_output->root_content_id)
    {
        AXIS2_FREE(env->allocator, om_output->root_content_id);
    }

    if (om_output->xml_writer)
    {
        axiom_xml_writer_free(om_output->xml_writer, env);
    }

    if (om_output->binary_node_list)
    {
        axutil_array_list_free(om_output->binary_node_list, env);
    }

    if (om_output->content_type)
    {
        AXIS2_FREE(env->allocator, om_output->content_type);
    }

    AXIS2_FREE(env->allocator, om_output);
    return;
}

AXIS2_EXTERN axis2_bool_t AXIS2_CALL
axiom_output_is_soap11(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    return om_output->is_soap11;
}

AXIS2_EXTERN axis2_bool_t AXIS2_CALL
axiom_output_is_ignore_xml_declaration(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    return om_output->ignore_xml_declaration;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_output_set_ignore_xml_declaration(
    axiom_output_t * om_output,
    const axutil_env_t * env,
    axis2_bool_t ignore_xml_dec)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    om_output->ignore_xml_declaration = ignore_xml_dec;
    return AXIS2_SUCCESS;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_output_set_soap11(
    axiom_output_t * om_output,
    const axutil_env_t * env,
    axis2_bool_t soap11)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    om_output->is_soap11 = soap11;
    return AXIS2_SUCCESS;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_output_set_xml_version(
    axiom_output_t * om_output,
    const axutil_env_t * env,
    axis2_char_t * xml_version)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);

    AXIS2_PARAM_CHECK(env->error, xml_version, AXIS2_FAILURE);

    if (om_output->xml_version)
    {
        AXIS2_FREE(env->allocator, om_output->xml_version);
        om_output->xml_version = NULL;
    }

    om_output->xml_version = axutil_strdup(env, xml_version);
    if (!om_output->xml_version)
    {
        return AXIS2_FAILURE;
    }
    return AXIS2_SUCCESS;
}

AXIS2_EXTERN axis2_char_t *AXIS2_CALL
axiom_output_get_xml_version(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    return om_output->xml_version;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_output_set_char_set_encoding(
    axiom_output_t * om_output,
    const axutil_env_t * env,
    axis2_char_t * char_set_encoding)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    om_output->char_set_encoding = char_set_encoding;
    return AXIS2_SUCCESS;
}

AXIS2_EXTERN axis2_char_t *AXIS2_CALL
axiom_output_get_char_set_encoding(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    return om_output->char_set_encoding;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_output_set_do_optimize(
    axiom_output_t * om_output,
    const axutil_env_t * env,
    axis2_bool_t optimize)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    om_output->do_optimize = optimize;
    return AXIS2_SUCCESS;
}

AXIS2_EXTERN axiom_xml_writer_t *AXIS2_CALL
axiom_output_get_xml_writer(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    return om_output->xml_writer;
}

AXIS2_EXTERN axis2_bool_t AXIS2_CALL
axiom_output_is_optimized(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    return om_output->do_optimize;
}

AXIS2_EXTERN const axis2_char_t *AXIS2_CALL
axiom_output_get_content_type(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    const axis2_char_t *soap_content_type = NULL;
    if (AXIS2_TRUE == om_output->do_optimize)
    {
        if (AXIS2_TRUE == om_output->is_soap11)
        {
            soap_content_type = AXIOM_SOAP11_CONTENT_TYPE;
        }
        else
        {
            soap_content_type = AXIOM_SOAP12_CONTENT_TYPE;
        }
        if (om_output->content_type)
        {
            AXIS2_FREE(env->allocator, om_output->content_type);
            om_output->content_type = NULL;
        }

        om_output->content_type =
            (axis2_char_t *)
            axiom_mime_part_get_content_type_for_mime(env, om_output->mime_boundry,
                                                        om_output->root_content_id,
                                                        om_output->char_set_encoding,
                                                        soap_content_type);
        return om_output->content_type;
    }
    else if (AXIS2_TRUE == om_output->is_soap11)
    {
        return AXIOM_SOAP11_CONTENT_TYPE;
    }
    return AXIOM_SOAP12_CONTENT_TYPE;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_output_write_optimized(
    axiom_output_t * om_output,
    const axutil_env_t * env,
    axiom_text_t * om_text)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    if (om_output->binary_node_list)
    {
        axutil_array_list_add(om_output->binary_node_list, env, om_text);
    }
    else
    {
        om_output->binary_node_list = axutil_array_list_create(env, 5);
        if (!(om_output->binary_node_list))
        {
            return AXIS2_FAILURE;
        }
        axutil_array_list_add(om_output->binary_node_list, env, om_text);
    }
    return AXIS2_SUCCESS;
}

AXIS2_EXTERN axis2_char_t *AXIS2_CALL
axiom_output_get_next_content_id(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    axis2_char_t *uuid = NULL;
    axis2_char_t *temp_str = NULL;
    axis2_char_t *temp_str1 = NULL;
    axis2_char_t id[256];
    om_output->next_id++;

    /** free existing id */
    if (om_output->next_content_id)
    {
        AXIS2_FREE(env->allocator, om_output->next_content_id);
        om_output->next_content_id = NULL;
    }

    uuid = axutil_uuid_gen(env);
    if (!uuid)
    {
        return NULL;
    }

    sprintf(id, "%d", om_output->next_id);

    temp_str = axutil_stracat(env, id, ".");
    temp_str1 = axutil_stracat(env, temp_str, uuid);
    om_output->next_content_id = axutil_stracat(env, temp_str1, "@apache.org");
    if (temp_str)
    {
        AXIS2_FREE(env->allocator, temp_str);
        temp_str = NULL;
    }
    if (temp_str1)
    {
        AXIS2_FREE(env->allocator, temp_str1);
        temp_str1 = NULL;
    }
    if (uuid)
    {
        AXIS2_FREE(env->allocator, uuid);
        uuid = NULL;
    }
    return om_output->next_content_id;
}

AXIS2_EXTERN axis2_char_t *AXIS2_CALL
axiom_output_get_root_content_id(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    axis2_char_t *temp_str = NULL;
    axis2_char_t *uuid = NULL;

    if (!om_output->root_content_id)
    {
        uuid = axutil_uuid_gen(env);

        temp_str = axutil_stracat(env, "0.", uuid);

        om_output->root_content_id =
            axutil_stracat(env, temp_str, "@apache.org");

        if (temp_str)
        {
            AXIS2_FREE(env->allocator, temp_str);
            temp_str = NULL;
        }
        if (uuid)
        {
            AXIS2_FREE(env->allocator, uuid);
            uuid = NULL;
        }
    }

    return om_output->root_content_id;
}

AXIS2_EXTERN axis2_char_t *AXIS2_CALL
axiom_output_get_mime_boundry(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    axis2_char_t *uuid = NULL;
    if (!om_output->mime_boundary)
    {
        uuid = axutil_uuid_gen(env);

        om_output->mime_boundary = axutil_stracat(env, "MIMEBoundary", uuid);
        if (uuid)
        {
            AXIS2_FREE(env->allocator, uuid);
            uuid = NULL;
        }
    }
    return om_output->mime_boundary;
}

/******************************************************************************/

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_output_write(
    axiom_output_t * om_output,
    const axutil_env_t * env,
    axiom_types_t type,
    int no_of_args,
    ...)
{
    int status = AXIS2_SUCCESS;
    axis2_char_t *args_list[MAX_ARGS];
    int i = 0;
    va_list ap;

    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);

    va_start(ap, no_of_args);
    for (i = 0; i < no_of_args; i++)
    {
        args_list[i] = va_arg(ap, axis2_char_t *);
    }
    va_end(ap);

    if (type == AXIOM_ELEMENT)
    {
        if (no_of_args == 0)
        {
            status =
                axiom_xml_writer_write_end_element(om_output->xml_writer, env);
        }
        else if (no_of_args == 1)
        {
            status =
                axiom_xml_writer_write_start_element(om_output->xml_writer,
                                                     env, args_list[0]);
        }
        else if (no_of_args == 2)
        {
            status =
                axiom_xml_writer_write_start_element_with_namespace(om_output->
                                                                    xml_writer,
                                                                    env,
                                                                    args_list
                                                                    [0],
                                                                    args_list
                                                                    [1]);
        }
        else if (no_of_args == 3)
        {
            status =
                axiom_xml_writer_write_start_element_with_namespace_prefix
                (om_output->xml_writer, env, args_list[0], args_list[1],
                 args_list[2]);
        }
        else if (no_of_args == 4)
        {
            if (!args_list[0])
            {
                status = AXIS2_FAILURE;
            }
            else if (!args_list[1])
            {
                status =
                    axiom_xml_writer_write_empty_element(om_output->xml_writer,
                                                         env, args_list[0]);
            }
            else if (!args_list[2])
            {
                status =
                    axiom_xml_writer_write_empty_element_with_namespace(om_output->
                                                                        xml_writer,
                                                                        env,
                                                                        args_list
                                                                        [0],
                                                                        args_list
                                                                        [1]);
            }
            else
            {
                status =
                    axiom_xml_writer_write_empty_element_with_namespace_prefix
                    (om_output->xml_writer, env, args_list[0], args_list[1],
                     args_list[2]);
            }
        }
    }
    else if (type == AXIOM_DATA_SOURCE)
    {
        status = axiom_xml_writer_write_raw(om_output->xml_writer,
                                            env, args_list[0]);
    }
    else if (type == AXIOM_ATTRIBUTE)
    {
        if (no_of_args == 2)
        {
            status =
                axiom_xml_writer_write_attribute(om_output->xml_writer,
                                                 env,
                                                 args_list[0], args_list[1]);
        }
        else if (no_of_args == 3)
        {
            status =
                axiom_xml_writer_write_attribute_with_namespace(om_output->
                                                                xml_writer, env,
                                                                args_list[0],
                                                                args_list[1],
                                                                args_list[2]);
        }
        else if (no_of_args == 4)
        {
            status =
                axiom_xml_writer_write_attribute_with_namespace_prefix
                (om_output->xml_writer, env, args_list[0], args_list[1],
                 args_list[2], args_list[3]);
        }
    }
    else if (type == AXIOM_NAMESPACE)
    {
        /* If the namespace prefix is xml, it must be the pre-defined xml
           namespace.  Although the XML spec allows it to be declared
           explicitly, this is superfluous and not accepted by all xml
           parsers. */
        if ((!args_list[0]) || (strcmp(args_list[0], "xml") != 0))
        {
            status = axiom_xml_writer_write_namespace(om_output->xml_writer,
                                                      env,
                                                      args_list[0], args_list[1]);
        }
    }
    else if (type == AXIOM_TEXT)
    {
        status = axiom_xml_writer_write_characters(om_output->xml_writer,
                                                   env, args_list[0]);
    }
    else if (type == AXIOM_COMMENT)
    {
        status = axiom_xml_writer_write_comment(om_output->xml_writer,
                                                env, args_list[0]);
    }
    else if (type == AXIOM_PROCESSING_INSTRUCTION)
    {
        if (no_of_args == 1)
        {
            status =
                axiom_xml_writer_write_processing_instruction(om_output->
                                                              xml_writer, env,
                                                              args_list[0]);
        }
        else if (no_of_args == 2)
        {
            status =
                axiom_xml_writer_write_processing_instruction_data(om_output->
                                                                   xml_writer,
                                                                   env,
                                                                   args_list[0],
                                                                   args_list
                                                                   [1]);
        }
    }
    else if (type == AXIOM_DOCTYPE)
    {
        status = axiom_xml_writer_write_dtd(om_output->xml_writer,
                                            env, args_list[0]);
    }

    if (status == AXIS2_SUCCESS)
    {
        return AXIS2_SUCCESS;
    }
    else
        return AXIS2_FAILURE;
}

axis2_status_t AXIS2_CALL
axiom_output_write_xml_version_encoding(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    if (!om_output->xml_version)
    {
        axiom_output_set_xml_version(om_output, env, "1.0");
    }
    if (!om_output->char_set_encoding)
    {
        axiom_output_set_char_set_encoding(om_output, env, "UTF-8");
    }
    return
        axiom_xml_writer_write_start_document_with_version_encoding(om_output->
                                                                    xml_writer,
                                                                    env,
                                                                    om_output->
                                                                    xml_version,
                                                                    om_output->
                                                                    char_set_encoding);

}

/* This method will be called from transport. After this method each and every
 * message part needs to be send are stored in an arraylits. So the transport 
 * sender should correctly figure out how to send from the given information.
 */ 


AXIS2_EXTERN axis2_status_t AXIS2_CALL 
    axiom_output_flush(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    const axis2_char_t *soap_content_type = NULL;

    AXIS2_ENV_CHECK(env, NULL);

    if (om_output->do_optimize)
    {
        axis2_char_t *root_content_id = NULL;
        axis2_char_t *buffer = NULL;

        /* Extracting the soap part */

        buffer = axiom_xml_writer_get_xml(om_output->xml_writer,
                                                      env);
        if (om_output->is_soap11)
        {
            soap_content_type = AXIOM_SOAP11_CONTENT_TYPE;
        }
        else
        {
            soap_content_type = AXIOM_SOAP12_CONTENT_TYPE;
        }
        
        /* The created mime_boundary for this soap message */
        
        om_output->mime_boundry = axiom_output_get_mime_boundry(om_output, env);
        
        /* This is also created for attachments*/
        root_content_id = axiom_output_get_root_content_id(om_output, env);
        
        /* different parts of the message is added according to their order
         * to an arraylist */
        om_output->mime_parts = axiom_mime_part_create_part_list( 
                                    env, buffer, om_output->binary_node_list,
                                    om_output->mime_boundry, 
                                    om_output->root_content_id, 
                                    om_output->char_set_encoding,
                                    soap_content_type);
        
        if(om_output->mime_parts)
        {
            return AXIS2_SUCCESS;
        }
        else
        {
            return AXIS2_FAILURE;
        }
    }
    return AXIS2_SUCCESS;
}

AXIS2_EXTERN axutil_array_list_t *AXIS2_CALL
axiom_output_get_mime_parts(
    axiom_output_t * om_output,
    const axutil_env_t * env)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    return om_output->mime_parts;
}

