/*
 * Copyright (c) 1994, 2004 Alex Leigh, All Rights Reserved
 *
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 *
 * Use, disclosure, copying, distribution, or transmission of this code without
 * express permission is prohibited.
 */

#include "mecha.h"

struct cSval {
    int type;

    char *str_ptr;
    size_t str_len;

    double dbl;
    long long integer;

    float flt;

    void (*obj_destruct)(void *, size_t);
    void *(*obj_copy)(void *, size_t);
    int (*obj_compare)(void *, void *, size_t);
    void *obj_ptr;
    size_t obj_size;

    size_t refcount;
};

int cFval_type_get(cTval *v)
{
    assert(v!=NULL);
    return v->type;
}

void cFval_used(cTval *v) {
    assert(v!=NULL);
    v->refcount++;
}

cTval *cFval_init(void)
{
    cTval *v = (cTval *)malloc(sizeof(cTval));
    
    assert(v!=NULL);
    
    v->type=CV_EMPTY;
    v->refcount=0;
    return v;
}

void cFval_empty(cTval *v)
{
    assert(v!=NULL);
    
    switch(v->type) {
        case CV_EMPTY:
            return;

        case CV_STRING:
            if(v->str_ptr!=NULL) {
                free(v->str_ptr);
            }
            break;
            
        case CV_OBJECT:
            if(v->obj_ptr!=NULL && v->obj_destruct!=NULL) {
                v->obj_destruct(v->obj_ptr, v->obj_size);
            }
            v->obj_destruct=NULL;
            break;
            
                /* CV_FLOAT: Do nothing */
                /* CV_INTEGER: Do nothing */
    }
    
    v->type=CV_EMPTY;
}

void cFval_free(cTval *v)
{
    assert(v!=NULL);

    if(v->refcount>1) {
        v->refcount--;
        return;
    }
    
    cFval_empty(v);

    free(v);
}

void cFval_assign_string(cTval *v, char *str, size_t len)
{
    assert(v!=NULL);

    cFval_empty(v);

    if(str==NULL) {
      v->type=CV_EMPTY;
      return;
    }

    v->type=CV_STRING;
    v->str_ptr=str;
    v->str_len=len;
}

void cFval_assign_string_copy(cTval *v, const char *str, size_t len)
{
    assert(v!=NULL);

    cFval_empty(v);

    v->type=CV_STRING;
    v->str_ptr=strFncopy(str, len);
    v->str_len=len;
}

void cFval_assign_object(cTval *v, void *obj, size_t size)
{    
    assert(v!=NULL);

    cFval_empty(v);

    v->type=CV_OBJECT;
    v->obj_ptr=obj;
    v->obj_size=size;
}

void cFval_assign_object_tors(cTval *v,
                              void (*obj_destructor)(void *, size_t),
                              void *(*obj_duplicator)(void *, size_t),
                              int (*obj_comparator)(void *, void *, size_t))
{
    assert(v!=NULL);

    v->obj_destruct=obj_destructor;
    v->obj_copy=obj_duplicator;
    v->obj_compare=obj_comparator;
}

void cFval_assign_float(cTval *v, double value)
{
    assert(v!=NULL);
    
    cFval_empty(v);

    v->type=CV_FLOAT;
    v->dbl=value;
}

void cFval_assign_integer(cTval *v, long long value)
{
    assert(v!=NULL);

    cFval_empty(v);

    v->type=CV_INTEGER;
    v->integer=value;
}

long long cFval_get_integer(cTval *v)
{
    assert(v!=NULL);

        /* todo: support object converter -aleigh */
    switch(v->type) {
        case CV_INTEGER:
            return v->integer;
        case CV_FLOAT:
            return (long long) v->dbl;
        case CV_STRING:
                return atoll(v->str_ptr);
        default:
            return 0;
    }
}

double cFval_get_float(cTval *v)
{
    assert(v!=NULL);

    switch(v->type) {
        case CV_INTEGER:
            return (double) v->integer;
        case CV_FLOAT:
            return v->dbl;
        case CV_STRING:
            return (double)atoll(v->str_ptr);
            
        default:
            return 0;
    }

        /* todo: Support object converter -aleigh */
}

/* Get a const char pointer to the string stored in the cval.
 * No conversion to other types is supported because that would
 * require the function to return orphaned memory.
 */
const char *cFval_get_string(cTval *v, size_t *size)
{
    assert(v!=NULL);

    if(v->type==CV_STRING) {
        *size=v->str_len;
        return v->str_ptr;
    }

    return NULL;
}

ssize_t cFval_get_string_copy(cTval *v, char *buf, size_t buf_size)
{
    assert(v!=NULL);

    switch(v->type) {
        case CV_INTEGER:
            return snprintf(buf,buf_size,"%Ld",v->integer);
        case CV_FLOAT:
            return snprintf(buf,buf_size,"%e",v->dbl);
        case CV_STRING:
            if(buf_size<v->str_len) {
                memFcopy(v->str_ptr,buf,buf_size-1);
                buf[buf_size]=0;
                return buf_size;
            } else {
                memFcopy(v->str_ptr,buf,v->str_len);
                return v->str_len;
            }
        default:
            return -1;
    }
}

cTval *cFval_copy(cTval *v)
{
    cTval *new=cFval_init();

    assert(v!=NULL);
    assert(new!=NULL);

    switch(v->type) {
        case CV_STRING:
            new->type=CV_STRING;
            new->str_ptr=strFncopy(v->str_ptr,v->str_len);
            new->str_len=v->str_len;
            break;
        case CV_OBJECT:
            new->type=CV_OBJECT;
            
            if(v->obj_copy==NULL) {
                new->obj_ptr=v->obj_ptr;
            } else {
                new->obj_ptr=v->obj_copy(v->obj_ptr,v->obj_size);
            }
            
            new->obj_size=v->obj_size;
            new->obj_destruct=v->obj_destruct;
            new->obj_copy=v->obj_copy;
            new->obj_compare=v->obj_compare;
            break;
        case CV_FLOAT:
            new->type=CV_FLOAT;
            new->dbl=v->dbl;
            break;
    }
    return new;
}

struct cSarray
{
    cTval **vals;
    size_t size;
    size_t count;
};

cTarray *cFarray_new(void)
{
    cTarray *array;

    array=(cTarray *)malloc(sizeof(cTarray));
    assert(array!=NULL);
    
    array->count=array->size=0;

    return array;
}

void cFarray_push(cTarray *array, cTval *val)
{
    assert(array!=NULL);
    assert(val!=NULL);
    
    if(array->size==0) {
        array->vals=malloc(sizeof(void *));
        assert(array->vals!=NULL);
        
        array->size=1;
        array->vals[0]=val;
        array->count++;
    } else {
        array->size++;
        
        array->vals=realloc(array->vals,array->size*sizeof(void *));
        assert(array->vals!=NULL);
        
        array->vals[array->size]=val;
        array->count++;
    }
    val->refcount++;
}

cTval *cFarray_pop(cTarray *array)
{
    cTval *val;
    
    assert(array!=NULL);

    if(array->count==0) return NULL;

    int c=0;
    for(int i=0;i<array->count;i++) {
        if(array->vals[i]!=NULL) {
            c++;
        }
        
        if(c==array->count) {
            val=array->vals[i];
            array->vals[i]=NULL;
            array->count--;
            val->refcount--;
            return val;
        }
    }
    assert(1!=1);
}

void *cFarray_foreach(cTarray *array, void *(*callback)(cTval *v))
{
    void *userdata;
    
    for(int i=0;i<array->size;i++) {
        if(array->vals[i]!=NULL) {
            userdata=callback(array->vals[i]);
            if(userdata!=NULL) {
                return userdata;
            }
        }
    }
    return NULL;
}

void cFarray_free(cTarray *array) {
    for(int i=0;i<array->size;i++) {
        if(array->vals[i]!=NULL) {
            cFval_free(array->vals[i]);
        }
    }
    free(array->vals);
    free(array);
}

struct cSassoc {
    cTarray *names;
    cTarray *values;

    hshTlist *forward;
    hshTlist *reverse;

    size_t count;
};


