/*
 *******************************************************************************
 *
 *   Copyright (C) 2003-2014, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *
 *******************************************************************************
 *   file name:  nfsprep.c
 *   encoding:   US-ASCII
 *   tab size:   8 (not used)
 *   indentation:4
 *
 *   created on: 2003jul11
 *   created by: Ram Viswanadha
 */

#include "unicode/utypes.h"

#if !UCONFIG_NO_IDNA

#include "nfsprep.h"
#include "ustr_imp.h"
#include "cintltst.h"
#include "cmemory.h"

#define NFS4_MAX_BUFFER_SIZE 1000
#define PREFIX_SUFFIX_SEPARATOR 0x0040 /* '@' */


const char* NFS4DataFileNames[5] ={
    "nfscss",
    "nfscsi",
    "nfscis",
    "nfsmxp",
    "nfsmxs"
};


int32_t
nfs4_prepare( const char* src, int32_t srcLength,
              char* dest, int32_t destCapacity,
              NFS4ProfileState state,
              UParseError* parseError,
              UErrorCode*  status){
    
    UChar b1Stack[NFS4_MAX_BUFFER_SIZE], 
          b2Stack[NFS4_MAX_BUFFER_SIZE]; 
    char  b3Stack[NFS4_MAX_BUFFER_SIZE];

    /* initialize pointers to stack buffers */
    UChar *b1 = b1Stack, *b2 = b2Stack;
    char  *b3=b3Stack;
    int32_t b1Len=0, b2Len=0, b3Len=0,
            b1Capacity = NFS4_MAX_BUFFER_SIZE, 
            b2Capacity = NFS4_MAX_BUFFER_SIZE,
            b3Capacity = NFS4_MAX_BUFFER_SIZE,
            reqLength=0;
    
    UStringPrepProfile* profile = NULL;
    /* get the test data path */
    const char *testdatapath = NULL;

    if(status==NULL || U_FAILURE(*status)){
        return 0;
    }
    if((src==NULL) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){
        *status = U_ILLEGAL_ARGUMENT_ERROR;
        return 0;
    }
    testdatapath = loadTestData(status);

    /* convert the string from UTF-8 to UTF-16 */
    u_strFromUTF8(b1,b1Capacity,&b1Len,src,srcLength,status);
    if(*status == U_BUFFER_OVERFLOW_ERROR){

        /* reset the status */
        *status = U_ZERO_ERROR;
        
        b1 = (UChar*) malloc(b1Len * U_SIZEOF_UCHAR);
        if(b1==NULL){
            *status = U_MEMORY_ALLOCATION_ERROR;
            goto CLEANUP;
        }

        b1Capacity = b1Len;
        u_strFromUTF8(b1, b1Capacity, &b1Len, src, srcLength, status);
    }

    /* open the profile */
    profile = usprep_open(testdatapath, NFS4DataFileNames[state],  status);
    /* prepare the string */
    b2Len = usprep_prepare(profile, b1, b1Len, b2, b2Capacity, USPREP_DEFAULT, parseError, status);
    if(*status == U_BUFFER_OVERFLOW_ERROR){
        *status = U_ZERO_ERROR;
        b2 = (UChar*) malloc(b2Len * U_SIZEOF_UCHAR);
        if(b2== NULL){
            *status = U_MEMORY_ALLOCATION_ERROR;
            goto CLEANUP;
        }
        b2Len = usprep_prepare(profile, b1, b1Len, b2, b2Len, USPREP_DEFAULT, parseError, status);
    }
    
    /* convert the string back to UTF-8 */
    u_strToUTF8(b3,b3Capacity, &b3Len, b2, b2Len, status);
    if(*status == U_BUFFER_OVERFLOW_ERROR){
        *status = U_ZERO_ERROR;
        b3 = (char*) malloc(b3Len);
        if(b3== NULL){
            *status = U_MEMORY_ALLOCATION_ERROR;
            goto CLEANUP;
        }
        b3Capacity = b3Len;
        u_strToUTF8(b3,b3Capacity, &b3Len, b2, b2Len, status);
    }

    reqLength = b3Len;
    if(dest!=NULL && reqLength <= destCapacity){
        memmove(dest, b3, reqLength);
    }

CLEANUP:
    if(b1!=b1Stack){
        free(b1);
    }
    if(b2!=b2Stack){
        free(b2);
    }
    if(b3!=b3Stack){
        free(b3);
    }

    return u_terminateChars(dest, destCapacity, reqLength, status);
}

/* sorted array for binary search*/
static const char* special_prefixes[]={
    "\x0041\x004e\x004f\x004e\x0059\x004d\x004f\x0055\x0053",    
    "\x0041\x0055\x0054\x0048\x0045\x004e\x0054\x0049\x0043\x0041\x0054\x0045\x0044",
    "\x0042\x0041\x0054\x0043\x0048", 
    "\x0044\x0049\x0041\x004c\x0055\x0050", 
    "\x0045\x0056\x0045\x0052\x0059\x004f\x004e\x0045", 
    "\x0047\x0052\x004f\x0055\x0050",
    "\x0049\x004e\x0054\x0045\x0052\x0041\x0043\x0054\x0049\x0056\x0045",  
    "\x004e\x0045\x0054\x0057\x004f\x0052\x004b", 
    "\x004f\x0057\x004e\x0045\x0052",
};


/* binary search the sorted array */
static int 
findStringIndex(const char* const *sortedArr, int32_t sortedArrLen, const char* target, int32_t targetLen){

    int left, middle, right,rc;

    left =0;
    right= sortedArrLen-1;

    while(left <= right){
        middle = (left+right)/2;
        rc=strncmp(sortedArr[middle],target, targetLen);
    
        if(rc<0){
            left = middle+1;
        }else if(rc >0){
            right = middle -1;
        }else{
            return middle;
        }
    }
    return -1;
}

static void 
getPrefixSuffix(const char *src, int32_t srcLength, 
                const char **prefix, int32_t *prefixLen,
                const char **suffix, int32_t *suffixLen,
                UErrorCode *status){

    int32_t i=0;
    *prefix = src;
    while(i<srcLength){
        if(src[i] == PREFIX_SUFFIX_SEPARATOR){
            if((i+1) == srcLength){
                /* we reached the end of the string */
                *suffix = NULL;
                i++;
                break;
            }
            i++;/* the prefix contains the separator */
            *suffix = src + i;
            break;
        }
        i++;
    }
    *prefixLen = i;
    *suffixLen = srcLength - i;
    /* special prefixes must not be followed by suffixes! */
    if((findStringIndex(special_prefixes,UPRV_LENGTHOF(special_prefixes), *prefix, *prefixLen-1) != -1) && (*suffix != NULL)){
        *status = U_PARSE_ERROR;
        return;
    }
            
}

int32_t
nfs4_mixed_prepare( const char* src, int32_t srcLength,
                    char* dest, int32_t destCapacity,
                    UParseError* parseError,
                    UErrorCode*  status){

    const char *prefix = NULL, *suffix = NULL;
    int32_t prefixLen=0, suffixLen=0;
    char  pStack[NFS4_MAX_BUFFER_SIZE], 
          sStack[NFS4_MAX_BUFFER_SIZE]; 
    char *p=pStack, *s=sStack;
    int32_t pLen=0, sLen=0, reqLen=0,
            pCapacity = NFS4_MAX_BUFFER_SIZE,
            sCapacity = NFS4_MAX_BUFFER_SIZE;


    if(status==NULL || U_FAILURE(*status)){
        return 0;
    }
    if((src==NULL) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){
        *status = U_ILLEGAL_ARGUMENT_ERROR;
        return 0;
    }
    if(srcLength == -1){
        srcLength = (int32_t)strlen(src);
    }
    getPrefixSuffix(src, srcLength, &prefix, &prefixLen, &suffix, &suffixLen, status); 

    /* prepare the prefix */
    pLen = nfs4_prepare(prefix, prefixLen, p, pCapacity, NFS4_MIXED_PREP_PREFIX, parseError, status);
    if(*status == U_BUFFER_OVERFLOW_ERROR){
        *status = U_ZERO_ERROR;
        p = (char*) malloc(pLen);
        if(p == NULL){
           *status = U_MEMORY_ALLOCATION_ERROR;
           goto CLEANUP;
        }
        pLen = nfs4_prepare(prefix, prefixLen, p, pLen, NFS4_MIXED_PREP_PREFIX, parseError, status);
    }

    /* prepare the suffix */
    if(suffix != NULL){
        sLen = nfs4_prepare(suffix, suffixLen, s, sCapacity, NFS4_MIXED_PREP_SUFFIX, parseError, status);
        if(*status == U_BUFFER_OVERFLOW_ERROR){
            *status = U_ZERO_ERROR;
            s = (char*) malloc(pLen);
            if(s == NULL){
               *status = U_MEMORY_ALLOCATION_ERROR;
               goto CLEANUP;
            }
            sLen = nfs4_prepare(suffix, suffixLen, s, sLen, NFS4_MIXED_PREP_SUFFIX, parseError, status);
        }
    }
    reqLen = pLen+sLen+1 /* for the delimiter */;
    if(dest != NULL && reqLen <= destCapacity){
        memmove(dest, p, pLen);
        /* add the suffix */
        if(suffix!=NULL){
            dest[pLen++] = PREFIX_SUFFIX_SEPARATOR;
            memmove(dest+pLen, s, sLen);
        }
    }

CLEANUP:
    if(p != pStack){
        free(p);
    }
    if(s != sStack){
        free(s);
    }
    
    return u_terminateChars(dest, destCapacity, reqLen, status);
}

int32_t
nfs4_cis_prepare(   const char* src, int32_t srcLength,
                    char* dest, int32_t destCapacity,
                    UParseError* parseError,
                    UErrorCode*  status){
    return nfs4_prepare(src, srcLength, dest, destCapacity, NFS4_CIS_PREP, parseError, status);
}


int32_t
nfs4_cs_prepare(    const char* src, int32_t srcLength,
                    char* dest, int32_t destCapacity,
                    UBool isCaseSensitive,
                    UParseError* parseError,
                    UErrorCode*  status){
    if(isCaseSensitive){
        return nfs4_prepare(src, srcLength, dest, destCapacity, NFS4_CS_PREP_CS, parseError, status);
    }else{
        return nfs4_prepare(src, srcLength, dest, destCapacity, NFS4_CS_PREP_CI, parseError, status);
    }
}

#endif
/*
 * Hey, Emacs, please set the following:
 *
 * Local Variables:
 * indent-tabs-mode: nil
 * End:
 *
 */

