#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include "avl.h"
#define ISSPACE(c) (((c) == ' ') || ((c) == '\t') || (((unsigned char)((c) - 11)) < 3)) 

char *skip_spaces(char *str)
{
    while (ISSPACE(*str))
        str++;
    return str;
}

char *slurp_file(const char *in_fname, size_t *sz)
{
    struct stat st;
    int fd = -1;
    char *configf = NULL;
    stat(in_fname, &st);
    if ((fd = open(in_fname, O_RDONLY)) < 0) {
        fprintf(stderr, "ERROR: Reading config file '%s': %d (%s)\n", in_fname,
                errno, strerror(errno));
        return NULL;
    }
    configf = (char*)malloc(st.st_size+1);
    read(fd, configf, st.st_size);
    close(fd);
    *sz = st.st_size;
    return configf;
}

int ParseConfig(struct AVLTree *cf, unsigned char *buf, size_t bufsize) {
    char *configf = buf, *configf_end = buf+bufsize;

    while (configf < configf_end) {
        char *parsestr, *value = NULL;
        unsigned int eq, len;
        configf = skip_spaces(configf);
        len = strchr(configf, '\n')-configf;
        parsestr = configf;
        configf += (len+1);

        if (!len || parsestr[0] == '#' || parsestr[0] == '\n') {
            continue;
        }

        if ((eq = strcspn(parsestr, "=")) > len) {
            value = "";
        } else {
            parsestr[eq++] = '\0';
            value = skip_spaces(parsestr+eq);
            value[len-eq] = '\0';
            fprintf(stderr, "directive = %s\n", parsestr);
            fprintf(stderr, "value = %s\n", value);
            if (value[0] == 0) {
                //fprintf(stderr, "ERROR: Illegal config option: %s\n", parsestr); 
                continue;
            }
            avl_insert(cf, parsestr, value);
        }
    }

    return 1;
}

// prints tree in-order. Rotations preserve in-order traversal.
void dump_inorder(struct node * tree, int out_fd) {
    if (tree == NULL) {
        return;
    }
    if (tree->c[0] != NULL) {
        dump_inorder(tree->c[0], out_fd);
    }
    write(out_fd, tree->k, strlen(tree->k));
    write(out_fd, "=", 1);
    write(out_fd, tree->d, strlen(tree->d));
    write(out_fd, "\n", 1);
    if (tree->c[1] != NULL) {
        dump_inorder(tree->c[1], out_fd);
    }
}

int SaveConfig(struct AVLTree *cf, const char *in_fname) {
    int fd = -1;
	if ((fd = open(in_fname, O_WRONLY|O_CREAT|O_APPEND, 0644)) < 0) {
		return -1;
	}
    dump_inorder(cf->root, fd); 
    close(fd);
	return 1;
}

char *FetchOpt(struct AVLTree *cf, char *in_key) {
    char *val = (char*)avl_lookup(cf, in_key);
    if(!val)
        return "";
    return val;
} 

void SetOpt(struct AVLTree *cf, char *in_key, char *in_val) {
	//char *oldval = (char*)avl_lookup(cf, in_key);
    //free(oldval);
    avl_insert(cf, in_key, in_val);
}

int main(int argc, char **argv) {
    size_t bufsize;
    char *buf = slurp_file(argv[1], &bufsize);
    struct AVLTree asdf;
    avl_create(&asdf);
    ParseConfig(&asdf, buf, bufsize);
    fprintf(stderr, "gps = %s\n", FetchOpt(&asdf, "gps"));
    fprintf(stderr, "gpstype = %s\n", FetchOpt(&asdf, "gpstype"));
    fprintf(stderr, "gpsdevice = %s\n", FetchOpt(&asdf, "gpsdevice"));
    SetOpt(&asdf, "gpsdevice", "/dev/rfcomm1");
    dump_inorder(asdf.root, 1);
    avl_destroy(&asdf);
    return 0;
}

