/*
 * 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 <axutil_utils.h>
#include <stdlib.h>
#include <axutil_string.h>
#include <string.h>
#include <platforms/axutil_platform_auto_sense.h>

AXIS2_EXPORT axis2_char_t *axis2_request_url_prefix = "services";

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axutil_parse_rest_url_for_params(
    const axutil_env_t *env,
    const axis2_char_t *tmpl,
    const axis2_char_t *url,
    int *match_count,
    axis2_char_t ****matches)
{
    axis2_char_t ***ret = NULL;
    axis2_char_t *tmp1 = NULL;
    axis2_char_t **tmp2 = NULL;
    axis2_char_t ***tmp3 = NULL;
    axis2_char_t *tmp4 = NULL;
    axis2_char_t *resource = NULL;
    axis2_char_t *query = NULL;
    axis2_char_t *url_tmp = NULL;
    axis2_char_t *url_resource = NULL;
    axis2_char_t *url_query = NULL;
    axis2_bool_t finished = AXIS2_FALSE;
    axis2_status_t status = AXIS2_FAILURE;
    int ret_count = 0;
    int i = 0;
    int j = 0;
    axis2_bool_t in_tok = AXIS2_FALSE;

    tmp2 = AXIS2_MALLOC(env->allocator, 2 * (sizeof(axis2_char_t *)));
    memset(tmp2, 0, 2 * sizeof(axis2_char_t *));

    if(tmpl[0] == '/')
    {
        tmp1 = (axis2_char_t *)tmpl;
        tmp1++;
        resource = axutil_strdup(env, tmp1);
    }
    else
    {
        resource = axutil_strdup(env, tmpl);
    }
    i = (int)strlen(resource);
    /* We are sure that the difference lies within the int range */
    if(resource[i] == '/')
    {
        resource[i] = '\0';
    }
    tmp1 = strchr(resource, '?');
    if(tmp1)
    {
        axis2_char_t *tmp4 = NULL;

        tmp4 = tmp1;
        tmp1++;
        resource[tmp4 - resource] = '\0';
        if(*tmp1 && ((tmp1 - resource) < (int)strlen(resource) - 1))
        /* We are sure that the difference lies within the int range */
        {
            query = tmp1;
            /* 
             * Query String based matching is not implemented. This is
             * reserved for future implementations.
             */
        }
    }

    /* Validation of Template */
    i = (int)strlen(resource);
    /* We are sure that the difference lies within the int range */

    if(!strchr(resource, '{') && !strchr(resource, '}'))
    {
        i = 0;
    }
    for(j = 0; j < i; j++)
    {
        if(!in_tok)
        {
            if(resource[j] == '}')
            {
                AXIS2_FREE(env->allocator, resource);
                return AXIS2_FAILURE;
            }
            else if(resource[j] == '{')
            {
                if(j + 1 == i || resource[j + 1] == '}')
                {
                    AXIS2_FREE(env->allocator, resource);
                    return AXIS2_FAILURE;
                }
                else if(resource[j + 1] == '{')
                {
                    j++;
                }
                else
                {
                    in_tok = AXIS2_TRUE;
                }
            }
        }
        else
        {
            if(resource[j] == '{')
            {
                AXIS2_FREE(env->allocator, resource);
                return AXIS2_FAILURE;
            }
            else if(resource[j] == '}')
            {
                if(j + 1 < i && resource[j + 1] == '}')
                {
                    j++;
                }
                else
                {
                    in_tok = AXIS2_FALSE;
                }
            }
        }
    }
    if(in_tok)
    {
        AXIS2_FREE(env->allocator, resource);
        return AXIS2_FAILURE;
    }

    /* Validity of template guaranteed if not returned */

    if(url[0] == '/')
    {
        tmp1 = (axis2_char_t *)url;
        tmp1++;
        url_resource = axutil_strdup(env, tmp1);
    }
    else
    {
        url_resource = axutil_strdup(env, url);
    }
    i = (int)strlen(url_resource);

    /* We are sure that the difference lies within the int range */

    if(url_resource[i] == '/')
    {
        url_resource[i] = '\0';
    }
    i = 0;
    url_tmp = url_resource;
    tmp1 = strchr(url_resource, '?');
    if(tmp1)
    {
        axis2_char_t *tmp4 = NULL;

        tmp4 = tmp1;
        tmp1++;
        url_resource[tmp4 - url_resource] = '\0';
        if(*tmp1 && ((tmp1 - url_resource) < (int)strlen(url_resource) - 1))
        /* We are sure that the difference lies within the int range */
        {
            url_query = tmp1;
        }
    }
    tmp1 = resource;

    /* Simplest case match */

    if(!strchr(resource, '{'))
    {
        if(strcmp(resource, url_resource) == 0)
        {
            finished = AXIS2_TRUE;
        }
    }

    while(!finished)
    {
        tmp4 = strchr(tmp1, '{');
        if(tmp4 && tmp4[1])
        {
            if(tmp4[1] != '{')
            {
                axis2_char_t *tmp5 = NULL;
                axis2_char_t *tmp6 = NULL;
                axis2_char_t *tmp7 = NULL;
                axis2_char_t *tmp8 = NULL;
                axis2_char_t *tmp9 = NULL;

                /* Logic for finding out constant portion to match */
                i = (int)(tmp4 - tmp1);
                tmp2[0] = AXIS2_MALLOC(env->allocator, (i + 1) * sizeof(char));
                strncpy(tmp2[0], tmp1, i);
                tmp2[0][i] = '\0';
                if(url_tmp && *url_tmp)
                {
                    tmp6 = url_tmp;
                    tmp5 = strstr(tmp6, tmp2[0]);
                    if(tmp5)
                    {
                        tmp5 += strlen(tmp2[0]);
                        tmp7 = tmp4;
                        tmp8 = tmp4;
                        tmp7++;
                        if(*tmp7)
                        {
                            axis2_bool_t finished_tmp = AXIS2_FALSE;
                            while(!finished_tmp)
                            {
                                tmp6 = strchr(tmp8, '}');
                                if(tmp6 && *tmp6)
                                {
                                    if(tmp6[1] != '}')
                                    {
                                        tmp8 = tmp6 + 1;
                                        break;
                                    }
                                }
                                else
                                {
                                    finished_tmp = AXIS2_TRUE;
                                }
                            }
                            if(!finished_tmp && !strchr(tmp8, '{'))
                            {
                                tmp7 = tmp8 + strlen(tmp8);
                            }
                            else
                            {
                                while(!finished_tmp)
                                {
                                    tmp6 = strchr(tmp8, '{');
                                    if(tmp6 && tmp6[1])
                                    {
                                        if(tmp6[1] != '{')
                                        {
                                            tmp7 = tmp6;
                                            break;
                                        }
                                    }
                                    else
                                    {
                                        finished_tmp = AXIS2_TRUE;
                                    }
                                }
                            }
                            if(!finished_tmp)
                            {
                                i = (int)(tmp7 - tmp8);
                                tmp9 = AXIS2_MALLOC(env->allocator, (i + 1) * sizeof(char));
                                strncpy(tmp9, tmp8, i);
                                tmp9[i] = '\0';
                            }
                        }
                        if(tmp9 && *tmp9)
                        {
                            tmp6 = strstr(tmp5, tmp9);
                            AXIS2_FREE(env->allocator, tmp9);
                            tmp9 = NULL;
                        }
                        else
                        {
                            tmp6 = strchr(tmp5, '/');
                        }
                        /* Logic for saving the match */
                        if(tmp6 && tmp6 != tmp5)
                        {
                            i = (int)(tmp6 - tmp5);
                            url_tmp = tmp6;
                            tmp2[1] = AXIS2_MALLOC(env->allocator, (i + 1) * sizeof(char));
                            strncpy(tmp2[1], tmp5, i);
                            tmp2[1][i] = '\0';
                        }
                        else
                        {
                            i = (int)strlen(tmp5);
                            /* We are sure that the difference lies within the int range */
                            tmp2[1] = AXIS2_MALLOC(env->allocator, (i + 1) * sizeof(char));
                            strncpy(tmp2[1], tmp5, i);
                            tmp2[1][i] = '\0';
                            url_tmp = NULL;
                        }
                    }
                }
                else
                {
                    break;
                }
                while(!finished)
                {
                    tmp1 = tmp4 + 1;
                    tmp4 = strchr(tmp1, '}');
                    if(tmp4 && *tmp4)
                    {
                        if(tmp4[1] != '}')
                        {
                            /* Logic for saving the key for the match */
                            i = (int)(tmp4 - tmp1);
                            if(tmp2[0])
                            {
                                AXIS2_FREE(env->allocator, tmp2[0]);
                            }
                            tmp2[0] = AXIS2_MALLOC(env->allocator, (i + 1) * sizeof(char));
                            strncpy(tmp2[0], tmp1, i);
                            tmp2[0][i] = '\0';
                            tmp3 = ret;
                            ret_count++;
                            ret = AXIS2_MALLOC(env->allocator, ret_count * 2
                                * (sizeof(axis2_char_t *)));
                            memset(ret, 0, ret_count * 2 * sizeof(axis2_char_t *));
                            for(i = 0; i < ret_count - 1; i++)
                            {
                                ret[i] = tmp3[i];
                            }
                            ret[i] = tmp2;
                            tmp2 = AXIS2_MALLOC(env->allocator, 2 * (sizeof(axis2_char_t *)));
                            memset(tmp2, 0, 2 * sizeof(axis2_char_t *));
                            tmp3 = NULL;
                            break;
                        }
                        else
                        {
                            tmp4++;
                        }
                    }
                    else
                    {
                        finished = AXIS2_TRUE;
                    }
                }
            }
            else
            {
                tmp4++;
            }
        }
        else
        {
            /* Result of mismatch at the simplest case */
            if(!strchr(resource, '{'))
            {
                finished = AXIS2_FALSE;
                break;
            }
            finished = AXIS2_TRUE;
        }
        tmp1 = tmp4 + 1;
    }
    if(resource)
    {
        AXIS2_FREE(env->allocator, resource);
    }
    if(url_resource)
    {
        AXIS2_FREE(env->allocator, url_resource);
    }
    if(tmp2)
    {
        if(tmp2[0])
        {
            AXIS2_FREE(env->allocator, tmp2[0]);
        }
        if(tmp2[1])
        {
            AXIS2_FREE(env->allocator, tmp2[1]);
        }
        AXIS2_FREE(env->allocator, tmp2);
    }
    if(finished)
    {
        status = AXIS2_SUCCESS;
    }
    *match_count = ret_count;
    *matches = ret;
    return status;
}

AXIS2_EXTERN axis2_char_t **AXIS2_CALL
axutil_parse_request_url_for_svc_and_op(
    const axutil_env_t *env,
    const axis2_char_t *request)
{
    axis2_char_t **ret = NULL;
    axis2_char_t *service_str = NULL;
    axis2_char_t *tmp = NULL;
    int i = 0;
    ret = AXIS2_MALLOC(env->allocator, 2 * (sizeof(axis2_char_t *)));
    memset(ret, 0, 2 * sizeof(axis2_char_t *));
    tmp = (axis2_char_t *)request;

    tmp = strstr(tmp, axis2_request_url_prefix);
    if(tmp)
    {
        service_str = tmp;
        tmp += axutil_strlen(axis2_request_url_prefix);
        /*break stop on first prefix as user may have prefix in service name */
    }
    if(service_str)
    {
        service_str += axutil_strlen(axis2_request_url_prefix);
        if('\0' != *service_str)
        {
            if(*service_str == '/')
                service_str++; /*to remove the leading '/' */
            tmp = strchr(service_str, '/');
            if(tmp)
            {
                i = (int)(tmp - service_str);
                ret[0] = AXIS2_MALLOC(env->allocator, i * sizeof(char) + 1);
                strncpy(ret[0], service_str, i);
                ret[0][i] = '\0';

                /* Now search for the op */
                service_str = tmp;
                if('\0' != *service_str)
                {
                    service_str++;
                    tmp = strchr(service_str, '?');
                    if(tmp)
                    {
                        i = (int)(tmp - service_str);
                        ret[1] = AXIS2_MALLOC(env->allocator, i * sizeof(char) + 1);
                        strncpy(ret[1], service_str, i);
                        ret[1][i] = '\0';
                    }
                    else
                    {
                        ret[1] = axutil_strdup(env, service_str);
                    }
                }
            }
            else
            {
                ret[0] = axutil_strdup(env, service_str);
            }
        }
    }
    return ret;
}

AXIS2_EXTERN axis2_char_t *AXIS2_CALL
axutil_xml_quote_string(
    const axutil_env_t *env,
    const axis2_char_t *s,
    axis2_bool_t quotes)
{
    const char *scan;
    size_t len = 0;
    size_t extra = 0;
    char *qstr;
    char *qscan;
    char c;

    for(scan = s; (c = *scan) != '\0'; ++scan, ++len)
    {
        if(c == '<' || c == '>')
            extra += 3; /* &lt; or &gt; */
        else if(c == '&')
            extra += 4; /* &amp; */
        else if(quotes && c == '"')
            extra += 5; /* &quot; */
    }

    /* nothing to do */
    if(extra == 0)
        return NULL;

    qstr = AXIS2_MALLOC(env->allocator, len + extra + 1);
    for(scan = s, qscan = qstr; (c = *scan) != '\0'; ++scan)
    {
        if(c == '<')
        {
            *qscan++ = '&';
            *qscan++ = 'l';
            *qscan++ = 't';
            *qscan++ = ';';
        }
        else if(c == '>')
        {
            *qscan++ = '&';
            *qscan++ = 'g';
            *qscan++ = 't';
            *qscan++ = ';';
        }
        else if(c == '&')
        {
            *qscan++ = '&';
            *qscan++ = 'a';
            *qscan++ = 'm';
            *qscan++ = 'p';
            *qscan++ = ';';
        }
        else if(quotes && c == '"')
        {
            *qscan++ = '&';
            *qscan++ = 'q';
            *qscan++ = 'u';
            *qscan++ = 'o';
            *qscan++ = 't';
            *qscan++ = ';';
        }
        else
        {
            *qscan++ = c;
        }
    }

    *qscan = '\0';
    return qstr;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axutil_url_decode(
    const axutil_env_t *env,
    axis2_char_t *dest,
    axis2_char_t *src)
{
    AXIS2_PARAM_CHECK(env->error, dest, AXIS2_FAILURE);
    AXIS2_PARAM_CHECK(env->error, src, AXIS2_FAILURE);

    for(; *src != '\0'; ++dest, ++src)
    {
        if(src[0] == '%' && isxdigit(src[1]) && isxdigit(src[2]))
        {
            *dest = (axis2_char_t)(axutil_hexit(src[1]) * 16 + axutil_hexit(src[2]));
            /* We are sure that the conversion is safe */
            src += 2;
        }
        else
        {
            *dest = *src;
        }
    }
    *dest = '\0';

    return AXIS2_SUCCESS;
}

AXIS2_EXTERN int AXIS2_CALL
axutil_hexit(
    axis2_char_t c)
{
    if(c >= '0' && c <= '9')
    {
        return c - '0';
    }
    if(c >= 'a' && c <= 'f')
    {
        return c - 'a' + 10;
    }
    if(c >= 'A' && c <= 'F')
    {
        return c - 'A' + 10;
    }
    return 0;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axis2_char_2_byte(
    const axutil_env_t *env,
    axis2_char_t *char_buffer,
    axis2_byte_t **byte_buffer,
    int *byte_buffer_size)
{
    int length = 0;
    int i = 0;
    axis2_byte_t *bytes = NULL;

    length = (int)axutil_strlen(char_buffer);
    bytes = (axis2_byte_t *)AXIS2_MALLOC(env->allocator, length * sizeof(axis2_byte_t));

    if(!bytes)
    {
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "No memory. Cannot create byte buffer");
        return AXIS2_FAILURE;
    }

    for(i = 0; i < length; i++)
    {
        bytes[i] = (axis2_byte_t)char_buffer[i];
    }
    *byte_buffer = bytes;
    *byte_buffer_size = length;
    return AXIS2_SUCCESS;
}

