//
//  CBPerlArray.m
//  Camel Bones - a bare-bones Perl bridge for Objective-C
//  Originally written for ShuX
//
//  Copyright (c) 2002 Sherm Pendley. All rights reserved.
//

#import "CBPerlArray.h"
#import "CBPerlArrayInternals.h"
#import "PerlImports.h"
#import "Conversions.h"

@implementation CBPerlArray

// Required primitive methods
- (unsigned)count {
    // Define a Perl context
    dTHX;

    return av_len((AV*)_myArray)+1;
}
- (id)objectAtIndex:(unsigned)index {
    // Define a Perl context
    dTHX;

    SV **svp;
    svp = av_fetch((AV*)_myArray, index, 0);
    return svp ? CBDerefSVtoID(*svp) : nil;
}

- (void)addObject:(id)anObject {
    // Define a Perl context
    dTHX;

    av_push((AV*)_myArray, CBDerefIDtoSV(anObject));
	if ([anObject respondsToSelector:@selector(retain)]) {
		[anObject retain];
	}
}
- (void)insertObject:(id)anObject atIndex:(unsigned)index {
    // Define a Perl context
    dTHX;

    unsigned i;
    unsigned lastIndex;
    SV **svp;

    lastIndex = av_len((AV*)_myArray);
    av_extend((AV*)_myArray, lastIndex+2);
    for(i=lastIndex; i>=index; i--) {
        svp = av_fetch((AV*)_myArray, i, 0);
        if (svp) {
            av_store((AV*)_myArray, i+1, *svp);
        }
    }
    svp = av_store((AV*)_myArray, index, CBDerefIDtoSV(anObject));
    if (svp) {
        if ([anObject respondsToSelector:@selector(retain)]) {
            [anObject retain];
        }
    }
}
- (void)removeLastObject {
    // Define a Perl context
    dTHX;

    SV *obj;
    id anObject;
    
    obj = av_pop((AV*)_myArray);
    anObject = CBDerefSVtoID(obj);
    if ([anObject respondsToSelector:@selector(autorelease)]) {
        [anObject autorelease];
    }
}
- (void)removeObjectAtIndex:(unsigned)index {
    // Define a Perl context
    dTHX;

    SV **svp;
    id anObject;
    int i;
    int lastIndex;
    
    svp = av_fetch((AV*)_myArray, index, 0);
    if (svp) {
        anObject = CBDerefSVtoID(*svp);
        if ([anObject respondsToSelector:@selector(autorelease)]) {
            [anObject autorelease];
        }
    }

    lastIndex = av_len((AV*)_myArray);
    for(i=index; i<lastIndex; i++) {
        svp = av_fetch((AV*)_myArray, i+1, 0);
        if (svp) {
            av_store((AV*)_myArray, i, *svp);
        }
    }
}
- (void)replaceObjectAtIndex:(unsigned)index withObject:(id)anObject {
    // Define a Perl context
    dTHX;

    SV **svp;
    id oldObject;
    
    svp = av_fetch((AV*)_myArray, index, 0);
    if (svp) {
        oldObject = CBDerefSVtoID(*svp);
        if ([oldObject respondsToSelector:@selector(autorelease)]) {
            [oldObject autorelease];
        }
    }

    av_store((AV*)_myArray, index, CBDerefIDtoSV(anObject));
}

// Destructor
- (void) dealloc {
    // Define a Perl context
    dTHX;

	SV *obj;
	id anObject;
	
    if (NULL != _myArray) {
    
        if (SvREFCNT((SV *)_myArray) > 0) {
            SvREFCNT_dec((SV *)_myArray);
        }
    }
    [super dealloc];
}

@end


// Extended methods
@implementation CBPerlArray (CBPerlArrayCreation)

// Convenience creation methods returning autoreleased instances
+ (id) arrayNamed: (NSString *)varName isReference: (BOOL)isRef create: (BOOL)shouldCreate {
    return [[[CBPerlArray alloc] initArrayNamed:varName isReference:isRef create:shouldCreate] autorelease];
}
+ (id) arrayNamed: (NSString *)varName isReference: (BOOL)isRef {
    return [[[CBPerlArray alloc] initArrayNamed:varName isReference:isRef] autorelease];
}
+ (id) arrayNamed: (NSString *)varName {
    return [[[CBPerlArray alloc] initArrayNamed:varName] autorelease];
}
+ (id) newArrayNamed: (NSString *)varName {
    return [[[CBPerlArray alloc] initNewArrayNamed:varName] autorelease];
}
+ (id) arrayReferenceNamed: (NSString *)varName {
    return [[[CBPerlArray alloc] initArrayReferenceNamed:varName] autorelease];
}
+ (id) newArrayReferenceNamed: (NSString *)varName {
    return [[[CBPerlArray alloc] initNewArrayReferenceNamed:varName] autorelease];
}

// Designated initializer
- (id) initArrayNamed: (NSString *)varName isReference: (BOOL)isRef create: (BOOL)shouldCreate {
    // Define a Perl context
    dTHX;

    self = [super init];
    if (nil != self) {
        if (isRef) {
            [self release];
            return nil;
        } else {
            (AV *)_myArray = get_av([varName UTF8String], (YES == shouldCreate) ? 1 : 0);
            if (NULL == _myArray) {
                [self release];
                return nil;
            }
            SvREFCNT_inc((SV *)_myArray);
        }
    }
    return self;
}

// Convenience initializers - these all expand to calls to the designated initializer above
- (id) initArrayNamed: (NSString *)varName isReference: (BOOL)isRef {
    return [self initArrayNamed:varName isReference:isRef create:NO];
}
- (id) initArrayNamed: (NSString *)varName {
    return [self initArrayNamed:varName isReference:NO create:NO];
}
- (id) initNewArrayNamed: (NSString *)varName {
    return [self initArrayNamed:varName isReference:NO create:YES];
}
- (id) initArrayReferenceNamed: (NSString *)varName {
    return [self initArrayNamed:varName isReference:YES create:NO];
}
- (id) initNewArrayReferenceNamed: (NSString *)varName {
    return [self initArrayNamed:varName isReference:YES create:YES];
}

@end


// Private methods
@implementation CBPerlArray (CBPerlArrayPrivate)

+ (id) arrayWithAV: (AV*)theAV {
    return [[[CBPerlArray alloc] initWithAV:theAV] autorelease];
}
- (id) initWithAV: (AV*)theAV {
    self = [super init];
    if (nil != self) {
        (AV*)_myArray = theAV;
        SvREFCNT_inc((SV*)_myArray);
    }
    return self;
}

@end
