/*======================================================================
 FILE: icalderivedproperty.c
 CREATOR: eric 09 May 1999

 (C) COPYRIGHT 1999, Eric Busboom <eric@civicknowledge.com>

 This library is free software; you can redistribute it and/or modify
 it under the terms of either:

    The LGPL as published by the Free Software Foundation, version
    2.1, available at: https://www.gnu.org/licenses/lgpl-2.1.html

  Or:

    The Mozilla Public License Version 2.0. You may obtain a copy of
    the License at https://www.mozilla.org/MPL/
 ======================================================================*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "icalderivedproperty.h"
#include "icalproperty.h"
#include "icalproperty_p.h"
#include "icalcomponent.h"
#include "icalerror.h"
#include "icalmemory.h"

#include <string.h>

/* This map associates the property kinds with:
   - the string representation of the property name;
   - the VALUE kind that libical uses internally;
   - the RFC 5545 VALUE kind that the property uses as a default (or NONE);
   - an array of RFC 5545 VALUE kinds that are legal for the property;
   - a bitmask of flags that further describe the property
*/

struct icalproperty_map
{
    icalproperty_kind kind;
    const char *name;
    icalvalue_kind libical_value;
    icalvalue_kind default_value;

    /* The size of this array needs to be 1 greater than the maximum number
       of component values in any multi-typed value (e.g. DATE-TIME-PERIOD).
       This size SHOULD be calculated by the generating perl script(s). */
    icalvalue_kind valid_values[4];

    unsigned int flags;
};

#define ICAL_PROPERTY_IS_STRUCTURED  (1U<<0)
#define ICAL_PROPERTY_IS_MULTIVALUED (1U<<1)

/* This map associates the property enumerations with the king of
   property that they are used in and the string representation of the
   enumeration */

struct icalproperty_enum_map
{
    icalproperty_kind prop;
    int prop_enum;
    const char *str;
};

<insert_code_here>

int icalproperty_kind_is_valid(const icalproperty_kind kind)
{
    int i = 0;
    int num_props = (int)(sizeof(property_map) / sizeof(property_map[0]));

    if (kind == ICAL_ANY_PROPERTY) {
        return 0;
    }

    num_props--;
    do {
        if (property_map[i].kind == kind) {
            return 1;
        }
    } while (i++ < num_props);

    return 0;
}

const char *icalproperty_kind_to_string(icalproperty_kind kind)
{
    int i, num_props;

    num_props = (int)(sizeof(property_map) / sizeof(property_map[0]));
    for (i = 0; i< num_props; i++) {
        if (property_map[i].kind == kind) {
            return property_map[i].name;
        }
    }

    return 0;
}

icalproperty_kind icalproperty_string_to_kind(const char *string)
{
    int i, num_props;

    if (string == 0) {
        return ICAL_NO_PROPERTY;
    }

    num_props = (int)(sizeof(property_map) / sizeof(property_map[0]));
    for (i = 0; i < num_props; i++) {
        if (property_map[i].name && (strcasecmp(property_map[i].name, string) == 0)) {
            return property_map[i].kind;
        }
    }

    if (strncmp(string, "X-", 2) == 0) {
        return ICAL_X_PROPERTY;
    }

    return ICAL_NO_PROPERTY;
}

icalproperty_kind icalproperty_value_kind_to_kind(icalvalue_kind kind)
{
    int i, num_props;

    num_props = (int)(sizeof(property_map) / sizeof(property_map[0]));
    for (i = 0; i < num_props; i++) {
        if (property_map[i].libical_value == kind) {
            return property_map[i].kind;
        }
    }

    return ICAL_NO_PROPERTY;
}

icalvalue_kind icalproperty_kind_to_value_kind(icalproperty_kind kind)
{
    int i, num_props;

    num_props = (int)(sizeof(property_map) / sizeof(property_map[0]));
    for (i = 0; i < num_props; i++) {
        const struct icalproperty_map *map = &property_map[i];

        if (map->kind == kind) {
            /* Return default value type if possible,
               otherwise return first legal value type */
            return (map->default_value != ICAL_NO_VALUE) ?
                map->default_value : map->valid_values[0];
        }
    }

    return ICAL_NO_VALUE;
}

int icalproperty_value_kind_is_valid(icalproperty_kind pkind,
                                     icalvalue_kind vkind)
{
    int i, j;
    int num_props;

    if (vkind == ICAL_NO_VALUE)
        return 0;

    if (pkind == ICAL_X_PROPERTY)
        return 1;

    num_props = (int)(sizeof(property_map) / sizeof(property_map[0]));
    for (i = 0; i < num_props; i++) {
        const struct icalproperty_map *map = &property_map[i];

        if (map->kind == pkind) {
            const icalvalue_kind *valid = map->valid_values;
            for (j = 0; valid[j] != ICAL_NO_VALUE; j++) {
                if (valid[j] == vkind) {
                    return 1;
                }
            }
            break;
        }
    }

    return 0;
}

int icalproperty_value_kind_is_multivalued(icalproperty_kind pkind,
                                           icalvalue_kind *vkind)
{
    int i, num_props;

    num_props = (int)(sizeof(property_map) / sizeof(property_map[0]));
    for (i = 0; i < num_props; i++) {
        const struct icalproperty_map *map = &property_map[i];

        if (map->kind == pkind) {
            if ((map->flags & ICAL_PROPERTY_IS_STRUCTURED) &&
                *vkind == map->default_value) {
                /* Normalize structured property VALUE type for proper parsing */
                *vkind = map->libical_value;
            }
            if ((map->flags & ICAL_PROPERTY_IS_MULTIVALUED)) {
                /* Property is defined as multi-valued.
                   Make sure the VALUE type is also defined as multi-valued. */
                switch (*vkind) {
                case ICAL_DATE_VALUE:
                case ICAL_DATETIME_VALUE:
                case ICAL_DATETIMEDATE_VALUE:
                case ICAL_DATETIMEPERIOD_VALUE:
                case ICAL_DURATION_VALUE:
                case ICAL_FLOAT_VALUE:
                case ICAL_INTEGER_VALUE:
                case ICAL_PERIOD_VALUE:
                case ICAL_TEXT_VALUE:
              //case ICAL_TIME_VALUE:
                case ICAL_X_VALUE:
                    return 1;
                    break;

                default:
                    break;
                }
            }

            break;
        }
    }

    return 0;
}

int icalproperty_value_kind_is_default(icalproperty_kind pkind,
                                       icalvalue_kind vkind)
{
    int i, num_props;

    num_props = (int)(sizeof(property_map) / sizeof(property_map[0]));
    for (i = 0; i < num_props; i++) {
        const struct icalproperty_map *map = &property_map[i];

        if (map->kind == pkind) {
            return ((vkind == map->default_value) ||
                    ((map->flags & ICAL_PROPERTY_IS_STRUCTURED) &&
                     (vkind == map->libical_value)));
        }
    }

    return 0;
}

const char *icalproperty_enum_to_string(int e)
{
    icalerror_check_arg_rz(e >= ICALPROPERTY_FIRST_ENUM, "e");
    icalerror_check_arg_rz(e <= ICALPROPERTY_LAST_ENUM, "e");

    return enum_map[e - ICALPROPERTY_FIRST_ENUM].str;
}

char *icalproperty_enum_to_string_r(int e)
{
    return icalmemory_strdup(icalproperty_enum_to_string(e));
}

int icalproperty_kind_and_string_to_enum(const int kind, const char *str)
{
    icalproperty_kind pkind;
    int i;

    icalerror_check_arg_rz(str != 0, "str");

    if ((pkind = icalproperty_value_kind_to_kind(kind)) == ICAL_NO_PROPERTY)
        return 0;

    while (*str == ' ') {
        str++;
    }

    for (i = ICALPROPERTY_FIRST_ENUM; i != ICALPROPERTY_LAST_ENUM; i++) {
        if (enum_map[i - ICALPROPERTY_FIRST_ENUM].prop == pkind)
            break;
    }
    if (i == ICALPROPERTY_LAST_ENUM)
        return 0;

    for (; i != ICALPROPERTY_LAST_ENUM; i++) {
        if (enum_map[i - ICALPROPERTY_FIRST_ENUM].prop == pkind &&
            strcasecmp(enum_map[i - ICALPROPERTY_FIRST_ENUM].str, str) == 0) {
            return enum_map[i - ICALPROPERTY_FIRST_ENUM].prop_enum;
        }
    }

    return 0;
}

int icalproperty_enum_belongs_to_property(icalproperty_kind kind, int e)
{
    int i;

    for (i = ICALPROPERTY_FIRST_ENUM; i != ICALPROPERTY_LAST_ENUM; i++) {
        if (enum_map[i - ICALPROPERTY_FIRST_ENUM].prop_enum == e &&
            enum_map[i - ICALPROPERTY_FIRST_ENUM].prop == kind) {
            return 1;
        }
    }

    return 0;
}

const char *icalproperty_method_to_string(icalproperty_method method)
{
    icalerror_check_arg_rz(method >= ICAL_METHOD_X, "method");
    icalerror_check_arg_rz(method <= ICAL_METHOD_NONE, "method");

    return enum_map[method - ICALPROPERTY_FIRST_ENUM].str;
}

icalproperty_method icalproperty_string_to_method(const char *str)
{
    int i;

    icalerror_check_arg_rx(str != 0, "str", ICAL_METHOD_NONE);

    while (*str == ' ') {
        str++;
    }

    for (i = ICAL_METHOD_X - ICALPROPERTY_FIRST_ENUM;
         i != ICAL_METHOD_NONE - ICALPROPERTY_FIRST_ENUM; i++) {
        if (strcasecmp(enum_map[i].str, str) == 0) {
            return (icalproperty_method) enum_map[i].prop_enum;
        }
    }

    return ICAL_METHOD_NONE;
}

const char *icalenum_status_to_string(icalproperty_status status)
{
    icalerror_check_arg_rz(status >= ICAL_STATUS_X, "status");
    icalerror_check_arg_rz(status <= ICAL_STATUS_NONE, "status");

    return enum_map[status - ICALPROPERTY_FIRST_ENUM].str;
}

icalproperty_status icalenum_string_to_status(const char *str)
{
    int i;

    icalerror_check_arg_rx(str != 0, "str", ICAL_STATUS_NONE);

    while (*str == ' ') {
        str++;
    }

    for (i = ICAL_STATUS_X - ICALPROPERTY_FIRST_ENUM;
         i != ICAL_STATUS_NONE - ICALPROPERTY_FIRST_ENUM; i++) {
        if (strcasecmp(enum_map[i].str, str) == 0) {
            return (icalproperty_status) enum_map[i].prop_enum;
        }
    }

    return ICAL_STATUS_NONE;
}
