// [The "BSD licence"]
// Copyright (c) 2006-2007 Kay Roepke 2010 Alan Condit
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. The name of the author may not be used to endorse or promote products
//    derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#import "RewriteRuleElementStream.h"

@implementation RewriteRuleElementStream

@synthesize cursor;
@synthesize dirty;
@synthesize isSingleElement;
@synthesize singleElement;
@synthesize elements;
@synthesize elementDescription;
@synthesize treeAdaptor;

+ (RewriteRuleElementStream *) newRewriteRuleElementStream:(id<TreeAdaptor>)aTreeAdaptor
                                                         description:(NSString *)anElementDescription
{
    return [[RewriteRuleElementStream alloc] initWithTreeAdaptor:aTreeAdaptor
                                                          description:anElementDescription];
}

+ (RewriteRuleElementStream *) newRewriteRuleElementStream:(id<TreeAdaptor>)aTreeAdaptor
                                                         description:(NSString *)anElementDescription
                                                             element:(id)anElement
{
    return [[RewriteRuleElementStream alloc] initWithTreeAdaptor:aTreeAdaptor
                                                          description:anElementDescription
                                                              element:anElement];
}

+ (RewriteRuleElementStream *) newRewriteRuleElementStream:(id<TreeAdaptor>)aTreeAdaptor
                                                         description:(NSString *)anElementDescription
                                                            elements:(NSArray *)theElements;
{
    return [[RewriteRuleElementStream alloc] initWithTreeAdaptor:aTreeAdaptor
                                                          description:anElementDescription
                                                             elements:theElements];
}

- (id) initWithTreeAdaptor:(id<TreeAdaptor>)aTreeAdaptor description:(NSString *)anElementDescription
{
    if ((self = [super init]) != nil) {
        cursor = 0;
        dirty = NO;
        [self setDescription:anElementDescription];
        [self setTreeAdaptor:aTreeAdaptor];
        dirty = NO;
        isSingleElement = YES;
        singleElement = nil;
        elements = nil;
    }
    return self;
}

- (id) initWithTreeAdaptor:(id<TreeAdaptor>)aTreeAdaptor description:(NSString *)anElementDescription element:(id)anElement
{
    if ((self = [super init]) != nil) {
        cursor = 0;
        dirty = NO;
        [self setDescription:anElementDescription];
        [self setTreeAdaptor:aTreeAdaptor];
        dirty = NO;
        isSingleElement = YES;
        singleElement = nil;
        elements = nil;
        [self addElement:anElement];
    }
    return self;
}

- (id) initWithTreeAdaptor:(id<TreeAdaptor>)aTreeAdaptor description:(NSString *)anElementDescription elements:(NSArray *)theElements
{
    self = [super init];
    if (self) {
        cursor = 0;
        dirty = NO;
        [self setDescription:anElementDescription];
        [self setTreeAdaptor:aTreeAdaptor];
        dirty = NO;
        singleElement = nil;
        isSingleElement = NO;
        elements = [[AMutableArray arrayWithArray:theElements] retain];
    }
    return self;
}

- (void) dealloc
{
#ifdef DEBUG_DEALLOC
    NSLog( @"called dealloc in RewriteRuleElementStream" );
#endif
    if ( singleElement && isSingleElement ) [singleElement release];
    else if ( elements && !isSingleElement ) [elements release];
    [self setDescription:nil];
    [self setTreeAdaptor:nil];
    [super dealloc];
}

- (void)reset
{
    cursor = 0;
    dirty = YES;
}

- (id<TreeAdaptor>) getTreeAdaptor
{
    return treeAdaptor;
}

- (void) setTreeAdaptor:(id<TreeAdaptor>)aTreeAdaptor
{
    if (treeAdaptor != aTreeAdaptor) {
        if ( treeAdaptor ) [treeAdaptor release];
        treeAdaptor = aTreeAdaptor;
        [treeAdaptor retain];
    }
}

- (void) addElement: (id)anElement
{
    if (anElement == nil)
        return;
    if (elements != nil) {
        [elements addObject:anElement];
        return;
        }
    if (singleElement == nil) {
        singleElement = anElement;
        singleElement = [anElement retain];
        return;
    }
    isSingleElement = NO;
    elements = [[AMutableArray arrayWithCapacity:5] retain];
    [elements addObject:singleElement];
    singleElement = nil;  // balance previous retain in initializer/addElement
    [elements addObject:anElement];
}

- (void) setElement: (id)anElement
{
    if (anElement == nil)
        return;
    if (elements != nil) {
        [elements addObject:anElement];
        return;
        }
    if (singleElement == nil) {
        singleElement = anElement;
        singleElement = [anElement retain];
        return;
    }
    isSingleElement = NO;
    elements = [[AMutableArray arrayWithCapacity:5] retain];
    [elements addObject:singleElement];
    singleElement = nil;  // balance previous retain in initializer/addElement
    [elements addObject:anElement];
}

- (id<BaseTree>) nextTree
{
    NSInteger n = [self size];
    if ( dirty && (cursor >= 0 && n == 1)) {
        // if out of elements and size is 1, dup
        id element = [self _next];
        return [self copyElement:element];
    }
    // test size above then fetch
    id element = [self _next];
    return element;
}

- (id) _next       // internal: TODO: redesign if necessary. maybe delegate
{
    NSInteger n = [self size];
    if (n == 0) {
        @throw [NSException exceptionWithName:@"RewriteEmptyStreamException" reason:nil userInfo:nil];// TODO: fill in real exception
    }
    if ( cursor >= n ) {
        if ( n == 1 ) {
            return [self toTree:singleElement]; // will be dup'ed in -next
        }
        @throw [NSException exceptionWithName:@"RewriteCardinalityException" reason:nil userInfo:nil];// TODO: fill in real exception
    }
    if (singleElement != nil) {
        cursor++;
        return [self toTree:singleElement];
    }
    id el = [elements objectAtIndex:cursor];
    cursor++;
    return [self toTree:el];
}

- (BOOL) hasNext
{
    return (singleElement != nil && cursor < 1) ||
            (elements != nil && cursor < [elements count]);
}

- (NSInteger) size
{
    NSInteger n = 0;
    if (singleElement != nil)
        n = 1;
    if (elements != nil)
        return [elements count];
    return n;
}

- (id) copyElement:(id)element
{
    [self doesNotRecognizeSelector:_cmd];   // subclass responsibility
    return nil;
}

- (id<BaseTree>) toTree:(id)element
{
    return element;
}

- (NSString *) getDescription
{
    return elementDescription;
}

- (void) setDescription:(NSString *) description
{
    if ( description != nil && description != elementDescription ) {
        if (elementDescription != nil) [elementDescription release];
        elementDescription = [NSString stringWithString:description];
        [elementDescription retain];
    }
}

@end
