/*
 *                         OpenSplice DDS
 *
 *   This software and documentation are Copyright 2006 to TO_YEAR PrismTech
 *   Limited, its affiliated companies and licensors. All rights reserved.
 *
 *   Licensed 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 <math.h>
#include <stdlib.h>

#include "c_typebase.h"
#include "c_base.h" /* for c_keep and c_free */

#include "os_errno.h"
#include "os_report.h"
#include "os_abstract.h"
#include "os_stdlib.h"

#ifdef NDEBUG
#define purify_memset(v,b,s)
#else
#define purify_memset(v,b,s) memset(v,b,s)
#endif

c_value
c_undefinedValue()
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_UNDEFINED;
    return v;
}

c_value
c_shortValue (
    const c_short value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_SHORT;
    v.is.Short = value;
    return v;
}

c_value
c_addressValue (
    const c_address value)
{
    c_value v;

    memset(&v, 0, sizeof(v));
    v.kind = V_ADDRESS;
    v.is.Address = value;
    return v;
}

c_value
c_longValue (
    const c_long value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_LONG;
    v.is.Long = value;
    return v;
}

c_value
c_longlongValue (
    const c_longlong value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_LONGLONG;
    v.is.LongLong = value;
    return v;
}

c_value
c_ushortValue (
    const c_ushort value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_USHORT;
    v.is.UShort = value;
    return v;
}

c_value
c_ulongValue (
    const c_ulong value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_ULONG;
    v.is.ULong = value;
    return v;
}

c_value
c_ulonglongValue (
    const c_ulonglong value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_ULONGLONG;
    v.is.ULongLong = value;
    return v;
}

c_value
c_boolValue (
    const c_bool value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_BOOLEAN;
    v.is.Boolean = value;
    return v;
}

c_value
c_octetValue (
    const c_octet value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_OCTET;
    v.is.Octet = value;
    return v;
}

c_value
c_charValue (
    const c_char value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_CHAR;
    v.is.Char = value;
    return v;
}

c_value
c_wcharValue (
    const c_wchar value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_WCHAR;
    v.is.WChar = value;
    return v;
}

c_value
c_floatValue (
    const c_float value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_FLOAT;
    v.is.Float = value;
    return v;
}

c_value
c_doubleValue (
    const c_double value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_DOUBLE;
    v.is.Double = value;
    return v;
}

c_value
c_stringValue (
    const c_string value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_STRING;
    v.is.String = value;
    return v;
}

c_value
c_wstringValue (
    const c_wstring value)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_WSTRING;
    v.is.WString = value;
    return v;
}

c_value
c_objectValue (
    const c_voidp object)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_OBJECT;
    v.is.Object = object;
    return v;
}

c_value
c_voidpValue (
    const c_voidp object)
{
    c_value v;

    purify_memset(&v, 0, sizeof(v));
    v.kind = V_VOIDP;
    v.is.Voidp = object;
    return v;
}

c_value
c_shortMinValue()
{
    return c_shortValue(C_MIN_SHORT);
}

c_value
c_longMinValue()
{
    return c_longValue(C_MIN_LONG);
}

c_value
c_longlongMinValue()
{
    return c_longlongValue(C_MIN_LONGLONG);
}

c_value
c_ushortMinValue()
{
    return c_ushortValue(C_MIN_USHORT);
}

c_value
c_ulongMinValue()
{
    return c_ulongValue(C_MIN_ULONG);
}

c_value
c_ulonglongMinValue()
{
    return c_ulonglongValue(C_MIN_ULONGLONG);
}

c_value
c_boolMinValue()
{
    return c_boolValue(0);
}

c_value
c_octetMinValue()
{
    return c_octetValue(C_MIN_OCTET);
}

c_value
c_charMinValue()
{
    c_value val;
    /* A char can be signed or unsigned by default,
     * depending on platform and compiler flags. */
    if ((char)-1 < 0) {
        /* char is signed */
        val = c_charValue((c_char)C_MIN_SCHAR);
    } else {
        /* char is unsigned */
        val = c_charValue((c_char)C_MIN_UCHAR);
    }
    return val;
}

c_value
c_floatMinValue()
{
    return c_floatValue(C_MIN_FLOAT);
}

c_value
c_doubleMinValue()
{
    return c_doubleValue(C_MIN_DOUBLE);
}

c_value
c_shortMaxValue()
{
    return c_shortValue(C_MAX_SHORT);
}

c_value
c_longMaxValue()
{
    return c_longValue(C_MAX_LONG);
}

c_value
c_longlongMaxValue()
{
    return c_longlongValue(C_MAX_LONGLONG);
}

c_value
c_ushortMaxValue()
{
    return c_ushortValue(C_MAX_USHORT);
}

c_value
c_ulongMaxValue()
{
    return c_ulongValue(C_MAX_ULONG);
}

c_value
c_ulonglongMaxValue()
{
    return c_ulonglongValue(C_MAX_ULONGLONG);
}

c_value
c_boolMaxValue()
{
    return c_boolValue(1);
}

c_value
c_octetMaxValue()
{
    return c_octetValue(C_MAX_OCTET);
}

c_value
c_charMaxValue()
{
    c_value val;
    /* A char can be signed or unsigned by default,
     * depending on platform and compiler flags. */
    if ((char)-1 < 0) {
        /* char is signed */
        val = c_charValue((c_char)C_MAX_SCHAR);
    } else {
        /* char is unsigned */
        val = c_charValue((c_char)C_MAX_UCHAR);
    }
    return val;
}

c_value
c_floatMaxValue()
{
    return c_floatValue(C_MAX_FLOAT);
}

c_value
c_doubleMaxValue()
{
    return c_doubleValue(C_MAX_DOUBLE);
}


c_char *
c_valueImage(
    const c_value value)
{
    char buf[1024];

    switch (value.kind) {
    case V_ADDRESS:
        {
            snprintf(buf,1024,"0x"PA_ADDRFMT,(PA_ADDRCAST)value.is.Address);
        }
    break;
    case V_BOOLEAN:
        if (value.is.Boolean) {
            return os_strdup("TRUE");
        } else {
            return os_strdup("FALSE");
        }
    case V_LONGLONG:
        {
            char llstr[36];
            (void)os_lltostr(value.is.LongLong, llstr, sizeof(llstr), NULL);
            snprintf(buf,1024,"%s",llstr);
        }
    break;
    case V_ULONGLONG:
        {
            char llstr[36];
            (void)os_ulltostr(value.is.ULongLong, llstr, sizeof(llstr), NULL);
            snprintf(buf,1024,"%s",llstr);
        }
    break;
    case V_SHORT:     snprintf(buf,1024,"%d",value.is.Short); break;
    case V_LONG:      snprintf(buf,1024,"%d",value.is.Long); break;
    case V_OCTET:     snprintf(buf,1024,"%u",value.is.Octet); break;
    case V_USHORT:    snprintf(buf,1024,"%u",value.is.UShort); break;
    case V_ULONG:     snprintf(buf,1024,"%u",value.is.ULong); break;
    case V_CHAR:
    {
        unsigned char c = (unsigned char) value.is.Char;

            if (c < 32 || c >= 127) {
                snprintf(buf,1024,"\\%03o",c);
            } else {
                snprintf(buf,1024,"%c",c);
            }

    }
    break;
    case V_STRING:
    {
        if (value.is.String == NULL) {
            snprintf(buf,1024,"(NULL)");
        } else {
            snprintf(buf,1024,"%s",value.is.String);
        }
    }
    break;
     case  V_FLOAT:     snprintf(buf,1024,"%0.7g",value.is.Float); break;
     case  V_DOUBLE:    snprintf(buf,1024,"%0.15g",value.is.Double); break;
     case  V_WCHAR:     snprintf(buf,1024,"(a-wchar-value)"); break;
     case  V_WSTRING:   snprintf(buf,1024,"0x"PA_ADDRFMT, (PA_ADDRCAST)value.is.WString); break;
     case  V_FIXED:     snprintf(buf,1024,"0x"PA_ADDRFMT, (PA_ADDRCAST)value.is.Fixed); break;
     case  V_OBJECT:    snprintf(buf,1024,"0x"PA_ADDRFMT, (PA_ADDRCAST)value.is.Object); break;
     case  V_VOIDP:     snprintf(buf,1024,"0x"PA_ADDRFMT, (PA_ADDRCAST)value.is.Voidp); break;
     case  V_UNDEFINED: snprintf(buf,1024,"(an-undefined-value)"); break;
     default: assert(FALSE);
    }
    return os_strdup(buf);
}

c_bool
c_imageValue(
    const char *image,
    c_value *imgValue,
    c_type imgType)
{
    c_type t;
    char *endptr;

    assert(imgType);

    t = c_typeActualType(imgType);
    switch(c_baseObjectKind(t)) {
    case M_ENUMERATION:
    {
        c_literal l = c_enumValue(c_enumeration(imgType), image);
        if (l) {
            *imgValue = l->value;
            c_free(l);
        } else {
            imgValue->kind = V_UNDEFINED;
            OS_REPORT(OS_ERROR,
                         "c_typebase::c_imageValue",0,
                         "expected legal enum label instead of \"%s\".",
                         image);
        }
        break;
    }
    case M_COLLECTION:
        if (c_collectionTypeKind(t) == OSPL_C_STRING) {
            if(imgValue->is.String){
                c_free(imgValue->is.String);
            }
            imgValue->is.String = c_stringNew(c_getBase(imgType), image);
            imgValue->kind = V_STRING;
        }
        break;
    case M_PRIMITIVE:
        switch (c_primitiveKind(t)) {
        case P_BOOLEAN:
            if (os_strncasecmp(image,"TRUE",5) == 0) {
                imgValue->kind = V_BOOLEAN;
                imgValue->is.Boolean = TRUE;
            } else if (os_strncasecmp(image,"FALSE",6) == 0) {
                imgValue->kind = V_BOOLEAN;
                imgValue->is.Boolean = FALSE;
            } else {
                imgValue->kind = V_UNDEFINED;
            }
            break;
        case P_LONGLONG:
            imgValue->is.LongLong = os_strtoll (image, &endptr, 0);
            if (*endptr == '\0') {
                imgValue->kind = V_LONGLONG;
            } else {
                imgValue->kind = V_UNDEFINED;
            }
            break;
        case P_ULONGLONG:
            imgValue->is.ULongLong = os_strtoull (image, &endptr, 0);
            if (*endptr == '\0') {
                imgValue->kind = V_ULONGLONG;
            } else {
                imgValue->kind = V_UNDEFINED;
            }
            break;
        case P_SHORT:
            if (sscanf(image,"%hd",&imgValue->is.Short)) {
                imgValue->kind = V_SHORT;
            } else {
                imgValue->kind = V_UNDEFINED;
            }
            break;
        case P_LONG:
            imgValue->is.Long = (c_long) strtol (image, &endptr, 0);
            if (*endptr == '\0') {
                imgValue->kind = V_LONG;
            } else {
                imgValue->kind = V_UNDEFINED;
            }
            break;
        case P_OCTET:
        {
            c_short s;

            if (sscanf(image,"%hd",&s)) {
                imgValue->kind = V_OCTET;
                imgValue->is.Octet = (c_octet)s;
            } else {
                imgValue->kind = V_UNDEFINED;
            }
            break;
        }
        case P_USHORT:
            if (sscanf(image,"%hu",&imgValue->is.UShort)) {
                imgValue->kind = V_USHORT;
            } else {
                imgValue->kind = V_UNDEFINED;
            }
            break;
        case P_ULONG:
            imgValue->is.ULong = (c_ulong) strtoul (image, &endptr, 0);
            if (*endptr == '\0') {
                imgValue->kind = V_ULONG;
            } else {
                imgValue->kind = V_UNDEFINED;
            }
            break;
        case P_CHAR:
            if (image != NULL) {
                imgValue->kind = V_CHAR;
                imgValue->is.Char = *image;
            } else {
                imgValue->kind = V_UNDEFINED;
            }
            break;
        case  P_FLOAT:
            imgValue->is.Float = os_strtof (image, &endptr);
            if (*endptr == '\0') {
                imgValue->kind = V_FLOAT;
            } else {
                imgValue->kind = V_UNDEFINED;
            }
            break;
        case  P_DOUBLE:
            imgValue->is.Double = os_strtod (image, &endptr);
            if (*endptr == '\0') {
                imgValue->kind = V_DOUBLE;
            } else {
                imgValue->kind = V_UNDEFINED;
            }
            break;
        default:
            assert(0);
        }
        break;
     default:
         assert(FALSE);
         break;
    }
    return (imgValue->kind != V_UNDEFINED);
}

c_equality
c_valueCompare(
    const c_value v1,
    const c_value v2)
{
    c_long r;

#define _CASE_(l,f) case l: if (v1.is.f>v2.is.f) return C_GT; \
                            if (v1.is.f<v2.is.f) return C_LT; \
                            return C_EQ

    switch(v1.kind) {
    _CASE_(V_ADDRESS,Address);
    _CASE_(V_BOOLEAN,Boolean);
    _CASE_(V_SHORT,Short);
    _CASE_(V_LONG,Long);
    _CASE_(V_LONGLONG,LongLong);
    _CASE_(V_OCTET,Octet);
    _CASE_(V_USHORT,UShort);
    _CASE_(V_ULONG,ULong);
    _CASE_(V_ULONGLONG,ULongLong);
    _CASE_(V_CHAR,Char);
    _CASE_(V_WCHAR,WChar);
    _CASE_(V_FLOAT,Float);
    _CASE_(V_DOUBLE,Double);
    _CASE_(V_OBJECT,Object);
    _CASE_(V_VOIDP,Voidp);
     case V_STRING:
     case V_WSTRING:
     case V_FIXED:
        if (v1.is.String == v2.is.String) {
            return C_EQ;
        }
        if (v1.is.String == NULL) {
            return C_LT;
        }
        if (v2.is.String == NULL) {
            return C_GT;
        }
        r = strcmp(v1.is.String,v2.is.String);
        if (r>0) {
            return C_GT;
        }
        if (r<0) {
            return C_LT;
        }
        return C_EQ;
     case V_UNDEFINED:
     case V_COUNT:
         assert(FALSE);
     break;
    }
#undef _CASE_
    assert(FALSE);
    return C_NE;
}

#if 0
/* Some compilers have an optimizer that turns this very simple matcher
   into something just as fast as the one below, but not all of them. So
   better use the one in which the recursion has been partially eliminated
   by hand. */
static int patmatch (const char *pat, const char *str)
{
    switch (*pat) {
        case 0:   return *str == 0;
        case '?': return *str == 0 ? 0 : patmatch (pat+1, str+1);
        case '*': return patmatch (pat+1, str) || (*str != 0 && patmatch (pat, str+1));
        default:  return *str != *pat ? 0 : patmatch (pat+1, str+1);
    }
}
#else
static int patmatch (const char *pat, const char *str)
{
    while (*pat) {
        if (*pat == '?') {
            /* any character will do */
            if (*str++ == 0) {
                return 0;
            }
            pat++;
        } else if (*pat == '*') {
            /* collapse a sequence of wildcards, requiring as many
               characters in str as there are ?s in the sequence */
            while (*pat == '*' || *pat == '?') {
                if (*pat == '?' && *str++ == 0) {
                    return 0;
                }
                pat++;
            }
            /* try matching on all positions where str matches pat */
            while (*str) {
                if (*str == *pat && patmatch (pat+1, str+1)) {
                    return 1;
                }
                str++;
            }
            return *pat == 0;
        } else {
            /* only an exact match */
            if (*str++ != *pat++) {
                return 0;
            }
        }
    }
    return *str == 0;
}
#endif

c_value
c_valueStringMatch (
    const c_value patternValue,
    const c_value stringValue)
{
    c_value v;
    c_char *pattern;
    c_char *str;

    assert(patternValue.kind == V_STRING);
    assert(stringValue.kind == V_STRING);

    pattern = patternValue.is.String;
    str = stringValue.is.String;
    assert(pattern != NULL);

    v.kind = V_BOOLEAN;
    v.is.Boolean = (c_bool) patmatch (pattern, str ? str : "");
    return v;
}


c_value
c_valueADD (
    c_value v1,
    c_value v2)
{
#define _CASE_(l,t,ct) case l: v1.is.t = (ct) (v1.is.t + v2.is.t); break
    switch (v1.kind) {
    _CASE_(V_ADDRESS,Address,c_address);
    _CASE_(V_BOOLEAN,Boolean,c_bool);
    _CASE_(V_SHORT,Short,c_short);
    _CASE_(V_LONG,Long,c_long);
    _CASE_(V_LONGLONG,LongLong,c_longlong);
    _CASE_(V_OCTET,Octet,c_octet);
    _CASE_(V_USHORT,UShort,c_ushort);
    _CASE_(V_ULONG,ULong,c_ulong);
    _CASE_(V_ULONGLONG,ULongLong,c_ulonglong);
    _CASE_(V_CHAR,Char,c_char);
    _CASE_(V_WCHAR,WChar,c_wchar);
    _CASE_(V_FLOAT,Float,c_float);
    _CASE_(V_DOUBLE,Double,c_double);
     default: assert(FALSE);
    }
    return v1;
#undef _CASE_
}

c_value
c_valueSUB (
    c_value v1,
    c_value v2)
{
#define _CASE_(l,t,ct) case l: v1.is.t = (ct) (v1.is.t - v2.is.t); break
    switch (v1.kind) {
    _CASE_(V_ADDRESS,Address,c_address);
    _CASE_(V_BOOLEAN,Boolean,c_bool);
    _CASE_(V_SHORT,Short,c_short);
    _CASE_(V_LONG,Long,c_long);
    _CASE_(V_LONGLONG,LongLong,c_longlong);
    _CASE_(V_OCTET,Octet,c_octet);
    _CASE_(V_USHORT,UShort,c_ushort);
    _CASE_(V_ULONG,ULong,c_ulong);
    _CASE_(V_ULONGLONG,ULongLong,c_ulonglong);
    _CASE_(V_CHAR,Char,c_char);
    _CASE_(V_WCHAR,WChar,c_wchar);
    _CASE_(V_FLOAT,Float,c_float);
    _CASE_(V_DOUBLE,Double,c_double);
     default: assert(FALSE);
    }
    return v1;
#undef _CASE_
}

c_value
c_valueLOR (
    c_value v1,
    c_value v2)
{
#define _CASE_(l,t,ct) case l: v1.is.t = (ct) (v1.is.t | v2.is.t); break
    switch (v1.kind) {
    _CASE_(V_ADDRESS,Address,c_address);
    _CASE_(V_BOOLEAN,Boolean,c_bool);
    _CASE_(V_SHORT,Short,c_short);
    _CASE_(V_LONG,Long,c_long);
    _CASE_(V_LONGLONG,LongLong,c_longlong);
    _CASE_(V_OCTET,Octet,c_octet);
    _CASE_(V_USHORT,UShort,c_ushort);
    _CASE_(V_ULONG,ULong,c_ulong);
    _CASE_(V_ULONGLONG,ULongLong,c_ulonglong);
    _CASE_(V_CHAR,Char,c_char);
    _CASE_(V_WCHAR,WChar,c_wchar);
     default: assert(FALSE);
    }
    return v1;
#undef _CASE_
}

c_value
c_valueLXOR (
    c_value v1,
    c_value v2)
{
#define _CASE_(l,t,ct) case l: v1.is.t = (ct) (v1.is.t ^ v2.is.t); break
    switch (v1.kind) {
    _CASE_(V_ADDRESS,Address,c_address);
    _CASE_(V_BOOLEAN,Boolean,c_bool);
    _CASE_(V_SHORT,Short,c_short);
    _CASE_(V_LONG,Long,c_long);
    _CASE_(V_LONGLONG,LongLong,c_longlong);
    _CASE_(V_OCTET,Octet,c_octet);
    _CASE_(V_USHORT,UShort,c_ushort);
    _CASE_(V_ULONG,ULong,c_ulong);
    _CASE_(V_ULONGLONG,ULongLong,c_ulonglong);
    _CASE_(V_CHAR,Char,c_char);
    _CASE_(V_WCHAR,WChar,c_wchar);
     default: assert(FALSE);
    }
    return v1;
#undef _CASE_
}

c_value
c_valueLAND (
    c_value v1,
    c_value v2)
{
#define _CASE_(l,t,ct) case l: v1.is.t = (ct) (v1.is.t & v2.is.t); break
    switch (v1.kind) {
    _CASE_(V_ADDRESS,Address,c_address);
    _CASE_(V_BOOLEAN,Boolean,c_bool);
    _CASE_(V_SHORT,Short,c_short);
    _CASE_(V_LONG,Long,c_long);
    _CASE_(V_LONGLONG,LongLong,c_longlong);
    _CASE_(V_OCTET,Octet,c_octet);
    _CASE_(V_USHORT,UShort,c_ushort);
    _CASE_(V_ULONG,ULong,c_ulong);
    _CASE_(V_ULONGLONG,ULongLong,c_ulonglong);
    _CASE_(V_CHAR,Char,c_char);
    _CASE_(V_WCHAR,WChar,c_wchar);
     default: assert(FALSE);
    }
    return v1;
#undef _CASE_
}

c_value
c_valueMUL (
    c_value v1,
    c_value v2)
{
#define _CASE_(l,t,ct) case l: v1.is.t = (ct) (v1.is.t * v2.is.t); break
    switch (v1.kind) {
    _CASE_(V_ADDRESS,Address,c_address);
    _CASE_(V_BOOLEAN,Boolean,c_bool);
    _CASE_(V_SHORT,Short,c_short);
    _CASE_(V_LONG,Long,c_long);
    _CASE_(V_LONGLONG,LongLong,c_longlong);
    _CASE_(V_OCTET,Octet,c_octet);
    _CASE_(V_USHORT,UShort,c_ushort);
    _CASE_(V_ULONG,ULong,c_ulong);
    _CASE_(V_ULONGLONG,ULongLong,c_ulonglong);
    _CASE_(V_CHAR,Char,c_char);
    _CASE_(V_WCHAR,WChar,c_wchar);
    _CASE_(V_FLOAT,Float,c_float);
    _CASE_(V_DOUBLE,Double,c_double);
     default: assert(FALSE);
    }
    return v1;
#undef _CASE_
}

c_value
c_valueDIV (
    c_value v1,
    c_value v2)
{
#define _CASE_(l,t,ct) case l: v1.is.t = (ct) (v1.is.t / v2.is.t); break
    switch (v1.kind) {
    _CASE_(V_ADDRESS,Address,c_address);
    _CASE_(V_BOOLEAN,Boolean,c_bool);
    _CASE_(V_SHORT,Short,c_short);
    _CASE_(V_LONG,Long,c_long);
    _CASE_(V_LONGLONG,LongLong,c_longlong);
    _CASE_(V_OCTET,Octet,c_octet);
    _CASE_(V_USHORT,UShort,c_ushort);
    _CASE_(V_ULONG,ULong,c_ulong);
    _CASE_(V_ULONGLONG,ULongLong,c_ulonglong);
    _CASE_(V_CHAR,Char,c_char);
    _CASE_(V_WCHAR,WChar,c_wchar);
    _CASE_(V_FLOAT,Float,c_float);
    _CASE_(V_DOUBLE,Double,c_double);
     default: assert(FALSE);
    }
    return v1;
#undef _CASE_
}

c_value
c_valueMOD (
    c_value v1,
    c_value v2)
{
#define _CASE_(l,t,ct) case l: v1.is.t = (ct) (v1.is.t % v2.is.t); break
    switch (v1.kind) {
    _CASE_(V_ADDRESS,Address,c_address);
    _CASE_(V_BOOLEAN,Boolean,c_bool);
    _CASE_(V_SHORT,Short,c_short);
    _CASE_(V_LONG,Long,c_long);
    _CASE_(V_LONGLONG,LongLong,c_longlong);
    _CASE_(V_OCTET,Octet,c_octet);
    _CASE_(V_USHORT,UShort,c_ushort);
    _CASE_(V_ULONG,ULong,c_ulong);
    _CASE_(V_ULONGLONG,ULongLong,c_ulonglong);
    _CASE_(V_CHAR,Char,c_char);
    _CASE_(V_WCHAR,WChar,c_wchar);
     default: assert(FALSE);
    }
    return v1;
#undef _CASE_
}

c_value
c_valuePOW (
    c_value v1,
    c_value v2)
{
#define _CASE_(l,s,t) case l: v1.is.s = \
                              (t)pow((double)v1.is.s, (double)v2.is.s); break
    switch (v1.kind) {
    _CASE_(V_ADDRESS,Address,c_address);
    _CASE_(V_SHORT,Short,c_short);
    _CASE_(V_LONG,Long,c_long);
    _CASE_(V_LONGLONG,LongLong,c_longlong);
    _CASE_(V_OCTET,Octet,c_octet);
    _CASE_(V_USHORT,UShort,c_ushort);
    _CASE_(V_ULONG,ULong,c_ulong);
    _CASE_(V_ULONGLONG,ULongLong,c_ulonglong);
    _CASE_(V_CHAR,Char,c_char);
    _CASE_(V_WCHAR,WChar,c_wchar);
    _CASE_(V_FLOAT,Float,c_float);
    _CASE_(V_DOUBLE,Double,c_double);
     default: assert(FALSE);
    }
    return v1;
#undef _CASE_
}

c_value
c_valueSL (
    c_value v,
    c_value a)
{
#define _CASE_(l,t,ct) case l: v.is.t = (ct) (v.is.t << a.is.t); break
    switch(v.kind) {
    _CASE_(V_ADDRESS,Address,c_address);
    _CASE_(V_SHORT,Short,c_short);
    _CASE_(V_LONG,Long,c_long);
    _CASE_(V_LONGLONG,LongLong,c_longlong);
    _CASE_(V_OCTET,Octet,c_octet);
    _CASE_(V_USHORT,UShort,c_ushort);
    _CASE_(V_ULONG,ULong,c_ulong);
    _CASE_(V_ULONGLONG,ULongLong,c_ulonglong);
    _CASE_(V_CHAR,Char,c_char);
    _CASE_(V_WCHAR,WChar,c_wchar);
     default: assert(FALSE);
    }
    return v;
#undef _CASE_
}

c_value
c_valueSR (
    c_value v,
    c_value a)
{
#define _CASE_(l,t,ct) case l: v.is.t = (ct) (v.is.t >> a.is.t); break
    switch(v.kind) {
    _CASE_(V_ADDRESS,Address,c_address);
    _CASE_(V_SHORT,Short,c_short);
    _CASE_(V_LONG,Long,c_long);
    _CASE_(V_LONGLONG,LongLong,c_longlong);
    _CASE_(V_OCTET,Octet,c_octet);
    _CASE_(V_USHORT,UShort,c_ushort);
    _CASE_(V_ULONG,ULong,c_ulong);
    _CASE_(V_ULONGLONG,ULongLong,c_ulonglong);
    _CASE_(V_CHAR,Char,c_char);
    _CASE_(V_WCHAR,WChar,c_wchar);
     default: assert(FALSE);
    }
    return v;
#undef _CASE_
}

c_value
c_valueCast (
    const c_value v,
    c_valueKind kind)
{
    c_value r;
    if (v.kind == kind) {
        return v;
    }
    r.kind = kind;
    switch(kind) {
    case V_CHAR: switch(v.kind) {
        case V_OCTET:     r.is.Char = (c_char)v.is.Octet; break;
        case V_STRING:    r.is.Char = *(c_char*)v.is.String; break;
        default:          r.kind = V_UNDEFINED; break;
        }
    break;
    case V_SHORT: switch(v.kind) {
        case V_ADDRESS:   r.is.Short = (c_short)v.is.Address; break;
        case V_CHAR:      r.is.Short = (c_short)v.is.Char; break;
        case V_OCTET:     r.is.Short = (c_short)v.is.Octet; break;
        case V_LONG:      r.is.Short = (c_short)v.is.Long; break;
        case V_LONGLONG:  r.is.Short = (c_short)v.is.LongLong; break;
        case V_USHORT:    r.is.Short = (c_short)v.is.UShort; break;
        case V_ULONG:     r.is.Short = (c_short)v.is.ULong; break;
        case V_ULONGLONG: r.is.Short = (c_short)v.is.ULongLong; break;
        case V_WCHAR:     r.is.Short = (c_short)v.is.WChar; break;
        default:          r.kind = V_UNDEFINED; break;
        }
    break;
    case V_LONG: switch(v.kind) {
        case V_ADDRESS:   r.is.Long = (c_long)v.is.Address; break;
        case V_OCTET:     r.is.Long = (c_long)v.is.Octet; break;
        case V_CHAR:      r.is.Long = (c_long)v.is.Char; break;
        case V_SHORT:     r.is.Long = (c_long)v.is.Short; break;
        case V_LONGLONG:  r.is.Long = (c_long)v.is.LongLong; break;
        case V_USHORT:    r.is.Long = (c_long)v.is.UShort; break;
        case V_ULONG:     r.is.Long = (c_long)v.is.ULong; break;
        case V_ULONGLONG: r.is.Long = (c_long)v.is.ULongLong; break;
        case V_WCHAR:     r.is.Long = (c_long)v.is.WChar; break;
        default:          r.kind = V_UNDEFINED; break;
        }
    break;
    case V_LONGLONG: switch(v.kind) {
        case V_ADDRESS:   r.is.LongLong = (c_longlong)v.is.Address; break;
        case V_OCTET:     r.is.LongLong = (c_longlong)v.is.Octet; break;
        case V_CHAR:      r.is.LongLong = (c_longlong)v.is.Char; break;
        case V_SHORT:     r.is.LongLong = (c_longlong)v.is.Short; break;
        case V_LONG:      r.is.LongLong = (c_longlong)v.is.Long; break;
        case V_USHORT:    r.is.LongLong = (c_longlong)v.is.UShort; break;
        case V_ULONG:     r.is.LongLong = (c_longlong)v.is.ULong; break;
        case V_ULONGLONG:  r.is.LongLong = (c_longlong)v.is.ULongLong; break;
        case V_WCHAR:     r.is.LongLong = (c_longlong)v.is.WChar; break;
        default:          r.kind = V_UNDEFINED; break;
        }
    break;
    case V_FLOAT: switch(v.kind) {
        case V_ADDRESS:   r.is.Float = (c_float)v.is.Address; break;
        case V_OCTET:     r.is.Float = (c_float)v.is.Octet; break;
        case V_CHAR:      r.is.Float = (c_float)v.is.Char; break;
        case V_WCHAR:     r.is.Float = (c_float)v.is.WChar; break;
        case V_SHORT:     r.is.Float = (c_float)v.is.Short; break;
        case V_LONG:      r.is.Float = (c_float)v.is.Long; break;
        case V_LONGLONG:  r.is.Float = (c_float)v.is.LongLong; break;
        case V_USHORT:    r.is.Float = (c_float)v.is.UShort; break;
        case V_ULONG:     r.is.Float = (c_float)v.is.ULong; break;
        case V_ULONGLONG: r.is.Float = (c_float)v.is.ULongLong; break;
        case V_DOUBLE:    r.is.Float = (c_float)v.is.Double; break;
        default:          r.kind = V_UNDEFINED; break;
        }
    break;
    case V_DOUBLE: switch(v.kind) {
        case V_ADDRESS:   r.is.Double = (c_double)v.is.Address; break;
        case V_OCTET:     r.is.Double = (c_double)v.is.Octet; break;
        case V_CHAR:      r.is.Double = (c_double)v.is.Char; break;
        case V_WCHAR:     r.is.Double = (c_double)v.is.WChar; break;
        case V_SHORT:     r.is.Double = (c_double)v.is.Short; break;
        case V_LONG:      r.is.Double = (c_double)v.is.Long; break;
        case V_LONGLONG:  r.is.Double = (c_double)v.is.LongLong; break;
        case V_USHORT:    r.is.Double = (c_double)v.is.UShort; break;
        case V_ULONG:     r.is.Double = (c_double)v.is.ULong; break;
        case V_ULONGLONG: r.is.Double = (c_double)v.is.ULongLong; break;
        case V_FLOAT:     r.is.Double = (c_double)v.is.Float; break;
        default:          r.kind = V_UNDEFINED; break;
        }
    break;
    case V_USHORT: switch(v.kind) {
        case V_ADDRESS:   r.is.UShort = (c_ushort)v.is.Address; break;
        case V_OCTET:     r.is.UShort = (c_ushort)v.is.Octet; break;
        case V_WCHAR:     r.is.UShort = (c_ushort)v.is.WChar; break;
        case V_CHAR:      r.is.UShort = (c_ushort)v.is.Char; break;
        case V_LONG:      r.is.UShort = (c_ushort)v.is.Long; break;
        case V_LONGLONG:  r.is.UShort = (c_ushort)v.is.LongLong; break;
        case V_USHORT:    r.is.UShort = (c_ushort)v.is.UShort; break;
        case V_ULONG:     r.is.UShort = (c_ushort)v.is.ULong; break;
        case V_ULONGLONG: r.is.UShort = (c_ushort)v.is.ULongLong; break;
        default:          r.kind = V_UNDEFINED; break;
        }
    break;
    case V_ULONG: switch(v.kind) {
        case V_ADDRESS:   r.is.ULong = (c_ulong)v.is.Address; break;
        case V_CHAR:      r.is.ULong = (c_ulong)v.is.Char; break;
        case V_OCTET:     r.is.ULong = (c_ulong)v.is.Octet; break;
        case V_SHORT:     r.is.ULong = (c_ulong)v.is.Short; break;
        case V_LONG:      r.is.ULong = (c_ulong)v.is.Long; break;
        case V_LONGLONG:  r.is.ULong = (c_ulong)v.is.LongLong; break;
        case V_USHORT:    r.is.ULong = (c_ulong)v.is.UShort; break;
        case V_ULONG:     r.is.ULong = (c_ulong)v.is.ULong; break;
        case V_ULONGLONG: r.is.ULong = (c_ulong)v.is.ULongLong; break;
        case V_WCHAR:     r.is.ULong = (c_ulong)v.is.WChar; break;
        default:          r.kind = V_UNDEFINED; break;
        }
    break;
    case V_ULONGLONG: switch(v.kind) {
        case V_ADDRESS:   r.is.ULongLong = (c_ulonglong)v.is.Address; break;
        case V_CHAR:      r.is.ULongLong = (c_ulonglong)v.is.Char; break;
        case V_SHORT:     r.is.ULongLong = (c_ulonglong)v.is.Short; break;
        case V_LONG:      r.is.ULongLong = (c_ulonglong)v.is.Long; break;
        case V_LONGLONG:  r.is.ULongLong = (c_ulonglong)v.is.LongLong; break;
        case V_OCTET:     r.is.ULongLong = (c_ulonglong)v.is.Octet; break;
        case V_USHORT:    r.is.ULongLong = (c_ulonglong)v.is.UShort; break;
        case V_ULONG:     r.is.ULongLong = (c_ulonglong)v.is.ULong; break;
        case V_WCHAR:     r.is.ULongLong = (c_ulonglong)v.is.WChar; break;
        default:          r.kind = V_UNDEFINED; break;
        }
    break;
    case V_WCHAR: switch(v.kind) {
        case V_CHAR:      r.is.WChar = (c_wchar)v.is.Char; break;
        case V_OCTET:     r.is.WChar = (c_wchar)v.is.Octet; break;
        default:          r.kind = V_UNDEFINED; break;
        }
    break;
    case V_OCTET: switch(v.kind) {
        case V_ADDRESS:   r.is.Octet = (c_octet)v.is.Address; break;
        case V_CHAR:      r.is.Octet = (c_octet)v.is.Char; break;
        case V_WCHAR:     r.is.Octet = (c_octet)v.is.WChar; break;
        case V_SHORT:     r.is.Octet = (c_octet)v.is.Short; break;
        case V_LONG:      r.is.Octet = (c_octet)v.is.Long; break;
        case V_LONGLONG:  r.is.Octet = (c_octet)v.is.LongLong; break;
        case V_USHORT:    r.is.Octet = (c_octet)v.is.UShort; break;
        case V_ULONG:     r.is.Octet = (c_octet)v.is.ULong; break;
        case V_ULONGLONG: r.is.Octet = (c_octet)v.is.ULongLong; break;
        default:          r.kind = V_UNDEFINED; break;
        }
    break;
    case V_BOOLEAN: switch(v.kind) {
        case V_ADDRESS:   r.is.Boolean = (c_bool)v.is.Address; break;
        case V_OCTET:     r.is.Boolean = (c_bool)v.is.Octet; break;
        case V_CHAR:      r.is.Boolean = (c_bool)v.is.Char; break;
        case V_SHORT:     r.is.Boolean = (c_bool)v.is.Short; break;
        case V_LONG:      r.is.Boolean = (c_bool)v.is.Long; break;
        case V_USHORT:    r.is.Boolean = (c_bool)v.is.UShort; break;
        case V_WCHAR:     r.is.Boolean = (c_bool)v.is.WChar; break;
        default:          r.kind = V_UNDEFINED; break;
        }
    break;
    default:              r.kind = V_UNDEFINED; break;
    }
    return r;
}

c_valueKind
c_normalizedKind (
    c_value v1,
    c_value v2)
{
    if (v1.kind == v2.kind) {
        return v1.kind;
    }
    switch(v1.kind) {

    case V_STRING: switch(v2.kind) {
        case V_CHAR:
            if (strlen(v1.is.String) == 1) {
                return v2.kind;
            }
        break;
        default:
        break;
        }
    break;
    case V_CHAR: switch(v2.kind) {
        case V_OCTET:
            return v1.kind;
        case V_STRING:
            if (strlen(v2.is.String) == 1) {
                return v1.kind;
            }
        break;
        default:
            return v2.kind;
        }
    break;
    case V_SHORT: switch(v2.kind) {
        case V_CHAR:
        case V_OCTET:
            return v1.kind;
        default:
            return v2.kind;
        }
    case V_LONG: switch(v2.kind) {
        case V_OCTET:
        case V_CHAR:
        case V_SHORT:
        case V_USHORT:
        case V_WCHAR:
        case V_LONGLONG:
            return v1.kind;
        default:
            return v2.kind;
        }
    case V_LONGLONG: switch(v2.kind) {
        case V_OCTET:
        case V_CHAR:
        case V_SHORT:
        case V_LONG:
        case V_USHORT:
        case V_ULONG:
        case V_WCHAR:
            return v1.kind;
        default:
            return v2.kind;
        }
    case V_FLOAT: switch(v2.kind) {
        case V_OCTET:
        case V_CHAR:
        case V_SHORT:
        case V_LONG:
        case V_LONGLONG:
        case V_USHORT:
        case V_ULONG:
        case V_ULONGLONG:
        case V_WCHAR:
            return v1.kind;
        default:
            return v2.kind;
        }
    case V_DOUBLE: switch(v2.kind) {
        case V_OCTET:
        case V_CHAR:
        case V_SHORT:
        case V_LONG:
        case V_LONGLONG:
        case V_USHORT:
        case V_ULONG:
        case V_ULONGLONG:
        case V_WCHAR:
            return v1.kind;
        default:
            return v2.kind;
        }
    case V_USHORT: switch(v2.kind) {
        case V_OCTET:
        case V_WCHAR:
            return v1.kind;
        default:
            return v2.kind;
        }
    case V_ULONG: switch(v2.kind) {
        case V_OCTET:
        case V_USHORT:
        case V_WCHAR:
            return v1.kind;
        default:
            return v2.kind;
        }
    case V_ULONGLONG: switch(v2.kind) {
        case V_OCTET:
        case V_USHORT:
        case V_ULONG:
        case V_WCHAR:
            return v1.kind;
        default:
            return v2.kind;
        }
    case V_WCHAR: switch(v2.kind) {
        case V_CHAR:
        case V_OCTET:
            return v1.kind;
        default:
            return v2.kind;
        }
    case V_OCTET: switch(v2.kind) {
        case V_CHAR:
            return v1.kind;
        default:
            return v2.kind;
        }
    case V_BOOLEAN: switch(v2.kind) {
        case V_OCTET:
        case V_CHAR:
        case V_SHORT:
        case V_LONG:
        case V_USHORT:
        case V_WCHAR:
            return v1.kind;
        default:
            return v2.kind;
        }
    default:
    break;
    }
    return V_UNDEFINED;
}

c_char *
c_operatorImage(
    c_operator o)
{
    switch(o) {
    case O_NE: return "NE";
    case O_EQ: return "EQ";
    case O_LT: return "LT";
    case O_GT: return "GT";
    case O_LE: return "LE";
    case O_GE: return "GE";
    case O_AND: return "AND";
    case O_OR: return "OR";
    case O_LEFT: return "LEFT";
    case O_RIGHT: return "RIGHT";
    case O_LOR: return "LOR";
    case O_LXOR: return "LXOR";
    case O_LAND: return "LAND";
    case O_ADD: return "ADD";
    case O_SUB: return "SUB";
    case O_MUL: return "MUL";
    case O_DIV: return "DIV";
    case O_MOD: return "MOD";
    case O_POW: return "POW";
    default: return "<Unknown>";
    }
}

c_value
c_valueCalculate(
    const c_value v1,
    const c_value v2,
    const c_operator o)
{
    c_value v1Cast;
    c_value v2Cast;
    c_value v;
    c_valueKind kind;

    purify_memset(&v, 0, sizeof(v));
    kind = c_normalizedKind(v1,v2);
    if (v1.kind != kind) {
        v1Cast = c_valueCast(v1,kind);
    } else {
        memcpy(&v1Cast, &v1, sizeof(v1));
    }
    if (v2.kind != kind) {
        v2Cast = c_valueCast(v2,kind);
    } else {
        memcpy(&v2Cast, &v2, sizeof(v2));
    }

    v.kind = V_BOOLEAN;
#define _EVAL_(l,r,o) (c_valueCompare(l,r) == o)
    switch(o) {
    case O_NE:    v.is.Boolean = !_EVAL_(v1Cast,v2Cast,C_EQ); break;
    case O_EQ:    v.is.Boolean =  _EVAL_(v1Cast,v2Cast,C_EQ); break;
    case O_LT:    v.is.Boolean =  _EVAL_(v1Cast,v2Cast,C_LT); break;
    case O_GT:    v.is.Boolean =  _EVAL_(v1Cast,v2Cast,C_GT); break;
    case O_LE:    v.is.Boolean =  _EVAL_(v1Cast,v2Cast,C_LT) || _EVAL_(v1Cast,v2Cast,C_EQ); break;
    case O_GE:    v.is.Boolean =  _EVAL_(v1Cast,v2Cast,C_GT) || _EVAL_(v1Cast,v2Cast,C_EQ); break;
    case O_AND:   v.is.Boolean = (v1Cast.is.Boolean && v2Cast.is.Boolean);  break;
    case O_OR:    v.is.Boolean = (v1Cast.is.Boolean || v2Cast.is.Boolean);  break;
    case O_LEFT:  return c_valueSL  (v1Cast,v2Cast);
    case O_RIGHT: return c_valueSR  (v1Cast,v2Cast);
    case O_LOR:   return c_valueLOR (v1Cast,v2Cast);
    case O_LXOR:  return c_valueLXOR(v1Cast,v2Cast);
    case O_LAND:  return c_valueLAND(v1Cast,v2Cast);
    case O_ADD:   return c_valueADD (v1Cast,v2Cast);
    case O_SUB:   return c_valueSUB (v1Cast,v2Cast);
    case O_MUL:   return c_valueMUL (v1Cast,v2Cast);
    case O_DIV:   return c_valueDIV (v1Cast,v2Cast);
    case O_MOD:   return c_valueMOD (v1Cast,v2Cast);
    case O_POW:   return c_valuePOW (v1Cast,v2Cast);
    default:      assert(FALSE);
    }
#undef _EVAL_

    return v;
}

c_value
c_valueKeepRef(
    c_value v)
{
    if (v.kind == V_STRING) {
        c_keep(v.is.String);
    } else if (v.kind == V_OBJECT) {
        c_keep(v.is.Object);
    } /* else { } */
    return v;
}

c_value
c_valueFreeRef(
    c_value v)
{
    if (v.kind == V_STRING) {
        c_free(v.is.String);
    } else if (v.kind == V_OBJECT) {
        c_free(v.is.Object);
    } /* else { } */
    return v;
}

