#include <stdio.h>
#include <uthash.h>
#include <ctype.h>
#include <string.h>
#include "chad.h"
#include "regex.h"

typedef void (*attribute_callback_t)(const char * const string,
                                     const int          length,
                                           void * const attributes);

typedef struct {
    char * key;
	attribute_callback_t callback;
    UT_hash_handle hh;
} display_t;
display_t * display_table = NULL;

typedef struct {
	void * attributes;
	struct hl_group_t * link;
} hl_group_t;

typedef enum {
	KEYSYMBOL,
	KEYWORD,
	MATCH,
	REGION
} token_type_t;

typedef struct {
	hl_group_t * hl;
	token_type_t t;
	char* syntax;
} token_t;

/* Temp solution
 *  this should be dynamic
 */
token_t * token_table[1000];
int token_table_top = 0;

int append_token(token_t * token){
	token_table[token_table_top++] = token;
	return 0;
}

token_t * new_symbol_token(const char         * const word,
                                 hl_group_t   * const    g) {

	char * new_word = strdup(word);

	token_t * mt = (token_t*)malloc(sizeof(token_t));
	mt->hl = g;
	mt->t = KEYSYMBOL;
	mt->syntax = new_word;
	append_token(mt);
	return mt;

}

int new_symbol_tokens(const char       * const *     symbols,
                            hl_group_t * const             g) {

	int i = 0;
	while (*symbols) {
		if(new_symbol_token(*symbols, g)){
			++i;
		}
		++symbols;
	}

	return i;
}

int new_char_tokens(const char       *         characters,
                          hl_group_t * const            g) {
	int i = 0;
	char buffer[2];
	buffer[1] = '\00';
	for(const char * s = characters; *s != '\00'; s++){
		buffer[0] = *s;
		if(new_symbol_token(buffer, g)){
			++i;
		}
	}
	return i;
}

token_t * new_keyword_token(const char         * const word,
                                  hl_group_t   * const    g) {

	size_t word_length = strlen(word);
	char * new_word = (char*)malloc(word_length + 4 + 1);
	memcpy(new_word, "\\<", 2);
	memcpy(new_word + 2, word, word_length);
	strcpy(new_word + 2 + word_length, "\\>");

	token_t * mt = (token_t*)malloc(sizeof(token_t));
	mt->hl = g;
	mt->t = KEYWORD;
	mt->syntax = new_word;
	append_token(mt);
	return mt;
}

token_t * new_token(const char         * const word,
                    const token_type_t            t,
                          hl_group_t   * const    g) {
	switch(t){
		case KEYSYMBOL: {
			return new_symbol_token(word, g);
		};
		case KEYWORD: {
			return new_keyword_token(word, g);
		};
		case MATCH: {
		} break;
		case REGION: {
		} break;
	}
	// XXX: implement the rest
}

int new_keyword_tokens(const char       * const *       words,
                             hl_group_t * const             g) {
	int i = 0;
	while (*words) {
		if(new_keyword_token(*words, g)){
			++i;
		}
		++words;
	}

	return i;
}

int token_fits(const token_t* const token,
               const char*    const    to) {

	const char * const pattern = token->syntax;

	if (pattern == NULL) {
		return true;
	}

	return regex_match(pattern, to);
}

void render_string(const char * const string,
                   const char * const mode) {
	for (const char * s = string; *s != '\00';) {
		int f;
		int i = 0;
		for (; i < token_table_top; i++) {
			f = token_fits(token_table[i], s);
			if(f){ break; }
		}
		//
		display_t * display;
		HASH_FIND_STR(display_table,
		              mode,
		              display);
		//
		if (f) {
			display->callback(s,
			                  f,
			                  token_table[i]->hl->attributes);
			s += f;
		} else {
			display->callback(s,
			                  0,
			                  NULL);
			++s;
		}
	}
}

void new_display_mode(display_t * mode) {
	HASH_ADD_STR(display_table,
	             key,
	             mode);
}