
/*
 * 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 <axis2_msg.h>
#include <axutil_property.h>

struct axis2_msg
{

    /** parent operation */
    axis2_op_t *parent;

    /** list of phases that represent the flow  */
    axutil_array_list_t *flow;

    /** name of the message */
    axis2_char_t *name;

    /** XML schema element qname */
    axutil_qname_t *element_qname;

    /** direction of message */
    axis2_char_t *direction;

    /** parameter container to hold message parameters */
    struct axutil_param_container *param_container;

    /** base description struct */
    axis2_desc_t *base;

    /** reference count of this object*/
    int ref;

};

AXIS2_EXTERN axis2_msg_t *AXIS2_CALL
axis2_msg_create(
    const axutil_env_t * env)
{
    axis2_msg_t *msg = NULL;

    AXIS2_ENV_CHECK(env, NULL);

    msg = (axis2_msg_t *) AXIS2_MALLOC(env->allocator, sizeof(axis2_msg_t));

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

    msg->param_container = NULL;
    msg->parent = NULL;
    msg->flow = NULL;
    msg->name = NULL;
    msg->element_qname = NULL;
    msg->direction = NULL;
    msg->base = NULL;
    msg->ref = 1;

    msg->param_container =
        (axutil_param_container_t *) axutil_param_container_create(env);
    if (!msg->param_container)
    {
        axis2_msg_free(msg, env);
        return NULL;
    }

    msg->flow = axutil_array_list_create(env, 0);
    if (!msg->flow)
    {
        axis2_msg_free(msg, env);
        return NULL;
    }

    msg->base = axis2_desc_create(env);
    if (!msg->base)
    {
        axis2_msg_free(msg, env);
        return NULL;
    }

    return msg;
}

AXIS2_EXTERN void AXIS2_CALL
axis2_msg_free(
    axis2_msg_t * msg,
    const axutil_env_t * env)
{
    AXIS2_ENV_CHECK(env, void);

    if (--(msg->ref) > 0)
    {
        return;
    }

    if (msg->flow)
    {
        int i = 0,
            size = 0;
        size = axutil_array_list_size(msg->flow, env);
        for (i = 0; i < size; i++)
        {
            axis2_phase_t *phase = NULL;
            phase = axutil_array_list_get(msg->flow, env, i);
            if (phase)
                axis2_phase_free(phase, env);
        }
        axutil_array_list_free(msg->flow, env);
    }

    if (msg->name)
    {
        AXIS2_FREE(env->allocator, msg->name);
    }

    if (msg->element_qname)
    {
        axutil_qname_free(msg->element_qname, env);
    }

    if (msg->direction)
    {
        AXIS2_FREE(env->allocator, msg->direction);
    }

    if (msg->param_container)
    {
        axutil_param_container_free(msg->param_container, env);
    }

    if (msg->base)
    {
        axis2_desc_free(msg->base, env);
    }

    msg->parent = NULL;

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

    return;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_msg_add_param(
    axis2_msg_t * msg,
    const axutil_env_t * env,
    axutil_param_t * param)
{
    axis2_char_t *param_name = NULL;

    AXIS2_ENV_CHECK(env, AXIS2_FALSE);
    AXIS2_PARAM_CHECK(env->error, param, AXIS2_FALSE);

    param_name = axutil_param_get_name(param, env);
    if (AXIS2_TRUE == axis2_msg_is_param_locked(msg, env, param_name))
    {
        AXIS2_ERROR_SET(env->error,
                        AXIS2_ERROR_PARAMETER_LOCKED_CANNOT_OVERRIDE,
                        AXIS2_FAILURE);
        return AXIS2_FAILURE;
    }
    return axutil_param_container_add_param(msg->param_container, env,
                                            param);
}

AXIS2_EXTERN axutil_param_t *AXIS2_CALL
axis2_msg_get_param(
    const axis2_msg_t * msg,
    const axutil_env_t * env,
    const axis2_char_t * param_name)
{
    AXIS2_PARAM_CHECK(env->error, param_name, NULL);

    return axutil_param_container_get_param(msg->param_container, env,
                                            param_name);
}

AXIS2_EXTERN axutil_array_list_t *AXIS2_CALL
axis2_msg_get_all_params(
    const axis2_msg_t * msg,
    const axutil_env_t * env)
{
    AXIS2_PARAM_CHECK(env->error, msg->param_container, AXIS2_FALSE);

    return axutil_param_container_get_params(msg->param_container, env);
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_msg_set_parent(
    axis2_msg_t * msg,
    const axutil_env_t * env,
    axis2_op_t * op)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    msg->parent = op;
    if (op)
    {
        axis2_desc_set_parent(msg->base, env, axis2_op_get_base(op, env));
    }
    return AXIS2_SUCCESS;
}

AXIS2_EXTERN axis2_op_t *AXIS2_CALL
axis2_msg_get_parent(
    const axis2_msg_t * msg,
    const axutil_env_t * env)
{
    return msg->parent;
}

AXIS2_EXTERN axutil_array_list_t *AXIS2_CALL
axis2_msg_get_flow(
    const axis2_msg_t * msg,
    const axutil_env_t * env)
{
    return msg->flow;
}

AXIS2_EXTERN axis2_bool_t AXIS2_CALL
axis2_msg_is_param_locked(
    axis2_msg_t * msg,
    const axutil_env_t * env,
    const axis2_char_t * param_name)
{
    axis2_op_t *parent_l = NULL;
    axutil_param_t *param_l = NULL;
    axis2_bool_t locked = AXIS2_FALSE;

    AXIS2_ENV_CHECK(env, AXIS2_FALSE);
    AXIS2_PARAM_CHECK(env->error, param_name, AXIS2_FALSE);

    /* checking the locked status in parent */
    parent_l = axis2_msg_get_parent(msg, env);
    if (parent_l)
    {
        locked = axis2_op_is_param_locked(parent_l, env, param_name);
    }
    if (AXIS2_TRUE == locked)
    {
        return AXIS2_TRUE;
    }
    else
    {
        param_l = axis2_msg_get_param(msg, env, param_name);
    }
    return (param_l && axutil_param_is_locked(param_l, env));
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_msg_set_flow(
    axis2_msg_t * msg,
    const axutil_env_t * env,
    axutil_array_list_t * flow)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    if (msg->flow)
    {
        axutil_array_list_free(msg->flow, env);
        msg->flow = NULL;
    }
    if (flow)
    {
        msg->flow = flow;
    }
    return AXIS2_SUCCESS;
}

AXIS2_EXTERN const axis2_char_t *AXIS2_CALL
axis2_msg_get_direction(
    const axis2_msg_t * msg,
    const axutil_env_t * env)
{
    return msg->direction;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_msg_set_direction(
    axis2_msg_t * msg,
    const axutil_env_t * env,
    const axis2_char_t * direction)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);

    if (msg->direction)
    {
        AXIS2_FREE(env->allocator, msg->direction);
        msg->direction = NULL;
    }

    if (direction)
    {
        msg->direction = axutil_strdup(env, direction);
        if (!(msg->direction))
        {
            return AXIS2_FAILURE;
        }
    }

    return AXIS2_SUCCESS;
}

AXIS2_EXTERN const axutil_qname_t *AXIS2_CALL
axis2_msg_get_element_qname(
    const axis2_msg_t * msg,
    const axutil_env_t * env)
{
    return msg->element_qname;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_msg_set_element_qname(
    axis2_msg_t * msg,
    const axutil_env_t * env,
    const axutil_qname_t * element_qname)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);

    if (msg->element_qname)
    {
        axutil_qname_free(msg->element_qname, env);
        msg->element_qname = NULL;
    }

    if (element_qname)
    {
        msg->element_qname =
            axutil_qname_clone((axutil_qname_t *) element_qname, env);
        if (!(msg->element_qname))
        {
            return AXIS2_FAILURE;
        }
    }

    return AXIS2_SUCCESS;
}

AXIS2_EXTERN const axis2_char_t *AXIS2_CALL
axis2_msg_get_name(
    const axis2_msg_t * msg,
    const axutil_env_t * env)
{
    return msg->name;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_msg_set_name(
    axis2_msg_t * msg,
    const axutil_env_t * env,
    const axis2_char_t * name)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);

    if (msg->name)
    {
        AXIS2_FREE(env->allocator, msg->name);
        msg->name = NULL;
    }

    if (name)
    {
        msg->name = axutil_strdup(env, name);
        if (!(msg->name))
        {
            return AXIS2_FAILURE;
        }
    }

    return AXIS2_SUCCESS;
}

AXIS2_EXTERN axis2_desc_t *AXIS2_CALL
axis2_msg_get_base(
    const axis2_msg_t * msg,
    const axutil_env_t * env)
{
    return msg->base;
}

AXIS2_EXTERN axutil_param_container_t *AXIS2_CALL
axis2_msg_get_param_container(
    const axis2_msg_t * msg,
    const axutil_env_t * env)
{
    return msg->param_container;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_msg_increment_ref(
    axis2_msg_t * msg,
    const axutil_env_t * env)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    msg->ref++;
    return AXIS2_SUCCESS;
}
