//
//  VersionImport.m
//  Flash
//
//  Created by Niko Matsakis on 05.08.07.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "VersionImport.h"
#import "OACoreData.h"
#import "OACategories.h"
#import "OADebug.h"
#import "OACategories.h"
#import "OAMacros.h"
#import "Accessors.h"
#import "OAReference.h"
#import "OAMigrationUtilities.h"

#define F 0
#define T 1
#define FLASHVERSIONING @"FlashVersioning"

/*
 Goal: to simplify the model.
 - Remove "inverse" relationships
 - Make all relationships one way
 - Add a "mother tongue" attribute: 
 we will remove all "equivalent" edges not to the mother tongue
 */

@implementation OldVersionImport

- (NSMutableDictionary*) importEntitiesOfType:(NSString*)entityType
{
    NSArray *fromEnts = [m_mctx[F] allObjectsOfEntityType:entityType];
    NSArray *toEnts = [m_mctx[T] copyAllObjects:fromEnts];
    return [NSDictionary dictionaryWithValues:toEnts pointerKeys:fromEnts];
}

- (void) transfer:(id)whenDoneObject selector:(SEL)whenDoneSelector
{
    // Locate the dictionary objects.
    for (int i = 0; i < 2; i++)
        m_dict[i] = [[m_mctx[i] objectOfEntityType:@"Dictionary"] retain];
    
    // Copy the masterId --- the new one must still be zero (i.e., no words added yet)
    if ([[m_dict[1] valueForKey:@"masterId"] intValue] != 0) {
        // ERROR: this hacky importing facility is not being used properly, just return for now
        return;
    }
    [m_dict[1] setValue:[m_dict[0] valueForKey:@"masterId"] forKey:@"masterId"];
    
    // Import the languages, copying all attributes.
    NSDictionary *langs = [self importEntitiesOfType:@"Language"];
    
    // Import the users, copying all attributes.
    //    NOTE: The "nativeTongue" of each user is now unset; 
    //    but the user can set it later.
    NSDictionary *users = [self importEntitiesOfType:@"User"];
    
    // Import the relationships (but not the automatic ones)
    NSArray *fromRels = 
        [[m_mctx[F] allObjectsOfEntityType:@"Relationship"]
        filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"immutable = NO"]];
    NSArray *toRels = [m_mctx[T] copyAllObjects:fromRels];
    NSMutableDictionary *rels = [NSDictionary dictionaryWithValues:toRels pointerKeys:fromRels];
    
    // Add in the immutable relationships which are automatically created
    NSArray *fromAutoRels = [m_mctx[F] objectsOfEntityType:@"Relationship" 
                                    matchingPredicateFormat:@"immutable = YES"];
    NSManagedObject *fromArticleRel = nil;
    FOREACH(NSManagedObject *, fromAutoRel, fromAutoRels) {
        NSString *name = [fromAutoRel nameValue];
        if ([name isEqual:@"article"]) fromArticleRel = fromAutoRel;
        NSManagedObject *toAutoRel = [m_mctx[T] objectOfEntityType:@"Relationship"
                                            matchingPredicateFormat:@"immutable = YES and name = %@", name];
        if (toAutoRel)
            [rels setObject:toAutoRel forPointer:fromAutoRel];
    } ENDFOREACH;
    
    // Import the articles.
    NSArray *fromArticles = [m_mctx[F] allObjectsOfEntityType:@"Article"];
    NSMutableDictionary *articles = [NSMutableDictionary dictionaryWithCapacity:[fromArticles count]];
    FOREACH(NSManagedObject *, fromArticle, fromArticles) {
        NSManagedObject *toLang = [langs objectForPointer:[fromArticle languageValue]];
        NSManagedObject *toArticle = [m_mctx[T] copyObject:fromArticle];
        [toArticle setLanguageValue:toLang];
        [articles setObject:toArticle forPointer:fromArticle];
    } ENDFOREACH;
    
    // Import the words, copying all attributes, 
    // and linking up the languages and articles.
    NSArray *fromWords = [m_mctx[F] allObjectsOfEntityType:@"Word"];
    NSMutableDictionary *words = [NSMutableDictionary dictionaryWithCapacity:[fromWords count]];
    FOREACH(NSManagedObject *, fromWord, fromWords) {
        NSManagedObject *toWord = [m_mctx[T] copyObject:fromWord];
        [words setObject:toWord forPointer:fromWord];
        NSManagedObject *toLang = [langs objectForPointer:[fromWord languageValue]];
        [toWord setLanguageValue:toLang];
        NSManagedObject *fromArticle = [fromWord articleValue];
        if (fromArticle) {
            NSManagedObject *toArticle = [articles objectForPointer:fromArticle];
            [toWord setArticleValue:toArticle];
        }
    } ENDFOREACH;
    
    // Import the connections.
    //    Generally, we ignore inverse connections: but for articles, we
    //    only *keep* inverse connections!
    NSArray *fromConns = [m_mctx[F] allObjectsOfEntityType:@"Connection"];
    NSMutableDictionary *conns = [NSMutableDictionary dictionaryWithCapacity:[fromConns count]];
    FOREACH(NSManagedObject *, fromConn, fromConns) {
        NSManagedObject *fromType = [fromConn typeValue];
        BOOL wantInverse = (fromType == fromArticleRel ? YES : NO);
        if ([[fromConn valueForKey:@"inverse"] intValue] != wantInverse)
            continue; // skip inverse connections, except for articles
        NSManagedObject *toType = [rels objectForPointer:fromType];
        NSAssert1(toType != nil, @"No relationship type corresponding to %@", [fromType nameValue]);
        NSManagedObject *toConn = [m_mctx[T] copyObject:fromConn];
        [toConn setTypeValue:toType];
        [toConn setFromValue:[words objectForPointer:[fromConn fromValue]]];
        [toConn setToValue:[words objectForPointer:[fromConn toValue]]];
        [conns setObject:toConn forPointer:fromConn];
    } ENDFOREACH;
    
    // Import the learning.
    NSArray *fromLearnings = [m_mctx[F] allObjectsOfEntityType:@"Learning"];
    FOREACH(NSManagedObject *, fromLearning, fromLearnings) {
        NSManagedObject *toConn = [conns objectForPointer:[fromLearning connectionValue]];
        if (toConn == nil) continue; // learning for an inverse connection? skip it
        NSManagedObject *toUser = [users objectForPointer:[fromLearning userValue]];
        NSManagedObject *toLearning = [m_mctx[T] copyObject:fromLearning];
        [toLearning setConnectionValue:toConn];
        [toLearning setUserValue:toUser];
    } ENDFOREACH;
    
    [whenDoneObject performSelector:whenDoneSelector withObject:nil];
}

- (void) _transferFromFile:(NSString*)fileName
            intoDictionary:(DictionaryDocument*)dictionary 
            whenDoneObject:(id)whenDoneObject
          whenDoneSelector:(SEL)whenDoneSelector
{
    NSError *error = nil;
    
    // Determine what version of core data was used at that time:
    NSString *version = [OAMigrationUtilities getMetadataForKey:@"ModelVersion" 
                                                inStoreWithPath:fileName 
                                                      inContext:nil];
    if (version == nil) 
        version = @"Dictionary-1";
    
    // Load the model and context from the file:
    m_model[0] = [OAMigrationUtilities namedObjectModel:version];
    if (m_model[0] == nil)
        error = [NSError errorWithDomain:FLASHVERSIONING code:0 userInfo:nil]; // XXX
    else {
        m_mctx[0] = [OAMigrationUtilities createRetainedCoreDataStackWithStorePath:fileName
                                                                         storeType:NSSQLiteStoreType
                                                                             model:m_model[0]];
        if (m_mctx[0] == nil)
            error = [NSError errorWithDomain:FLASHVERSIONING code:1 userInfo:nil];
    }
    
    // Extract the destination:
    m_model[1] = [dictionary managedObjectModel];
    m_mctx[1] = [dictionary managedObjectContext];
    
    if (error != nil)
        [whenDoneObject performSelector:whenDoneSelector withObject:error];
    else
        [self transfer:whenDoneObject selector:whenDoneSelector];
}

+ (void) transferFromFile:(NSString*)fileName
           intoDictionary:(DictionaryDocument*)dictionary 
           whenDoneObject:(id)whenDoneObject
         whenDoneSelector:(SEL)whenDoneSelector
{
    OldVersionImport *im = [[[OldVersionImport alloc] init] autorelease];
    [im _transferFromFile:fileName 
           intoDictionary:dictionary
           whenDoneObject:whenDoneObject
         whenDoneSelector:whenDoneSelector];
}


- (void) dealloc
{
    for (int i = 0; i < 2; i++) {
        [m_mctx[i] release];
        [m_model[i] release];
        [m_dict[i] release];
    }
    [super dealloc];
}

@end