/* 
 * example of dictionary 
 * Teodor Sigaev <teodor@sigaev.ru>
 */
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include "postgres.h"

#include "dict.h"
#include "common.h"

/* #include "subinclude.h" */
#include "ts_locale.h"

typedef struct {
        int toupper; /* Whether to convert input string to uppercase before processint */
        int order; /* Whether to require strict order of digits (smaller follow greater) */
} DictRoman;

PG_FUNCTION_INFO_V1(dinit_roman);
Datum dinit_roman(PG_FUNCTION_ARGS);

Datum 
dinit_roman(PG_FUNCTION_ARGS) {
 	DictRoman *d = (DictRoman*)malloc(sizeof(DictRoman));
        
	Map *cfg, *pcfg;
	text *in;
 
 	if (!d)
                elog(ERROR, "No memory");

        /* Default values */
	d->toupper = false;
	d->order = true;

        if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL) {
                /* no options */
                PG_RETURN_POINTER(d);
        }
        
        /* Parse options */
        in = PG_GETARG_TEXT_P(0);
	parse_cfgdict(in, &cfg);
	PG_FREE_IF_COPY(in, 0);
	pcfg = cfg;

	while (pcfg->key) {
                if (strcasecmp("TOUPPER", pcfg->key) == 0) {
                        if (strcasecmp("true", pcfg->value) == 0) {
                                d->toupper = true;
                        } else if (strcasecmp("false", pcfg->value) == 0) {
                                d->toupper = false;
                        } else {
                                elog(ERROR,"Unknown value: %s => %s", pcfg->key, pcfg->value);
                        }
                } else if (strcasecmp("ORDER", pcfg->key) == 0) {
                        if (strcasecmp("true", pcfg->value) == 0) {
                                d->order = true;
                        } else if (strcasecmp("false", pcfg->value) == 0) {
                                d->order = false;
                        } else {
                                elog(ERROR,"Unknown value: %s => %s", pcfg->key, pcfg->value);
                        }
                } else {
                        elog(ERROR,"Unknown option: %s => %s", pcfg->key, pcfg->value);
                }
            
                pfree(pcfg->key);
                pfree(pcfg->value);
                pcfg ++;
	}
        
	pfree(cfg); 	

 	PG_RETURN_POINTER(d);
}

static unsigned char *tokens[] = {"M",  "CM", "D", "CD", "CCC", "CC", "C", "XC", "L", "XL", "XXX", "XX", "X", "IX", "V", "IV", "III", "II", "I"};
static int values[] = {1000, 900, 500, 400, 300, 200, 100, 90, 50, 40, 30, 20, 10, 9, 5, 4, 3, 2, 1}; 
static int ntokens = 19;

/* Zero as a result means the input is not valid Roman numeral */
/* Warning: this function (possibly) modifies the input string! */
static unsigned int roman_to_arabic(unsigned char *string, unsigned int toupper, unsigned int order)
{
        unsigned char *pos = string;
        unsigned int result = 0;
        unsigned int prev_value = 0;

        if(toupper){
                /* Convert input string to uppercase before processing */
                while(*pos){
                        if(*pos >= 'a' && *pos <= 'z')
                                *pos += 'A' - 'a';

                        pos ++;
                }

                pos = string;
        }
        
        while(*pos){
                int d = 0;
                int is_matched = false;
        
                for(d = 0; d < ntokens; d++)
                        if(strncmp(pos, tokens[d], strlen(tokens[d])) == 0){
                                unsigned int value = values[d];
                
                                if(prev_value && value > prev_value && order == true){
                                        /* printf("Wrong order of tokens!\n"); */
                                        return 0;
                                }
                
                                result += values[d];
                                pos += strlen(tokens[d]);
                                is_matched = true;
                                prev_value = value;
                
                                break;
                        }
        
                if(!is_matched){
                        /* printf("Can't match token: %s,value is %d\n", pos, result); */
                        return 0;
                }
        }

        return result;
}

PG_FUNCTION_INFO_V1(dlexize_roman);
Datum dlexize_roman(PG_FUNCTION_ARGS);
Datum
dlexize_roman(PG_FUNCTION_ARGS) {
 	DictRoman *d = (DictRoman*)PG_GETARG_POINTER(0);
	char *in = (char*)PG_GETARG_POINTER(1);
        char *in_string = pnstrdup(in, PG_GETARG_INT32(2));
        int value = roman_to_arabic(in_string, d->toupper, d->order);

        pfree(in_string);
        
        if(value){
                /* Don't know how to optimize it in Postgres memory framework */
                char *txt = palloc(1024);
                TSLexeme *res = palloc(sizeof(TSLexeme)*2);

                sprintf(txt, "%d", value);
                txt = repalloc(txt, strlen(txt) + 1);
                         
                res[1].lexeme = NULL;
                res[0].lexeme = txt;

                PG_RETURN_POINTER(res);
        } else
                PG_RETURN_POINTER(NULL);
}
