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"

/* Copyright (C) 1997, Kenneth Albanowski.
   This code may be distributed under the same terms as Perl itself. */

#include "PerlGtkInt.h"

#include "ppport.h"

#include "MiscTypes.h"

#include "Derived.h"

void CroakOpts(char * name, char * value, struct opts * o)
{
	dTHR;

	SV * result = sv_newmortal();
	int i;
	
	sv_catpv(result, "invalid ");
	sv_catpv(result, name);
	sv_catpv(result, " ");
	sv_catpv(result, value);
	sv_catpv(result, ", expecting");
	for(i=0;o[i].name;i++) {
		if (i==0)
			sv_catpv(result," '");
		else if (o[i+1].name)
			sv_catpv(result,"', '");
		else
			sv_catpv(result,"', or '");
		sv_catpv(result, o[i].name);
	}
	sv_catpv(result,"'");
	croak(SvPV(result, PL_na));
}

void CroakOptsHash(char * name, char * value, HV * o)
{
	dTHR;

	SV * result = sv_newmortal();
	HE * he;
	int i=0;
	
	sv_catpv(result, "invalid ");
	sv_catpv(result, name);
	sv_catpv(result, " ");
	sv_catpv(result, value);
	sv_catpv(result, ", expecting");
	hv_iterinit(o);
	he = hv_iternext(o);
	while(he) {
		I32 len;
		char * key = hv_iterkey(he, &len);
		he = hv_iternext(o);
		if (i==0)
			sv_catpv(result," '");
		else if (he)
			sv_catpv(result,"', '");
		else
			sv_catpv(result,"', or '");
		i=1;
		sv_catpvn(result, key, len);
	}
	sv_catpv(result,"'");
	croak(SvPV(result, PL_na));
}

long SvOpt(SV * name, char * optname, struct opts * o) 
{
	dTHR;

	int i;
	char * n = SvPV(name, PL_na);
	for(i=0;o[i].name;i++) 
		if (strEQ(o[i].name, n))
			return o[i].value;
	CroakOpts(optname, n, o);
}

SV * newSVOpt(long value, char * optname, struct opts * o) 
{
	int i;
	for(i=0;o[i].name;i++)
		if (o[i].value == value)
			return newSVpv(o[i].name, 0);
	croak("invalid %s value %d", optname, value);
}


SV * newSVMiscRef(void * object, char * classname, int * newref)
{
	HV * previous = RetrieveMisc(object);
	SV * result;
	if (!object)
		return newSVsv(&PL_sv_undef);
	if (previous) {
		/*printf("Retriveing object %d as HV %d\n", object, previous);*/
		result = newRV((SV*)previous);
		if (newref)
			*newref = 0;
	} else {
		HV * h = newHV();
		hv_store(h, "_gtk", 4, newSViv((long)object), 0);
		result = newRV((SV*)h);
		RegisterMisc(h, object);
		/*printf("Storing object %d as HV %d\n", object, h);*/
		sv_bless(result, gv_stashpv(classname, FALSE));
		SvREFCNT_dec(h);
		if (newref)
			*newref = 1;
	}
	return result;
}

long SvOptsHash(SV * name, char * optname, HV * o) 
{
	int i;
	STRLEN len;
	char * n = SvPV(name, len);
	SV ** s;
	if (*n == '-') {
		n++;
		len--;
	}
	s = hv_fetch(o, n, len, 0);
	if (s)
		return SvIV(*s);
	CroakOptsHash(optname, n, o);
}

SV * newSVOptsHash(long value, char * optname, HV * o) 
{
	int i;
	HE * h;
	SV * result = 0;
	hv_iterinit(o);
	while(h = hv_iternext(o)) {
		SV * s = hv_iterval(o, h);
		if (SvIV(s) == value) {
			I32 len;
			char * p = hv_iterkey(h, &len);
			result = newSVpv(p, len);
		}
	}
	if (result)
		return result;
	croak("invalid %s value %d", optname, value);
}

long SvFlagsHash(SV * name, char * optname, HV * o) 
{
	int i;
	int val=0;
	if (!name || !SvOK(name))
		return 0;
	if (SvRV(name) && (SvTYPE(SvRV(name)) == SVt_PVAV)) {
		AV * r = (AV*)SvRV(name);
		for(i=0;i<=av_len(r);i++)
			val |= SvOptsHash(*av_fetch(r, i, 0), optname, o);
	} else if (SvRV(name) && (SvTYPE(SvRV(name)) == SVt_PVHV)) {
		HV * r = (HV*)SvRV(name);
		HE * h;
		hv_iterinit(r);
		while(h = hv_iternext(r)) {
			I32 len;
			char * key = hv_iterkey(h, &len);
			SV ** f;
			if (*key == '-') {
				key++;
				len--;
			}
			f = hv_fetch(o, key, len, 0);
			if (f)
				val |= SvIV(hv_iterval(o, h));
			else
				CroakOptsHash(optname, key, o);
		}
	} else
		val |= SvOptsHash(name, optname, o);
	return val;
}

SV * newSVFlagsHash(long value, char * optname, HV * o) 
{
	SV * target, *result;
	int i;
	HE * he;
	SV * s;
	I32 len;
	char * key;
	
	if (!pgtk_use_array) 
		target = (SV*)newHV();
	else
		target = (SV*)newAV();
		
	hv_iterinit(o);
	while(s = hv_iternextsv(o, &key, &len)) {
		int val = SvIV(s);
			
		if ((value & val) == val) {
			if (!pgtk_use_array)
				hv_store((HV*)target, key, len, newSViv(1), 0);
			else
				av_push((AV*)target, newSVpv(key, len));
			value &= ~val;
		}
	}
	
	result = newRV(target);
	SvREFCNT_dec(target);
	return result;
}


long SvOptFlags(SV * name, char * optname, struct opts * o) 
{
	int i;
	int val=0;
	if (!name || !SvOK(name))
		return 0;
	if (SvRV(name) && (SvTYPE(SvRV(name)) == SVt_PVAV)) {
		AV * r = (AV*)SvRV(name);
		for(i=0;i<=av_len(r);i++)
			val |= SvOpt(*av_fetch(r, i, 0), optname, o);
	} else if (SvRV(name) && (SvTYPE(SvRV(name)) == SVt_PVHV)) {
		HV * r = (HV*)SvRV(name);
		/* This is bad, as we don't catch members with invalid names */
		for(i=0;o[i].name;i++) {
			SV ** s = hv_fetch(r, o[i].name, strlen(o[i].name), 0);
			if (s && SvOK(*s) && SvTRUE(*s))
				val |= o[i].value;
		}
	} else
		val |= SvOpt(name, optname, o);
	return val;
}

SV * newSVOptFlags(long value, char * optname, struct opts * o) 
{
	SV * result;
	if (!pgtk_use_array) {
		HV * h = newHV();
		int i;
		result = newRV((SV*)h);
		SvREFCNT_dec(h);
		for(i=0;o[i].name;i++)
			if ((value & o[i].value) == o[i].value) {
				hv_store(h, o[i].name, strlen(o[i].name), newSViv(1), 0);
				value &= ~o[i].value;
			}
	} else {
		AV * a = newAV();
		int i;
		result = newRV((SV*)a);
		SvREFCNT_dec(a);
		for(i=0;o[i].name;i++)
			if ((value & o[i].value) == o[i].value) {
				av_push(a, newSVpv(o[i].name, 0));
				value &= ~o[i].value;
			}
	}
	return result;
}

SV * newSVDefEnumHash (GtkType type, long value) {
	GtkEnumValue * vals;
	SV * result;

	vals = gtk_type_enum_get_values(type);
	if (!vals)
		croak("Invalid type for enum: %s", gtk_type_name(type));
	while (vals && vals->value_nick) {
		if (vals->value == value) {
			result = newSVpv(vals->value_nick, 0);
			if (!pgtk_use_minus) {
				char *s = SvPV(result, PL_na);
				while (*s) {
					if (*s == '-') *s = '_';
					*s++;
				}
			}
			return result;
		}
		vals++;
	}
	croak("Invalid value %d for %s", value, gtk_type_name(type));
}

SV * newSVDefFlagsHash (GtkType type, long value) {
	GtkFlagValue * vals;
	SV * result;
	char *s, *p;
	
	vals = gtk_type_flags_get_values(type);
	if (!vals)
		croak("Invalid type for flags: %s", gtk_type_name(type));
	if (!pgtk_use_array) {
		HV * h = newHV();
		result = newRV((SV*)h);
		SvREFCNT_dec(h);
		while(vals && vals->value_nick) {
			if ((vals->value & value) == value) {
				if (pgtk_use_minus)
					hv_store(h, vals->value_nick, strlen(vals->value_nick), newSViv(1), 0);
				else {
					p = s = g_strdup(vals->value_nick);
					while (*s) {
						if (*s == '-') *s = '_';
						*s++;
					}
					hv_store(h, p, strlen(p), newSViv(1), 0);
					g_free(p);
				}
				value &= ~vals->value;
			}
			vals++;
		}
	} else {
		AV * a = newAV();
		result = newRV((SV*)a);
		SvREFCNT_dec(a);
		while(vals && vals->value_nick) {
			if ((vals->value & value) == value) {
				if (pgtk_use_minus)
					av_push(a, newSVpv(vals->value_nick, 0));
				else {
					p = s = g_strdup(vals->value_nick);
					while (*s) {
						if (*s == '-') *s = '_';
						*s++;
					}
					av_push(a, newSVpv(p, 0));
					g_free(p);
				}
				value &= ~vals->value;
			}
			vals++;
		}
	}
	/* check for unhandled bits in value ... */
	return result;
}

static int hystrEQ(register char* a, register char *b) {
	while (*a && *b) {
		if (*a == *b || ((*a == '-' || *a == '_') && (*b == '-' || *b == '_'))) {
			a++;
			b++;
		} else
			return 0;
	}
	return *a == *b;
}

long SvEFValueLookup (GtkEnumValue * vals, char* name, GtkType type) {
	GtkEnumValue *v;
	dTHR;

	if (!name)
		croak("Need a value in lookup");
	if (*name == '-')
		name++;
	v = vals;
	while (v && v->value_nick) {
		if (hystrEQ(name, v->value_nick))
			return v->value;
		v++;
	}
	{
		SV * r;
		char * endc;
		long val;
		
		/* last chanche: integer value... */
		val = strtol(name, &endc, 0);
		if (*name && *endc == '\0')
			return val;
		v = vals;
		r = sv_newmortal();
		sv_catpv(r, "invalid ");
		sv_catpv(r, gtk_type_name(type));
		sv_catpv(r, " value ");
		sv_catpv(r, name);
		sv_catpv(r, ", expecting: ");
		while (v && v->value_nick) {
			sv_catpv(r, v->value_nick);
			if (++v)
				sv_catpv(r, ", ");
		}
		croak(SvPV(r, PL_na));
		return 0;
	}
}

long SvDefEnumHash (GtkType type, SV *name) {
	long val = 0;
	GtkEnumValue * vals;
	vals = gtk_type_enum_get_values(type);
	if (!vals)
		croak("Invalid type for enum: %s", gtk_type_name(type));
	return SvEFValueLookup(vals, SvPV(name, PL_na), type);
}

long SvDefFlagsHash (GtkType type, SV *name) {
	long val = 0;
	GtkFlagValue * vals;
	int i;
	vals = gtk_type_flags_get_values(type);
	if (!vals)
		croak("Invalid type for flags: %s", gtk_type_name(type));
	if (SvRV(name) && (SvTYPE(SvRV(name)) == SVt_PVAV)) {
		AV * r = (AV*)SvRV(name);
		for(i=0;i<=av_len(r);i++)
			val |= SvEFValueLookup(vals, SvPV(*av_fetch(r, i, 0), PL_na), type);
	} else if (SvRV(name) && (SvTYPE(SvRV(name)) == SVt_PVHV)) {
		HV * r = (HV*)SvRV(name);
		HE * he;
		I32 len;

		hv_iterinit(r);
		while ((he=hv_iternext(r))) {
			val |= SvEFValueLookup(vals, hv_iterkey(he, &len), type);
		}
	} else
		val |= SvEFValueLookup(vals, SvPV(name, PL_na), type);
	return val;
}

void * SvMiscRef(SV * o, char * classname)
{
	HV * q;
	SV ** s;
	if (!o || !SvOK(o) || !(q=(HV*)SvRV(o)) || (SvTYPE(q) != SVt_PVHV))
		return 0;
	if (classname && !PerlGtk_sv_derived_from(o, classname))
		croak("variable is not of type %s", classname);
	s = hv_fetch(q, "_gtk", 4, 0);
	if (!s || !SvIV(*s))
		croak("variable is damaged %s", classname);
	return (void*)SvIV(*s);
}

static HV * MiscCache = 0;

void UnregisterMisc(HV * hv_object, void * gtk_object)
{
	int i;
	char buffer[40];
	sprintf(buffer, "%lu", (unsigned long)gtk_object);
	if (!MiscCache)
		MiscCache = newHV();
	
	/*printf("Removing object %d, HV %d\n", gtk_object, hv_object);*/
	
	hv_delete(hv_object, "_gtk", 4, G_DISCARD);
	hv_delete(MiscCache, buffer, strlen(buffer), G_DISCARD);
}

void RegisterMisc(HV * hv_object, void * gtk_object)
{
	char buffer[40];
	sprintf(buffer, "%lu", (unsigned long)gtk_object);
	if (!MiscCache)
		MiscCache = newHV();
	hv_store(MiscCache, buffer, strlen(buffer), newSViv((long)hv_object), 0);
}

HV * RetrieveMisc(void * gtk_object)
{
	SV ** s;
	char buffer[40];
	if (!MiscCache)
		MiscCache = newHV();
	sprintf(buffer, "%lu", (unsigned long)gtk_object);
	s = hv_fetch(MiscCache, buffer, strlen(buffer), 0);
	if (s)
		return (HV*)SvIV(*s);
	else
		return 0;
}

void * alloc_temp(int size)
{
    dTHR;

    SV * s = sv_2mortal(newSVpv("",0));
    SvGROW(s, size);
    return SvPV(s, PL_na);
}