The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"

#define FAIL_STATUS
#define ERROR_HANDLER perl_error_handler

#include "config.h"
#include "text-fuzzy.h"
#include "text-fuzzy-perl.c"

#undef FAIL_STATUS
#define FAIL_STATUS

typedef text_fuzzy_t * Text__Fuzzy;

MODULE=Text::Fuzzy PACKAGE=Text::Fuzzy

PROTOTYPES: ENABLE

BOOT:
	/* Set the error handler in "text-fuzzy.c" to be the error
	   handler defined in "text-fuzzy-perl.c". */

	text_fuzzy_error_handler = perl_error_handler;


Text::Fuzzy
new (class, search_term, ...)
	const char * class;
	SV * search_term;
PREINIT:
	int i;
	text_fuzzy_t * r;
CODE:
	r = 0;

	sv_to_text_fuzzy (search_term, & r);

        if (! r) {
        	croak ("error making %s.\n", class);
	}

	/* Loop over the parameters in "...". The first two terms are
	   "class" and "search_term", so we start from 2 here. */

	for (i = 2; i < items; i++) {
		SV * x;
		char * p;
		unsigned int len;

		if (i >= items - 1) {
			warn ("Odd number of parameters %d of %d",
			      i, (int) items);
			break;
		}

		/* Read in parameters in the "form max => 22",
		"no_exact => 1", etc. */

		x = ST (i);
		p = (char *) SvPV (x, len);
		if (strncmp (p, "max", strlen ("max")) == 0) {
			int max;
			max = SvIV (ST (i + 1));
			if (max < 0) {
				TEXT_FUZZY (set_max_distance (r, NO_MAX_DISTANCE));
			}
			else {
				TEXT_FUZZY (set_max_distance (r, max));
			}
		}
		else if (strncmp (p, "no_exact", strlen ("no_exact")) == 0) {
			r->no_exact = SvTRUE (ST (i + 1)) ? 1 : 0;
		}
		else if (strncmp (p, "trans", strlen ("trans")) == 0) {
			r->transpositions_ok = SvTRUE (ST (i + 1)) ? 1 : 0;
		}
		else {
			warn ("Unknown parameter %s", p);
		}
		/* Plan to throw one away; you will anyway. */
		i++;
	}
	RETVAL = r;
OUTPUT:
        RETVAL

SV *
get_max_distance (tf)
	Text::Fuzzy tf;
PREINIT:
	int maximum;
CODE:
	TEXT_FUZZY (get_max_distance (tf, & maximum));	
        if (maximum >= 0) {
		RETVAL = newSViv (maximum);
	}
	else {
		RETVAL = &PL_sv_undef;
	}
OUTPUT:
	RETVAL

void
set_max_distance (tf, max_distance = &PL_sv_undef)
	Text::Fuzzy tf;
	SV * max_distance;
PREINIT:
	int maximum;
CODE:
	/* Set the maximum distance to "none". */

        maximum = NO_MAX_DISTANCE;
        if (SvOK (max_distance)) {
		maximum = (int) SvIV (max_distance);
		if (maximum < 0) {
			maximum = NO_MAX_DISTANCE;
		}
	}
	TEXT_FUZZY (set_max_distance (tf, maximum));

void
transpositions_ok (tf, trans)
	Text::Fuzzy tf;
	SV * trans;
CODE:
	if (SvTRUE (trans)) {
		TEXT_FUZZY (set_transpositions (tf, 1));
	}
	else {
		TEXT_FUZZY (set_transpositions (tf, 0));
	}

int
get_trans (tf)
	Text::Fuzzy tf;
CODE:
	TEXT_FUZZY (get_transpositions (tf, & RETVAL));
OUTPUT:
	RETVAL

int
distance (tf, word)
	Text::Fuzzy tf;
        SV * word;
CODE:
	RETVAL = text_fuzzy_sv_distance (tf, word);
OUTPUT:
	RETVAL


void
nearest (tf, words)
	Text::Fuzzy tf;
        AV * words;
PREINIT:
	int i;
	int n;
	AV * wantarray;
PPCODE:

	wantarray = 0;

	if (GIMME_V == G_ARRAY) {

	   	/* The user wants an array containing all of the
	   	nearest values. */

		wantarray = newAV ();
		/* Free the array */
		sv_2mortal ((SV *) wantarray);
		n = text_fuzzy_av_distance (tf, words, wantarray);
	}
	else {
		/* Even in void context, we still do the search, in
		   case the user just wants to know the minimum
		   distance and ignores the actual values. */

		n = text_fuzzy_av_distance (tf, words, 0);
	}

	if (wantarray) {
		SV * e;
		EXTEND (SP, av_len (wantarray));
		for (i = 0; i <= av_len (wantarray); i++) {
			e = * av_fetch (wantarray, i, 0);
			SvREFCNT_inc_simple_void_NN (e);
			PUSHs (sv_2mortal (e));
		}
        }
        else {
		if (n >= 0) {
            		PUSHs (sv_2mortal (newSViv (n)));
		}
		else {
            		PUSHs (& PL_sv_undef);
		}
        }


int
last_distance (tf)
	Text::Fuzzy tf;
CODE:
	TEXT_FUZZY (last_distance (tf, & RETVAL));
OUTPUT:
	RETVAL


SV *
unicode_length (tf)
	Text::Fuzzy tf;
PREINIT:
	int unicode_length;
CODE:
	TEXT_FUZZY (get_unicode_length (tf, & unicode_length));
        if (unicode_length == TEXT_FUZZY_INVALID_UNICODE_LENGTH) {
		RETVAL = &PL_sv_undef;
	}
	else {
		RETVAL = newSViv (tf->text.ulength);
	}
OUTPUT:
	RETVAL


void
no_alphabet (tf, yes_no)
	Text::Fuzzy tf;
        SV * yes_no;
CODE:
	TEXT_FUZZY (no_alphabet (tf, SvTRUE (yes_no)));


int
ualphabet_rejections (tf)
	Text::Fuzzy tf;
CODE:
	TEXT_FUZZY (ualphabet_rejections (tf, & RETVAL));
OUTPUT:
        RETVAL


int
length_rejections (tf)
	Text::Fuzzy tf;
CODE:
	TEXT_FUZZY (get_length_rejections (tf, & RETVAL));
OUTPUT:
        RETVAL


char *
scan_file (tf, file_name)
	Text::Fuzzy tf;
        char * file_name;
CODE:
        TEXT_FUZZY (scan_file (tf, file_name, & RETVAL));
OUTPUT:
        RETVAL


void
no_exact (tf, yes_no)
	Text::Fuzzy tf;
	SV * yes_no;
CODE:
	TEXT_FUZZY (set_no_exact (tf, SvTRUE (yes_no)));

int
alphabet_rejections (tf)
	Text::Fuzzy tf;
CODE:
	TEXT_FUZZY (alphabet_rejections (tf, & RETVAL));
OUTPUT:
	RETVAL

void
DESTROY (tf)
	Text::Fuzzy tf;
CODE:
	text_fuzzy_free (tf);