//
//  OAListSerializerFormatter.m
//  drachma
//
//  Created by Niko Matsakis on 12/12/06.
//  Copyright 2006 __MyCompanyName__. All rights reserved.
//

#import "OAListSerializerFormatter.h"
#import "OACategories.h"
#import "OAMacros.h"

@implementation OAListSerializerFormatter

+ formatterWithAvailableChoices:(OAKeyPath*)p namePath:(NSString*)np
{
    OAListSerializerFormatter *form = [[[OAListSerializerFormatter alloc] init] autorelease];
    [form setAvailableChoices:p namePath:np];
    return form;
}

+ formatterWithAvailableChoices:(OAKeyPath*)p namePath:(NSString*)np containerClass:(id)containerClass
{
    OAListSerializerFormatter *form = [[[OAListSerializerFormatter alloc] init] autorelease];
    [form setAvailableChoices:p namePath:np];
    [form setContainerClass:containerClass];
    return form;
}

+ formatterWithInputSeparator:(NSString*)isep 
              outputSeparator:(NSString*)osep 
             availableChoices:(OAKeyPath*)p 
                     namePath:(NSString*)np
{
    OAListSerializerFormatter *form = [[[OAListSerializerFormatter alloc] init] autorelease];
    [form setAvailableChoices:p namePath:np];
    [form setInputSeparator:isep outputSeparator:osep];
    return form;
}

- init
{
    if ((self = [super init])) {
        m_inputSeparator = @",";
        m_outputSeparator = @", ";
        m_containerClass = [NSMutableArray class];
    }
    return self;
}

- (void) dealloc
{
    [m_choices release];
    [m_namePath release];
    [m_inputSeparator release];
    [m_outputSeparator release];
    [m_containerClass release];
    [super dealloc];
}

- (void) setInputSeparator:(NSString*)insep outputSeparator:(NSString*)outsep
{
    [m_inputSeparator release];
    [m_outputSeparator release];
    m_inputSeparator = [insep copy];
    m_outputSeparator = [outsep copy];
}

- (void) setAvailableChoices:(OAKeyPath*)choices namePath:(NSString*)namePath
{
    [m_choices release];
    [m_namePath release];
    m_choices = [choices retain];
    m_namePath = [namePath copy];
}

- (void) setContainerClass:(id)containerClass
{
    m_containerClass = [containerClass retain];
}

- (BOOL)getObjectValue:(id *)anObject forString:(NSString *)string errorDescription:(NSString **)error
{
    // break the array at every comma and strip any white space:
    NSArray *pieces = [[string componentsSeparatedByString:m_inputSeparator] mapArrayWith:@selector(strip)];

    // if we are just looking for a list of strings, we're done
    if (m_choices == nil) {
        *anObject = pieces;
        return YES;
    }

    // otherwise, get the list of things that each entry might be:
    NSArray *choices = (m_choices != nil ? [m_choices value] : nil);
    
    // make a dictionary mapping name of each choice to the choice itself:
    NSDictionary *choicesDict = [NSDictionary dictionaryWithObjects:choices 
                                                            forKeys:[choices mapArrayWith:@selector(valueForKeyPath:) 
                                                                               withObject:m_namePath]];
    
    // convert each entry in the array, if we can; for each error, add to the error string
    id result = [[[m_containerClass alloc] init] autorelease];
    NSMutableString *errorString = [NSMutableString string];
    FOREACH (NSString *, piece, pieces) {
        id res = [choicesDict objectForKey:piece];
        if (res != nil)
            [result addObject:res];
        else
            [errorString appendString:[NSString stringWithFormat:@"'%@' is not a valid entry.", piece]];
    } ENDFOREACH;
    
    // if any of the pieces are not valid, return an error:
    if ([errorString length] != 0) {
        if (error != nil)
            *error = errorString;
        return NO;
    }

    // otherwise, return the list of converted objects
    *anObject = result;
    return YES;    
}

- (NSString *)stringForObjectValue:(id)anObject
{
    if ([anObject respondsToSelector:@selector(objectEnumerator)]) {
        id stringArray = anObject;
        if (m_namePath != nil) {
            stringArray = mapToArrayWithObject(anObject, @selector(valueForKeyPath:), m_namePath);
        }
        return [NSString stringByJoining:stringArray withString:m_outputSeparator];
    }
    
    // workaround --- if a user enters an empty string, cocoa seems to call 
    // "stringForObjectValue:" instead of getObjectValue:forString:errorDescription:.
    // This is quite irritating.  Not sure if it's my fault...? 
    if ([anObject isKindOfClass:[NSString class]]) {
        return anObject;
    }
    
    @throw [NSException exceptionWithName:@"InvalidList" reason:@"Invalid list provided" userInfo:[NSDictionary dictionary]];
}


@end
