/*
 * 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 <signal.h>
#include <axiom.h>
#include <axiom_soap.h>
#include <axis2_engine.h>
#include <axiom_mime_parser.h>
#include <axutil_http_chunked_stream.h>
#include <axis2_amqp_defines.h>
#include <axis2_amqp_util.h>
#include <axis2_amqp_request_processor.h>

void* AXIS2_THREAD_FUNC
axis2_amqp_request_processor_thread_function(
	axutil_thread_t* thread,
	void* request_data)
{
	axis2_status_t status = AXIS2_FAILURE;
	axutil_env_t* env = NULL;
	axutil_env_t* thread_env = NULL;
	axis2_amqp_request_processor_resource_pack_t* request_resource_pack = NULL;

#ifndef WIN32
#ifdef AXIS2_SVR_MULTI_THREADED
	signal(SIGPIPE, SIG_IGN);
#endif
#endif

	request_resource_pack = (axis2_amqp_request_processor_resource_pack_t*)request_data;
	
	env = request_resource_pack->env;
	thread_env = axutil_init_thread_env(env);

	/* Process Request */
	status = axis2_amqp_process_request(thread_env, request_resource_pack);

	if (status == AXIS2_SUCCESS)
	{
		AXIS2_LOG_INFO(thread_env->log, "Request Processed Successfully");
	}
	else
	{
		 AXIS2_LOG_WARNING(thread_env->log, AXIS2_LOG_SI, "Error while Processing Request");
	}

	AXIS2_FREE(thread_env->allocator, request_resource_pack->request_content);
	AXIS2_FREE(thread_env->allocator, request_resource_pack->reply_to);
	AXIS2_FREE(thread_env->allocator, request_resource_pack->content_type);
	AXIS2_FREE(thread_env->allocator, request_resource_pack->soap_action);
	
	AXIS2_FREE(thread_env->allocator, request_resource_pack);

	if (thread_env)
	{
		thread_env = NULL;
	}

#ifdef AXIS2_SVR_MULTI_THREADED
	axutil_thread_pool_exit_thread(env->thread_pool, thread);
#endif

	return NULL;
}


axis2_status_t
axis2_amqp_process_request(
	const axutil_env_t* env,
	axis2_amqp_request_processor_resource_pack_t* request_resource_pack)
{
	axiom_xml_reader_t* xml_reader = NULL;
	axiom_stax_builder_t* stax_builder = NULL;
	axiom_soap_builder_t* soap_builder = NULL;
	axis2_transport_out_desc_t* out_desc = NULL;
	axis2_transport_in_desc_t* in_desc = NULL;
	axis2_msg_ctx_t* msg_ctx = NULL;
	axiom_soap_envelope_t* soap_envelope = NULL;
	axis2_engine_t* engine = NULL;
	const axis2_char_t* soap_ns_uri = NULL;
	axis2_bool_t is_soap_11 = AXIS2_FALSE;
	axis2_char_t *soap_body_str = NULL;
	int soap_body_len = 0;
	axis2_bool_t is_mtom = AXIS2_FALSE;
	axis2_status_t status = AXIS2_FAILURE;
	axutil_hash_t *binary_data_map = NULL;
	axiom_soap_body_t *soap_body = NULL;
	axutil_property_t* reply_to_property = NULL;

	/* Create msg_ctx */
	if (!request_resource_pack->conf_ctx)
	{
		AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Conf Context not Available");
		return AXIS2_FAILURE;
	}

	out_desc = axis2_conf_get_transport_out(
			axis2_conf_ctx_get_conf(request_resource_pack->conf_ctx, env),
			env, AXIS2_TRANSPORT_ENUM_AMQP);
	if (!out_desc)
	{
		AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Transport Out Descriptor not Found");
		return AXIS2_FAILURE;
	}

	in_desc = axis2_conf_get_transport_in(
			axis2_conf_ctx_get_conf(request_resource_pack->conf_ctx, env),
			env, AXIS2_TRANSPORT_ENUM_AMQP);
	if (!in_desc)
	{
		AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Transport In Descriptor not Found");
		return AXIS2_FAILURE;
	}

	/* Create msg_ctx */
	msg_ctx = axis2_msg_ctx_create(env, request_resource_pack->conf_ctx, in_desc, out_desc);
	
	axis2_msg_ctx_set_server_side(msg_ctx, env, AXIS2_TRUE);
	
	/* Handle MTOM */
	if (strstr(request_resource_pack->content_type, AXIS2_AMQP_HEADER_ACCEPT_MULTIPART_RELATED))
	{
		axis2_char_t* mime_boundary = 
			axis2_amqp_util_get_value_from_content_type(env,
					request_resource_pack->content_type,
					AXIS2_AMQP_HEADER_CONTENT_TYPE_MIME_BOUNDARY);

		if (mime_boundary)
		{
			axiom_mime_parser_t *mime_parser = NULL;
            int soap_body_len = 0;
            axutil_param_t *buffer_size_param = NULL;
            axutil_param_t *max_buffers_param = NULL;
            axutil_param_t *attachment_dir_param = NULL;
            axis2_char_t *value_size = NULL;
            axis2_char_t *value_num = NULL;
            axis2_char_t *value_dir = NULL;
            int size = 0;
            int num = 0;

            mime_parser = axiom_mime_parser_create(env);
            
			buffer_size_param = axis2_msg_ctx_get_parameter(msg_ctx, env, AXIS2_MTOM_BUFFER_SIZE);
            if (buffer_size_param)
            {
                value_size = (axis2_char_t*)axutil_param_get_value(buffer_size_param, env);
                if (value_size)
                {
                    size = atoi(value_size);
                    axiom_mime_parser_set_buffer_size(mime_parser, env, size);
                }
            }
            
            max_buffers_param = axis2_msg_ctx_get_parameter(msg_ctx, env, AXIS2_MTOM_MAX_BUFFERS);
            if (max_buffers_param)
            {
                value_num = (axis2_char_t*)axutil_param_get_value(max_buffers_param, env);
                if (value_num)
                {
                    num = atoi(value_num);
                    axiom_mime_parser_set_max_buffers(mime_parser, env, num);
                }
            }

            /* If this paramter is there mime_parser will cached the attachment 
             * using to the directory for large attachments. */    
            attachment_dir_param = axis2_msg_ctx_get_parameter(msg_ctx, env, AXIS2_ATTACHMENT_DIR);
            if (attachment_dir_param)
            {
                value_dir = (axis2_char_t*)axutil_param_get_value(attachment_dir_param, env);
                if (value_dir)
                {
                    axiom_mime_parser_set_attachment_dir(mime_parser, env, value_dir);
                }
            }

            if (mime_parser)
            {
				axis2_callback_info_t *callback_ctx = NULL;
				axutil_stream_t *stream = NULL;

				callback_ctx = AXIS2_MALLOC(env->allocator, sizeof(axis2_callback_info_t));

            	stream = axutil_stream_create_basic(env);
            	if (stream)
            	{
					axutil_stream_write(stream, env, request_resource_pack->request_content, 
							request_resource_pack->content_length);
                	callback_ctx->env = env;
                	callback_ctx->in_stream = stream;
                	callback_ctx->content_length = request_resource_pack->content_length;
                	callback_ctx->unread_len = request_resource_pack->content_length;
                	callback_ctx->chunked_stream = NULL;
            	}

                binary_data_map = 
                    axiom_mime_parser_parse(mime_parser, env,
                                            axis2_amqp_util_on_data_request,
                                            (void*)callback_ctx,
                                            mime_boundary);
                if (!binary_data_map)
                {
                    return AXIS2_FAILURE;
                }
                
                soap_body_str = axiom_mime_parser_get_soap_body_str(mime_parser, env);
				soap_body_len = axiom_mime_parser_get_soap_body_len(mime_parser, env);

				axutil_stream_free(stream, env);
				AXIS2_FREE(env->allocator, callback_ctx);
				axiom_mime_parser_free(mime_parser, env);
            }

			AXIS2_FREE(env->allocator, mime_boundary);
		}

		is_mtom = AXIS2_TRUE;
	}
	else
	{
		soap_body_str = request_resource_pack->request_content;
		soap_body_len = request_resource_pack->content_length;
	}

	soap_body_len = axutil_strlen(soap_body_str);

	xml_reader = axiom_xml_reader_create_for_memory(env, soap_body_str, soap_body_len,
			NULL, AXIS2_XML_PARSER_TYPE_BUFFER);
	if (!xml_reader)
	{
		AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Failed to Create XML Reader");
		return AXIS2_FAILURE;
	}
	
	stax_builder = axiom_stax_builder_create(env, xml_reader);
	if (!stax_builder)
	{
		AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Failed to Create StAX Builder");
		return AXIS2_FAILURE;
	}

	soap_ns_uri = AXIOM_SOAP12_SOAP_ENVELOPE_NAMESPACE_URI;

	if (request_resource_pack->content_type)
	{
		if (strstr(request_resource_pack->content_type, AXIS2_AMQP_HEADER_ACCEPT_TEXT_XML))
		{
			is_soap_11 = AXIS2_TRUE;
			soap_ns_uri = AXIOM_SOAP11_SOAP_ENVELOPE_NAMESPACE_URI;
		}
		/*if (strstr(request_resource_pack->content_type, AXIS2_AMQP_HEADER_ACCEPT_APPL_SOAP))
		{
			is_soap_11 = AXIS2_FALSE;
			soap_ns_uri = AXIOM_SOAP12_SOAP_ENVELOPE_NAMESPACE_URI;
		}
		else if (strstr(request_resource_pack->content_type, AXIS2_AMQP_HEADER_ACCEPT_TEXT_XML))
		{
			is_soap_11 = AXIS2_TRUE;
			soap_ns_uri = AXIOM_SOAP11_SOAP_ENVELOPE_NAMESPACE_URI;
		}*/
	}

	soap_builder = axiom_soap_builder_create(env, stax_builder, soap_ns_uri);
	if (!soap_builder)
	{
		AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Failed to Create SOAP Builder");
		return AXIS2_FAILURE;
	}
	
	if (binary_data_map)
	{
		axiom_soap_builder_set_mime_body_parts(soap_builder, env, binary_data_map);
	}

	soap_envelope = axiom_soap_builder_get_soap_envelope(soap_builder, env);
	axis2_msg_ctx_set_soap_envelope(msg_ctx, env, soap_envelope);

	soap_body = axiom_soap_envelope_get_body(soap_envelope, env);
	
	if (!soap_body)
	{
		return AXIS2_FAILURE;
	}

	/* SOAPAction */
	if (request_resource_pack->soap_action)
	{
		axis2_msg_ctx_set_soap_action(msg_ctx, env, 
				axutil_string_create(env, request_resource_pack->soap_action));
	}

	/* SOAP version */
	axis2_msg_ctx_set_is_soap_11(msg_ctx, env, is_soap_11);

	/* Set ReplyTo in the msg_ctx as a property. This is used by the server when
	 * 1. WS-A is not in use 
	 * 2. ReplyTo is an anonymous EPR - Sandesha2/Dual-channel */
	reply_to_property = axutil_property_create_with_args(env, AXIS2_SCOPE_REQUEST, 0, 0,
			(void*)request_resource_pack->reply_to);
	axis2_msg_ctx_set_property(msg_ctx, env, AXIS2_AMQP_MSG_CTX_PROPERTY_REPLY_TO, 
			reply_to_property);

	engine = axis2_engine_create(env, request_resource_pack->conf_ctx);

	if (AXIS2_TRUE == axiom_soap_body_has_fault(soap_body, env))
	{
		status = axis2_engine_receive_fault(engine, env, msg_ctx);
	}
	else
	{
		status = axis2_engine_receive(engine, env, msg_ctx);
	}

	if (engine)
	{
		axis2_engine_free(engine, env);
	}

	if (soap_body_str && is_mtom)
	{
		AXIS2_FREE(env->allocator, soap_body_str);
	}

	return status;
}
