/*
 Flash: Flashcard program
 Copyright (C) 2005, Niko Matsakis (niko@alum.mit.edu)
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#import "QuizQuestion.h"
#import "DictionaryDocument.h"
#import "QuizController.h"
#import "Accessors.h"
#import "OACategories.h"
#import "QuizAnswer.h"
#import "OAMacros.h"

@implementation QuizQuestion

#pragma mark -
#pragma mark Init and dealloc

- initWithWord:(NSManagedObject*)word dictionary:(DictionaryDocument*)dict;
{
    if ((self = [super init])) {
        m_questionWord = [word retain];
        m_dictionary = [dict retain];
        m_answers = [[NSMutableArray alloc] initWithCapacity:32];
        
        // Register as an observer of the fromWord's prompt; 
        // when it changes, we need to indicate
        // that our own prompt changed.
        [m_questionWord addObserver:self
                         forKeyPath:@"text"
                            options:0
                            context:NULL];        
    }
    return self;
}

- (void) dealloc
{
    [m_dictionary release];
    [m_questionWord removeObserver:self forKeyPath:@"text"];
    [m_questionWord release];
    [m_answers release];
    [super dealloc];
}

// Routine invoked by external users.  Determines the answer word automatically.
- (void) addConnection:(NSManagedObject*)connection
            answerWord:(NSManagedObject*)answerWord
{
    // Find the default answer word:
    if (answerWord == nil) {
        if ([connection fromValue] == m_questionWord)
            answerWord = [connection toValue];
        else
            answerWord = [connection fromValue];
    }
    
    // Determine if the connection is indirect:
    //    Indirect means that it does not directly involved the question word for
    //    this QuizQuestion.  This happens when we add an equivalent relation
    //    that comes from some word that this language is related to.
    BOOL indirect = 
        [connection fromValue] != m_questionWord &&
        [connection toValue] != m_questionWord;
    
    // Create a corresponding QuizAnswer object
    QuizAnswer *qa = [[[QuizAnswer alloc] initWithConnection:connection 
                                                  answerWord:answerWord
                                                    indirect:indirect
                                                  dictionary:m_dictionary] autorelease];
    [m_answers addObject:qa];
    
    // Track whether one of the answers concerns an article.
    //   This affects the prompt we supply; if it is YES, we omit the article
    if ([m_dictionary relationshipForArticles] == [connection typeValue]) {
        NSAssert1(m_article == nil, @"Word %@ is connected to two articles", [m_questionWord textValue]);
        m_article = [answerWord textValue];
    }
}

#pragma mark -
#pragma mark Simple accessors

- (NSNumber*) answerCount
{
    return [NSNumber numberWithInt:[m_answers count]];
}

- (NSArray*) answers
{
    return m_answers;
}

// Returns true if our list of answers is empty.
- (BOOL) emptyAnswers
{
    return [m_answers count] == 0;
}

#pragma mark -
#pragma mark Prompt manipulation

- (NSString*) prompt
{
    if (!m_article)
        return [m_questionWord textValue];
    else {
        // Return the word ignoring the article and space after it.
        // So if word was "die Gabel", article is "die", we return
        // "Gabel" by skipping 3+1 letters.
        NSAssert2([[m_questionWord textValue] hasPrefix:m_article], @"Word %@ does not have article %@ as prefix",
                  [m_questionWord textValue], m_article);
        int artlen = [m_article length]+1;
        return [[m_questionWord textValue] substringFromIndex:artlen];
    }
}

- (void) setPrompt:(NSString*)value
{
    if (!m_article) {
        [m_questionWord setTextValue:value];
    }
    else {
        NSString *newvalue = [NSString stringWithFormat:@"%@ %@", m_article, value];
        [m_questionWord setTextValue:newvalue];
    }
}

#pragma mark -
#pragma mark Key value observing

// We observe key-values so that they we can indicate that our 
// own dependent values change.

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object 
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (object == m_questionWord) {
        [self willChangeValueForKey:@"prompt"];
        [self didChangeValueForKey:@"prompt"];
    }
}

#pragma mark -
#pragma mark Dead connection removal

- (NSNumber *) pruneAnswers
{
    // filter the answers.
    NSMutableArray *remainingAnswers = [m_answers filterArrayWith:@selector(shouldRetainAnswer)];
    
    if ([remainingAnswers count] != [m_answers count]) {
        [m_answers release];
        m_answers = [remainingAnswers retain];
    }
    
    return [NSNumber numberWithBool:[m_answers count] == 0];
}

#pragma mark -
#pragma mark Description, Hash, Equals

- (unsigned) hash
{
    return [m_questionWord hash];
}

- (BOOL) isEqual:object
{
    if (self == object)
        return YES;
    
    if (![object isKindOfClass:[self class]])
        return NO;
    
    QuizQuestion *qq = (QuizQuestion*)object;
    return m_questionWord == qq->m_questionWord;
}

- (NSString*) description
{
    return [NSString stringWithFormat:@"<QuizQuestion word=%@ #ans=%d>", 
        [m_questionWord textValue],
        [m_answers count]];
}

@end
