#include <basic/errcode.h>
#include <basic/misc.h>

/*==========*/
/* ???? */
/*==========*/
static char *_hex_char="0123456789ABCDEF";

static unsigned char _char_offset(char c)
{/*{{{*/
    unsigned char b;
    for(b=0; b<16; b++)
        if(_hex_char[b] == c)  return(b);
    return('\xff');
}/*}}}*/

char *expand_to_str(unsigned char *hex, int size, char *str)
{/*{{{*/
    char *strtmp;
    int strsize;
    int i;
    unsigned char h;

    if(!hex || size < 0 || !str)
        return(NULL);

    strsize=size*2;
    ALLOC_MULTI_RETERR(strtmp, char, strsize+1, NULL);
    for(i=0; i<size; i++)
    {
        h=hex[i];
        strtmp[i*2]=_hex_char[h >> 4];
        strtmp[i*2+1]=_hex_char[h & 0x0F];
    }
    strncpy(str, strtmp, strsize); 
    str[strsize]=ZERO;
    free(strtmp);
    return(str);
}/*}}}*/

unsigned char *collapse_from_str(char *str, int size, unsigned char *hex)
{/*{{{*/
    unsigned char *hextmp;
    int hexsize;
    int i;
    unsigned char high,low;

    if(!str || size < 0 || size & 0x01 || !hex)
        return(NULL);
 
    hexsize=size/2;
    ALLOC_MULTI_RETERR(hextmp, unsigned char, hexsize+1, NULL);
    for(i=0; i<size; i+=2)
    {
        high=_char_offset(str[i]);
        low=_char_offset(str[i+1]);
        if(high == 0xff || low == 0xff)
        {
            free(hextmp);
            return(NULL);
        }
        hextmp[i/2]=high<<4 | low;
    }
    memcpy(hex, hextmp, hexsize); 
    free(hextmp);
    return(hex);
}/*}}}*/

char *trim_head_space(char *str)
{/*{{{*/
    if(str)
    {
        int i, j;
        int len;

        len=strlen(str);
        i=0;
        while(str[i] == SPACE && i < len)  i++;
        for(j=i; j<len; j++)
            str[j-i]=str[j];
        str[j-i]=ZERO;
    }
    return(str);
}/*}}}*/

char *trim_tail_space(char *str)
{/*{{{*/
    if(str)
    {
        int i;

        if(str[0] == ZERO)  return(0);

        for(i=strlen(str)-1; i>=0; i--)
            if(str[i] != SPACE)  break;
        str[i+1]=ZERO;
    }
    return(str);
}/*}}}*/

char *trim_tail_space_notnull(char *str)
{/*{{{*/
    if(str)
    {
        trim_tail_space(str);
        if(str[0] == ZERO)
            strcpy(str, SPACES);
    }
    return(str);
}/*}}}*/

char *trim_redundant_space(char *str)
{/*{{{*/
    if(str)
    {
        int i;
        int str_size, str_real_size;
        enum { NOT_FOUND, FOUND } found;
        str_size=strlen(str);

        trim_head_space(str);
        trim_tail_space(str);
        found=NOT_FOUND;
        str_real_size=0;
        for(i=0; i<str_size; i++)
        {
            if(str[i]==SPACE && found==NOT_FOUND)
            {
                found=FOUND;
                str[str_real_size++]=str[i];
                continue;
            }
            if(str[i]==SPACE && found==FOUND)
            {   continue;   }

            found=NOT_FOUND;
            str[str_real_size++]=str[i];
        }
        str[str_real_size]=ZERO;
    }
    return(str);
}/*}}}*/

char *trim_space(char *str)
{/*{{{*/
    trim_head_space(str);
    trim_tail_space(str);
    return(str);
}/*}}}*/

char *pad_space(char *str, int buflen)
{/*{{{*/
    if(buflen <= 0)  return(NULL);
    if(str)
    {
        int str_len;

        if((str_len=strlen(str)) < buflen)
        {
            memset(str+str_len, SPACE, buflen-str_len);
            str[buflen]=ZERO;
        }
    }
    return(str);
}/*}}}*/

char *expand_tab(char *str, int buflen, int unit)
{/*{{{*/
    int slen;
    char *tmpstr;
    int src_index, dest_index;

    if(!str)  return(NULL);

    if(unit <= 0 || unit > 8)  unit=4;

    if(buflen > 0)
    {
        int tabcount;

        slen=0;  tabcount=0;
        while(str[slen] != ZERO)
        {
            if(str[slen] == TAB)  tabcount++;
            slen++;
        }
        if(slen+tabcount*(unit-1) > buflen-1)
            return(NULL);
    }
    else
        slen=strlen(str);

    ALLOC_MULTI_RETERR(tmpstr, char, slen+1, NULL);
    strcpy(tmpstr, str);
    for(src_index=0, dest_index=0; src_index<slen; src_index++)
    {
        if(tmpstr[src_index] == TAB)
        {
            memset(str+dest_index, SPACE, unit);
            dest_index+=unit;
        }
        else
        {
            str[dest_index]=tmpstr[src_index];
            dest_index++;
        }
    }
    str[dest_index]=ZERO;
    free(tmpstr);

    return(str);
}/*}}}*/

unsigned int get_hash(unsigned char *data, int len)
{/*{{{*/
    unsigned int hash;
    int i;

    if(!data || len <= 0)  return(0);

    hash=0x3e9a168c;
    for(i=0; i<len; i++)
        hash=(hash << 5) + hash + *data++;

    return(hash);
}/*}}}*/

int wrap_size(unsigned char *msg_in, int *msg_size,
        unsigned char *msg_out)
{/*{{{*/
    char buf[10];

    if(!msg_in ||
       !msg_size || *msg_size < 0 ||
       *msg_size > MSG_SIZE-MSG_SIZE_SIZE ||
       !msg_out)
        return(RET_ERR_PARA);

    sprintf(buf, "%06X", *msg_size);
    memmove(msg_out+MSG_SIZE_SIZE, msg_in, *msg_size);
    memcpy(msg_out, buf, SHORT_NUMBER_SIZE);
    (*msg_size)+=MSG_SIZE_SIZE;
    msg_out[*msg_size]=ZERO;

    return(0);
}/*}}}*/

int unwrap_size(unsigned char *msg_in, int *msg_size,
        unsigned char *msg_out)
{/*{{{*/
    char buf[10];

    if(!msg_in
            || !msg_size
            || *msg_size > MSG_SIZE || *msg_size < MSG_SIZE_SIZE
            || !msg_out)
        return(RET_ERR_PARA);

    memcpy(buf, msg_in, MSG_SIZE_SIZE);
    buf[MSG_SIZE_SIZE]=ZERO;
    *msg_size=strtol(buf, NULL, 16);
    memmove(msg_out, msg_in+MSG_SIZE_SIZE, *msg_size);
    msg_out[*msg_size]=ZERO;

    return(0);
}/*}}}*/

int split_str(char *str, char delim, char ***arrp)
{/*{{{*/
    char *in;
    char **arr;
    int str_size;
    char delimiter[2];
    char *itemp, *currp;
    int item_num;
    int item_alloc_num;

    if(!arrp)
        return(RET_ERR_PARA);
    *arrp=(char **)NULL;
    if(!str || (str_size=strlen(str)) <= 0 || str_size > MSG_SIZE)
        return(RET_ERR_PARA);

    if(!(in=strdup(str)))
        return(RET_ERR_ALLOC);

    if(delim == ZERO)
    {
        int len=strlen(in);
        while(len--)
            if(in[len] == TAB)  in[len]=SPACE;
        delim=SPACE;
    }
    if(delim == SPACE)
        trim_redundant_space(in);
    delimiter[0]=delim;  delimiter[1]=ZERO;

    item_num=item_alloc_num=0;
    arr=(char **)NULL;
    if((itemp=strtok_r(in, delimiter, &currp)))
    {
        do
        {
#define _EXPAND_NUM     5
            if(item_num == item_alloc_num)
                EXPAND_ARRAY(arr, char *, _EXPAND_NUM,
                        item_alloc_num, OUT_ARRAY);
            if(!(arr[item_num]=strdup(itemp)))
                goto OUT_ARRAY;
            item_num++;
        } while((itemp=strtok_r(NULL, delimiter, &currp)));
    }

    free(in);
    *arrp=arr;
    return(item_num);

OUT_ARRAY:
    FREE_PTR_ARRAY(arr, item_num);
    free(in);
    return(RET_ERR_ALLOC);
}/*}}}*/

char *str_toupper(char *str)
{/*{{{*/
    if(str)
    {
        int c;
        int len;

        len=strlen(str);
        while(len--)
        {
            c=toupper(str[len]);
            str[len]=c;
        }
    }
    return(str);
}/*}}}*/

char *str_tolower(char *str)
{/*{{{*/
    if(str)
    {
        int c;
        int len;

        len=strlen(str);
        while(len--)
        {
            c=tolower(str[len]);
            str[len]=c;
        }
    }
    return(str);
}/*}}}*/

char *replace_str(char *src, char *old, char *rep, char *dest) 
{/*{{{*/
    int len;
    int count;
    int src_size, old_size, rep_size, dest_size;
    char *srcp, *oldp, *destp, *allocp; 

    if(!src)  return(NULL);

    src_size=strlen(src);

    if(!old || !rep)
    {
        if(!dest)
            ALLOC_MULTI_RETERR(dest, char, src_size+1, NULL);
        if(src != dest)
            strcpy(dest, src);
        return(dest);
    }

    old_size=strlen(old);
    rep_size=strlen(rep);
    if(rep_size > old_size)
    {
        count=0;
        oldp=srcp=src;
        while((oldp=strstr(srcp, old)))
        {
            srcp=oldp+old_size;
            count++;
        }
        dest_size=src_size+count*(rep_size-old_size); 
    }
    else
        dest_size=src_size;
    if(dest == src)     // ??????????????
    {
        ALLOC_MULTI_RETERR(allocp, char, dest_size+1, NULL);
    }
    else
    {
        if(!dest)       // ???
            ALLOC_MULTI_RETERR(allocp, char, dest_size+1, NULL);
        else
            allocp=dest;
    }

    oldp=srcp=src;
    destp=allocp;
    while((oldp=strstr(srcp, old)))
    { 
        len=oldp-srcp;
        memcpy(destp, srcp, len); 
        destp+=len;

        memcpy(destp, rep, rep_size);
        destp+=rep_size;

        srcp=oldp+old_size;
    }
    len=src+src_size-srcp;
    memcpy(destp, srcp, len); 
    destp+=len;
    *destp=ZERO;

    if(dest == src)     // ??????????????
    {
        strcpy(dest, allocp);
        free(allocp);
    }
    else
    {
        if(!dest)       // ???
            dest=allocp;
    }

    return(dest);
}/*}}}*/

int str_is_dec(char *str)
{/*{{{*/
    if(str)
    {
        int i;
        int len=strlen(str);
        for(i=0; i<len; i++)
            if(!isdigit(str[i])) return(0);
        return(1);
    }
    return(0);

}/*}}}*/

int str_is_hex(char *str)
{/*{{{*/
    if(str)
    {
        int i;
        for(i=0; i<strlen(str); i++)
            if(!isxdigit(str[i])) return(0);
        return(1);
    }
    return(0);

}/*}}}*/

int dec_to_hex(int dec)
{/*{{{*/
    char buf[20];

    sprintf(buf, "%d", dec);
    return(strtol(buf, NULL, 16));
}/*}}}*/

int pack_size(int org_size, int unit)
{/*{{{*/
    int res;

    if(org_size <= 0)
        return(0);
    if(unit <= 1)
        return(org_size);
    res=org_size % unit;
    return(res ? org_size+unit-res : org_size);
}/*}}}*/

/*======*/
/* ?? */
/*======*/
unsigned short get_crc_16(unsigned char *p, int count)
{/*{{{*/
    static unsigned short a_crc_16[16]=
    {
        0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
        0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
    };
    unsigned short r, crc = 0;

    while(count--)
    {
        r=a_crc_16[crc & 0xF];
        crc=(crc>>4) & 0x0FFF;
        crc=crc^r^a_crc_16[*p & 0xF];
        r=a_crc_16[crc & 0xF];
        crc=(crc>>4) & 0x0FFF;
        crc=crc^r^a_crc_16[(*p>>4) & 0xF];
        p++;
    }
    return(crc);
}/*}}}*/

/*========*/
/* ??? */
/*========*/
int random_int(int min_val, int max_val, unsigned int *seedp)
{/*{{{*/
    if(min_val != max_val)
    {
        struct timeval time_val;
        unsigned int seed;

        if(min_val > max_val)
        {
            int tmp;
            tmp=min_val;
            min_val=max_val;
            max_val=tmp;
        }
        if(seedp)
        {
            if(*seedp == 0)
            {
                gettimeofday(&time_val, NULL);
                *seedp=(unsigned int)time_val.tv_usec;
            }
        }
        else
        {
            gettimeofday(&time_val, NULL);
            seed=time_val.tv_usec;
            seedp=&seed;
        }

        return(rand_r(seedp)%(max_val-min_val+1)+min_val);
    }
    return(min_val);
}/*}}}*/

int random_array_int(int array[], int num)
{/*{{{*/
    if(!array || num <= 0)
        return(RET_ERR_PARA);

    if(num > 1)
    {
        int *arraytmp;
        int idx;
        unsigned int seed=0;
        ALLOC_MULTI_RETERR(arraytmp, int, num, RET_ERR_ALLOC);
        memcpy(arraytmp, array, num*sizeof(int));
        while(num > 0)
        {
            idx=random_int(0, num-1, &seed);
            array[num-1]=arraytmp[idx];
            memmove(&arraytmp[idx], &arraytmp[idx+1],
                    (num-idx-1)*sizeof(int)); 
            num--;
        }
        free(arraytmp);
    }
    return(0);
}/*}}}*/

static char CHARBASE[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

char *random_password(char *password, int size)
{/*{{{*/
    if(!password)
       return(password);
    CLR_BUF(password);
   if(size <= 0)
        return(password);

    unsigned int seed=0;
    int i;
    for(i=0; i<size; ++i)
    {
        password[i]=CHARBASE[random_int(0, strlen(CHARBASE)-1, &seed)];
    }
    password[size]=ZERO;
    return(password);
}/*}}}*/

/*==============*/
/* ?????? */
/*==============*/
char *extract_dirname(char *filename, char *dirname)
{/*{{{*/
    if(dirname)
    {
        CLR_BUF(dirname);
        if(filename)
        {
            char fullname[LONG_FILE_NM_SIZE+1];
            char *p;
            int len;

            strcpy(fullname, filename);
            len=strlen(fullname);
            if(strcmp(fullname, "/"))
            {
                if(fullname[len-1] == SLASH)
                    fullname[len-1]=ZERO;
                if((p=strrchr(fullname, SLASH)))
                {
                    len=p-fullname+1;
                    strncpy(dirname, fullname, len);
                    if(len > 1) len--;
                    dirname[len]=ZERO;
                }
                else
                    strcpy(dirname, ".");
            }
            else
                strcpy(dirname, "/");
        }
    }
    return(dirname);
}/*}}}*/

char *extract_basename(char *filename, char *basename)
{/*{{{*/
    if(basename)
    {
        CLR_BUF(basename);
        if(filename)
        {
            char fullname[LONG_FILE_NM_SIZE+1];
            char *p;
            int len;

            strcpy(fullname, filename);
            len=strlen(fullname);
            if(strcmp(fullname, "/"))
            {
                if(fullname[len-1] == SLASH)
                    fullname[len-1]=ZERO;
                if((p=strrchr(fullname, SLASH)))
                    strcpy(basename, p+1);
                else
                    strcpy(basename, fullname);
            }
            else
                strcpy(basename, "/");
        }
    }
    return(basename);
}/*}}}*/

int remove_dir(char *path)
{/*{{{*/
    struct stat st;
    int len;
    DIR *dirp;
    struct dirent *dp;
    char buf[LONG_FILE_NM_SIZE+1];
    int result;

    if(stat(path, &st) != 0 || !S_ISDIR(st.st_mode))
    {   return(-1);   }
    len=strlen(path);
    dirp=opendir(path);
    while((dp=readdir(dirp)))
    {
        if(dp->d_name[0] == '.') 
            continue;
        sprintf(buf, "%s/%s", path, dp->d_name);
        if(stat(buf, &st) != 0)
        {   closedir(dirp); return(-1);   }
        //printf("%s\n", buf);
        if(S_ISDIR(st.st_mode))
        {       
            if((result=remove_dir(buf)))
            {   closedir(dirp); return(result);   }

        }       
        else    
            if(unlink(buf))
            {   closedir(dirp); return(-2);   }
    }
    closedir(dirp);
    if(rmdir(path))
    {   return(-3);    }

    return(0);
}/*}}}*/

int create_dir(char *dir)
{/*{{{*/
    int res;
    struct stat st;

    if(!dir)
        return(RET_ERR_PARA);

    if(stat(dir, &st))
    {
        if((res=get_last_error(NULL)) != ENOENT)
        {   return(-1);   }
        if(mkdir(dir, S_IRUSR | S_IWUSR | S_IXUSR |
                    S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1)
        {   return(-2);   }
    }
    else
    {
        if(!S_ISDIR(st.st_mode) || !(st.st_mode & S_IRUSR) ||
                !(st.st_mode & S_IWUSR) || !(st.st_mode & S_IXUSR))
        {   return(-3);   }
    } 
    return(0);
}/*}}}*/