#!/usr/bin/env python

#
# Copyright (c) 2006 Christoph Pfisterer
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
#  * Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the
#    distribution.
#
#  * Neither the name of Christoph Pfisterer nor the names of the
#    contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

#
# mk_fsw_strfunc.py
#

# definitions

types = {
    'ISO88591': 'fsw_u8',
    'UTF8': 'fsw_u8',
    'UTF16': 'fsw_u16',
    'UTF16_SWAPPED': 'fsw_u16',
}
getnext = {
    'ISO88591': 'VARC = *VARP++;',
    'UTF8': """VARC = *VARP++;
if ((VARC & 0xe0) == 0xc0) {
    VARC = ((VARC & 0x1f) << 6) | (*VARP++ & 0x3f);
} else if ((VARC & 0xf0) == 0xe0) {
    VARC = ((VARC & 0x0f) << 12) | ((*VARP++ & 0x3f) << 6);
    VARC |= (*VARP++ & 0x3f);
} else if ((VARC & 0xf8) == 0xf0) {
    VARC = ((VARC & 0x07) << 18) | ((*VARP++ & 0x3f) << 12);
    VARC |= ((*VARP++ & 0x3f) << 6);
    VARC |= (*VARP++ & 0x3f);
}""",
    'UTF16': 'VARC = *VARP++;',
    'UTF16_SWAPPED': 'VARC = *VARP++; VARC = FSW_SWAPVALUE_U16(VARC);',
}

combos = ( ('ISO88591', 'UTF8'), ('ISO88591', 'UTF16'), ('ISO88591', 'UTF16_SWAPPED'),
           ('UTF8', 'UTF16'), ('UTF8', 'UTF16_SWAPPED'),
           ('UTF16', 'UTF16_SWAPPED') )

coerce_combos = {}
for combo in combos:
    coerce_combos.setdefault(combo[0], []).append(combo[1])
    coerce_combos.setdefault(combo[1], []).append(combo[0])

# generate functions

output = """/* fsw_strfunc.h generated by mk_fsw_strfunc.py */
"""

# generate streq functions (symmetric)

for combo in combos:
    (enc1, enc2) = combo
    type1 = types[enc1]
    type2 = types[enc2]
    getnext1 = getnext[enc1].replace('VARC', 'c1').replace('VARP', 'p1').replace("\n", "\n        ")
    getnext2 = getnext[enc2].replace('VARC', 'c2').replace('VARP', 'p2').replace("\n", "\n        ")
    
    output += """
static int fsw_streq_%(enc1)s_%(enc2)s(void *s1data, void *s2data, int len)
{
    int i;
    %(type1)s *p1 = (%(type1)s *)s1data;
    %(type2)s *p2 = (%(type2)s *)s2data;
    fsw_u32 c1, c2;
    
    for (i = 0; i < len; i++) {
        %(getnext1)s
        %(getnext2)s
        if (c1 != c2)
            return 0;
    }
    return 1;
}
""" % locals()

# generate strcoerce functions (asymmetric, destination-specific)

for enc2 in ('ISO88591', 'UTF16'):
    for enc1 in coerce_combos[enc2]:
        type1 = types[enc1]
        type2 = types[enc2]
        getnext1 = getnext[enc1].replace('VARC', 'c').replace('VARP', 'sp').replace("\n", "\n        ")
        output += """
static fsw_status_t fsw_strcoerce_%(enc1)s_%(enc2)s(void *srcdata, int srclen, struct fsw_string *dest)
{
    fsw_status_t    status;
    int             i;
    %(type1)s       *sp;
    %(type2)s       *dp;
    fsw_u32         c;
    
    dest->type = FSW_STRING_TYPE_%(enc2)s;
    dest->len  = srclen;
    dest->size = srclen * sizeof(%(type2)s);
    status = fsw_alloc(dest->size, &dest->data);
    if (status)
        return status;
    
    sp = (%(type1)s *)srcdata;
    dp = (%(type2)s *)dest->data;
    for (i = 0; i < srclen; i++) {
        %(getnext1)s
        *dp++ = c;
    }
    return FSW_SUCCESS;
}
""" % locals()

for enc2 in ('UTF8',):
    for enc1 in coerce_combos[enc2]:
        type1 = types[enc1]
        type2 = types[enc2]
        getnext1 = getnext[enc1].replace('VARC', 'c').replace('VARP', 'sp').replace("\n", "\n        ")
        output += """
static fsw_status_t fsw_strcoerce_%(enc1)s_%(enc2)s(void *srcdata, int srclen, struct fsw_string *dest)
{
    fsw_status_t    status;
    int             i, destsize;
    %(type1)s       *sp;
    %(type2)s       *dp;
    fsw_u32         c;
    
    sp = (%(type1)s *)srcdata;
    destsize = 0;
    for (i = 0; i < srclen; i++) {
        %(getnext1)s
        
        if (c < 0x000080)
            destsize++;
        else if (c < 0x000800)
            destsize += 2;
        else if (c < 0x010000)
            destsize += 3;
        else
            destsize += 4;
    }
    
    dest->type = FSW_STRING_TYPE_%(enc2)s;
    dest->len  = srclen;
    dest->size = destsize;
    status = fsw_alloc(dest->size, &dest->data);
    if (status)
        return status;
    
    sp = (%(type1)s *)srcdata;
    dp = (%(type2)s *)dest->data;
    for (i = 0; i < srclen; i++) {
        %(getnext1)s
        
        if (c < 0x000080) {
            *dp++ = c;
        } else if (c < 0x000800) {
            *dp++ = 0xc0 | ((c >> 6) & 0x1f);
            *dp++ = 0x80 | (c & 0x3f);
        } else if (c < 0x010000) {
            *dp++ = 0xe0 | ((c >> 12) & 0x0f);
            *dp++ = 0x80 | ((c >> 6) & 0x3f);
            *dp++ = 0x80 | (c & 0x3f);
        } else {
            *dp++ = 0xf0 | ((c >> 18) & 0x07);
            *dp++ = 0x80 | ((c >> 12) & 0x3f);
            *dp++ = 0x80 | ((c >> 6) & 0x3f);
            *dp++ = 0x80 | (c & 0x3f);
        }
    }
    return FSW_SUCCESS;
}
""" % locals()

# coerce functions with destination UFT16_SWAPPED missing by design

# write output file

f = file("fsw_strfunc.h", "w")
f.write(output)
f.close()

# EOF
