//
//  Conversions.m
//  CamelBones
//
//  Copyright (c) 2004 Sherm Pendley. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "PerlImports.h"
#import "Wrappers.h"

#import "CBPerlObject.h"
#import "CBPerlObjectInternals.h"
#import "CBPerlArray.h"
#import "CBPerlArrayInternals.h"
#import "CBPerlHash.h"
#import "CBPerlHashInternals.h"
#import "CBPerl.h"

id CBDerefSVtoID(SV* sv) {
    // Define a Perl context
    dTHX;

    // Basic sanity check
    if (!sv) {
        NSLog(@"Warning: NULL passed to CBDerefSVtoID");
        return nil;
    }

    // Check to see if it's an object reference
    if (sv_isobject(sv)) {

        // If this is just an NSObject wrapper, pass the native object
        if (sv_derived_from(sv, "NSObject")) {
            HV *hv = (HV *)SvRV(sv);
            SV **nativeSvp = hv_fetch(hv, "NATIVE_OBJ", strlen("NATIVE_OBJ"), 0);
            SV *nativeSV = nativeSvp ? *nativeSvp : NULL;

            if (nativeSV) {
                return (id) SvIV(nativeSV);
            } else {
                return nil;
            }


        // Otherwise create a new CBPerlObject to "wrap" the Perl object
        } else {
            return [[CBPerlObject objectWithSV: sv] retain];
        }

    // Then check to see if it's some other kind of reference
    } else if (SvROK(sv)) {
        SV *target;

        // Get the value referred to, and dereference it
        target = SvRV(sv);
        return CBDerefSVtoID(target);

    // It's not a reference - check for floats
    } else if (SvNOK(sv)) {
        return [NSNumber numberWithDouble: SvNV(sv)];

    // and ints
    } else if (SvIOK(sv)) {
        return [NSNumber numberWithLong: SvIV(sv)];

    // and strings
    } else if (SvPOK(sv)) {
        return [NSString stringWithUTF8String: SvPV(sv, PL_na)];

    // and arrays
    } else if (SvTYPE((AV*)sv) == SVt_PVAV) {
        return [CBPerlArray arrayWithAV: (AV*)sv];

    // and finally hashes
    } else if (SvTYPE((HV*)sv) == SVt_PVHV) {
        return [CBPerlHash dictionaryWithHV: (HV*)sv];
    }

    // WTF?
    if ([[CBPerl sharedPerl] varAsInt: @"CamelBones::ShowUnhandledTypeWarnings"]) {
        NSLog(@"Warning: Unhandled SV type passed to CBDerefSVtoID");
    }
    return nil;
}

SV* CBDerefIDtoSV(id target) {
    // Define a Perl context
    dTHX;

    // Basic sanity checking first
    if (target == nil) {
        return &PL_sv_undef;
        
    // Check first for a wrapped Perl object or variable
    } else if ([target isMemberOfClass: [CBPerlObject class]]) {
        return [(CBPerlObject*)target getSV];
    }

    // Some types of objects may get special handling
    else if ([target isKindOfClass: [NSString class]]
    		&& [[CBPerl sharedPerl] varAsInt: @"CamelBones::ReturnStringsAsObjects"] == 0) {
    	const char *u = [(NSString *)target UTF8String];
    	int len = strlen(u);
        SV *newSV = newSVpv(u, len);
        SvUTF8_on(newSV);
        return newSV;
    }

    // If it's a descendant of NSObject, create and return a wrapper
    else if ([target isKindOfClass: [NSObject class]]) {
        return CBCreateWrapperObject(target);
    }

    // WTF?
	if ([[CBPerl sharedPerl] varAsInt: @"CamelBones::ShowUnhandledTypeWarnings"]) {    
		NSLog(@"Warning: CBDerefIDtoSV called with unknown object target type");
	}

    return &PL_sv_undef;
}

Class CBClassFromSV(SV* sv) {
	return(NSClassFromString(CBDerefSVtoID(sv)));
}

SV* CBSVFromClass(Class c) {
	return(CBDerefIDtoSV(NSStringFromClass(c)));
}

SEL CBSelectorFromSV(SV* sv) {
	return(NSSelectorFromString(CBDerefSVtoID(sv)));
}

SV* CBSVFromSelector(SEL aSel) {
	return(CBDerefIDtoSV(NSStringFromSelector(aSel)));
}

