//
//  UserTabController.m
//  Flash
//
//  Created by Nicholas Matsakis on 9/1/07.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "UserTabController.h"
#import "OAMacros.h"
#import "OACategories.h"
#import "OACoreData.h"
#import "Accessors.h"
#import "OADebug.h"
#import "QuizController.h"
#import "Errors.h"

@implementation UserTabController

- init
{
    if ((self = [super init])) {
        a_purgeAge = 60;
        [self setStatisticsStale:NO]; // also initializes statisticsColor
    }
    return self;
}

- (void) awakeFromNib
{
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self 
               selector:@selector(objectsDidChange:) 
                   name:NSManagedObjectContextObjectsDidChangeNotification 
                 object:[m_dictionary managedObjectContext]];
    [m_usersListing addObserver:self forKeyPath:@"selection" options:0 context:nil];
}

- (void) dealloc
{
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center removeObserver:self];
    [m_usersListing removeObserver:self forKeyPath:@"selection"];
    [a_statistics release];
    [super dealloc];
}

- (void) objectsDidChange:(NSNotification*)notificiation
{
    // when the database changes, indicate that the statistics may be stale
    [self setStatisticsStale:YES];
}

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    // when the current user changes, we must recompute the statistics
    [self setStatistics:nil];
}

// Statistics I need to track:
// 1. Per language:
//    - total number of connections
// 2. Per user, per language:
//    - total number of learning objects
//    - total number of *expired* learning objects
- (NSArray*) statistics
{
    if (a_statistics == nil) {
        NSManagedObject *user = [m_usersListing selectedObject];
        NSManagedObjectContext *mctx = [m_dictionary managedObjectContext];
        NSMutableArray *statistics = [NSMutableArray array];
        if (user != nil) {
            NSArray *languages = [mctx allObjectsOfEntityType:E_LANGUAGE];
            FOREACH(NSManagedObject *, language, languages) {
                NSMutableDictionary *entry = [NSMutableDictionary dictionary];
                [entry setObject:language forKey:@"language"];
                
                // find the total number of connections for this language:
                NSArray *connections = 
                    [mctx objectsOfEntityType:E_CONNECTION
                      matchingPredicateFormat:@"from.language == %@", language];
                [entry setObject:[NSNumber numberWithInt:[connections count]] forKey:@"totalConnections"];
                
                // compute the percentage that are learned and expired
                NSArray *learnedConnections = 
                    [connections filteredArrayUsingPredicateFormat:@"ANY learned.user == %@", user];
                NSArray *expiredLearnings =
                    [mctx objectsOfEntityType:E_LEARNING
                      matchingPredicateFormat:
                        @"connection.from.language == %@ AND "
                        @"user == %@ AND "
                        @"askNextOn < %@", 
                        language, user, [NSDate date]];
                double connCnt = [connections count];
                double learnCnt = [learnedConnections count];
                double expiredCnt = [expiredLearnings count];
                [entry setObject:[NSNumber numberWithDouble:learnCnt] forKey:@"learned"];
                [entry setObject:[NSNumber numberWithDouble:learnCnt/connCnt*100.0] forKey:@"learnedP"];
                [entry setObject:[NSNumber numberWithDouble:expiredCnt] forKey:@"expired"];
                [entry setObject:[NSNumber numberWithDouble:expiredCnt/connCnt*100.0] forKey:@"expiredP"];
            
                OALog(@"Recomputing statistics: user=%@ language=%@ total=%d learned=%d expired=%d",
                      [user nameValue], [language nameValue], 
                      [connections count], [learnedConnections count], [expiredLearnings count]);
                [statistics addObject:entry];
            } ENDFOREACH;
        }
        a_statistics = [statistics retain];
    }
    
    return a_statistics;
}

- (void) setStatistics:(NSArray*)array
{
    // this routine is invoked to let us know that the statistics are out of
    // date, so we merely clear our cached results
    [a_statistics release];
    a_statistics = nil;
    [self setStatisticsStale:NO];
}

- (BOOL) statisticsStale
{
    return a_statisticsStale;
}

- (void) setStatisticsStale:(BOOL)value
{
    a_statisticsStale = value;
    [self setStatisticsColor:(value 
                              ? [NSColor grayColor]
                              : [NSColor blackColor])];
}

- (NSColor*) statisticsColor
{
    return a_statisticsColor;
}

- (void) setStatisticsColor:(NSColor*)color
{
    [a_statisticsColor release];
    a_statisticsColor = [color copy];
}

- (IBAction) refreshStatistics:(id)sender
{
    [self setStatistics:nil];
}

- (IBAction) startQuiz:sender
{
    NSManagedObject *user = [m_usersListing selectedObject];
    NSManagedObject *language = [[m_usersStatistics selectedObject] valueForKey:@"language"];
    int quizLength = [user quizLengthValue];
    OALog (@"Starting quiz for user %@ with language %@",
           [user nameValue], [language nameValue]);
    
    QuizController *qctr = [[[QuizController alloc] initWithDictionary:m_dictionary 
                                                                  user:user
                                                              language:language
                                                            quizLength:quizLength] autorelease];
    [m_dictionary addWindowController:qctr];
    [qctr showWindow:self];
}

#pragma mark -
#pragma mark Purge Old Mistakes

- (int) purgeAge
{
    return a_purgeAge;
}

- (void) setPurgeAge:(int)age
{
    OALog(@"setMaximumLearningAge:%d", age);
    a_purgeAge = age;
}

- (BOOL) validatePurgeAge:(NSNumber**)age error:(NSError**)error
{
    int a = [*age intValue];
    if (a <= 0) {
        if (error)
            *error = [NSError errorWithDomain:FLASHDOMAIN 
                                         code:INVALIDMLAGE
                                     userInfo:
                [NSDictionary dictionaryWithObjectsAndKeys:
                    NSLocalizedString(@"Maximum Learning Age Must Be > 0", 
                                      @"Maximum learning age must be at least 1 day!"),
                    NSLocalizedDescriptionKey,
                    nil]];
        return NO;
    }
    return YES;
}

- (IBAction) startPurgeSheet:(id)sender
{
    NSApplication *app = [NSApplication sharedApplication];
    [app beginSheet:m_purgePanel 
     modalForWindow:[m_dictionary mainWindow]
      modalDelegate:self
     didEndSelector:@selector(purgeSheetDidEnd:returnCode:contextInfo:) 
        contextInfo:nil];
}

- (IBAction) dontPurge:(id)sender
{
    NSApplication *app = [NSApplication sharedApplication];
    [app endSheet:m_purgePanel returnCode:0];
}

- (IBAction) purge:(id)sender
{
    NSApplication *app = [NSApplication sharedApplication];
    [app endSheet:m_purgePanel returnCode:1];
}

// This is called after versioning is complete.  It purges any connections
// whose expiration date is more than 2 months ago --- the idea is that if the
// user hasn't done any quizzes in a while, we can forget about those words
// and start fresh.
- (void)purgeSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
    [m_purgePanel orderOut:self];
    if (returnCode == 0) // canceled
        return;

    NSManagedObject *user = [m_usersListing selectedObject];
    NSManagedObject *lang = [[m_usersStatistics selectedObject] objectForKey:@"language"];
    
    OALog(@"Purging all learnings for %@ in language %@ older than %d days...", 
          [user nameValue], [lang nameValue], [self purgeAge]);
    
    NSTimeInterval day = 60 * 60 * 24;
    NSTimeInterval tooOldInterval = [self purgeAge] * day;
    NSDate *tooOldDate = [NSDate dateWithTimeIntervalSinceNow:-tooOldInterval];
    NSManagedObjectContext *mctx = [m_dictionary managedObjectContext];
    NSArray *tooOldLearnings = [mctx objectsOfEntityType:E_LEARNING 
                                 matchingPredicateFormat:
                                @"user == %@ AND connection.from.language == %@ AND askNextOn < %@", 
                                user, lang, tooOldDate];
    NSDate *mostRecent = [[NSDate distantPast] copy];
    int count = 0;
    FOREACH (NSManagedObject *, tooOldLearning, tooOldLearnings) {
        NSDate *askNextOn = [tooOldLearning askNextOnValue];
        if ([mostRecent isLessThan:askNextOn]) 
            mostRecent = [askNextOn copy];
        count++;
        [mctx deleteObject:tooOldLearning];
    } ENDFOREACH;
    OALog(@"Purged %d connections, of which the oldest was %@", count, mostRecent);
    [mostRecent release];
    [self setStatistics:nil]; // these will certainly be out of date!
}

- (IBAction) resetNextDate:(id)sender
{
	NSArray *selectedLearnings = [m_usersLearning selectedObjects];
	FOREACH (NSManagedObject *, learning, selectedLearnings) {
		[learning adjustAskNextOnFromStage];
	} ENDFOREACH;
}

@end
