/*
        Copyright (c) 1996, NeXT Software, Inc.
        All rights reserved.

        You may freely copy, distribute and reuse the code in this example.
        NeXT disclaims any warranty of any kind, expressed or implied,
        as to its fitness for any particular use.
*/
#import "ConsistencyExtension.h"
#import <EOModeler/EOModeler.h>

@implementation ConsistencyExtension

static ConsistencyExtension *myConsistencyChecker = nil;

+ (void)load {
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    myConsistencyChecker = [[ConsistencyExtension alloc] init];

    [nc addObserver:myConsistencyChecker selector:@selector(beginConsistencyCheckNotification:) name:EOMCheckConsistencyBeginNotification object:nil];
    [nc addObserver:myConsistencyChecker selector:@selector(endConsistencyCheckNotification:) name:EOMCheckConsistencyEndNotification object:nil];

    [nc addObserver:myConsistencyChecker selector:@selector(checkEntityNotification:) name:EOMCheckConsistencyForEntityNotification object:nil];
    [nc addObserver:myConsistencyChecker selector:@selector(checkStoredProcedureNotification:) name:EOMCheckConsistencyForStoredProcedureNotification object:nil];
    [nc addObserver:myConsistencyChecker selector:@selector(checkModelNotification:) name:EOMCheckConsistencyForModelNotification object:nil];
}

- (void)beginConsistencyCheckNotification:(NSNotification *)notification
{
    entitiesAreConsistent = storedProceduresAreConsistent = modelIsConsistent = YES;
}

- (void)endConsistencyCheckNotification:(NSNotification *)notification
{
    EOModelerDocument *doc = [notification object];

    // For each test that ran successfully, report what was found to be valid.
    if (entitiesAreConsistent) {
        if ([[[doc model] adaptorName] hasPrefix:@"Oracle"])
            [doc appendConsistencyCheckSuccessText:[[NSAttributedString alloc] initWithString:@"All the entities have no more than 1 LONG or LONGRAW column.\n"]];
    }

    if (storedProceduresAreConsistent)
        [doc appendConsistencyCheckSuccessText:[[NSAttributedString alloc] initWithString:@"All the stored procedures that are used for DELETE have arguments that match the primary key attributes of the entity for which they will be used.\n"]];
    
    if (modelIsConsistent)
        [doc appendConsistencyCheckSuccessText:[[NSAttributedString alloc] initWithString:@"The model has at least one entity.\n"]];
}


- (void)checkEntityNotification:(NSNotification *)notification
{
    EOEntity *entity = [[notification userInfo] objectForKey:EOMConsistencyModelObjectKey];
    EOModelerDocument *doc = [notification object];

    // If this is an Oracle model, make sure this entity doesn't
    // have more than one blob attribute.

    if ([[[entity model] adaptorName] hasPrefix:@"Oracle"]) {
        int i, icount;
        int numberOfBlobs = 0;
        NSArray *atts = [entity attributes];

        for (i = 0, icount = [atts count]; i < icount; i++) {
            EOAttribute *att = [atts objectAtIndex:i];
            if (![att isFlattened] && [[att externalType] hasPrefix:@"LONG"])
                numberOfBlobs++;
        }
        if (numberOfBlobs > 1) {
            [doc appendConsistencyCheckErrorText:[NSMutableAttributedString mutableAttributedStringWithBoldSubstitutionsWithFormat:@"Entity %@ has more than one LONG or LONGRAW  columns and the Oracle database does not support this.\n", [entity name]]];
            // Please note that mutableAttributedStringWithBoldSubstitutionsWithFormat:
            // will only deal with substitutions of the form "%@" and it expects all the
            // args to be NSStrings. It is limited, but very useful for generating error
            // strings for consistency checking.
            entitiesAreConsistent = NO;
        }
    }
}

- (void)checkStoredProcedureNotification:(NSNotification *)notification
{
    EOStoredProcedure *sp = [[notification userInfo] objectForKey:EOMConsistencyModelObjectKey];
    EOModelerDocument *doc = [notification object];
    NSArray *entities = [[sp model] entities];
    int i, icount;

    // If this stored procedure is used as a delete stored procedure, make sure the args
    // match the primary key attributes.

    for (i = 0, icount = [entities count]; i < icount; i++) {
        EOEntity *entity = [entities objectAtIndex:i];
        if ([entity storedProcedureForOperation:EODeleteProcedureOperation] == sp) {
            NSMutableArray *pkAttrs, *args;

            pkAttrs = [[entity primaryKeyAttributes] mutableCopy];
            args = [[sp arguments] mutableCopy];

            if ([pkAttrs count] != [args count]) {
                [doc appendConsistencyCheckErrorText:[NSMutableAttributedString mutableAttributedStringWithBoldSubstitutionsWithFormat:@"The number of arguments for stored procedure %@ does not match the number of primary key attributes for entity %@, which lists %@ as its stored procedure for DELETE operations.\n", [sp name], [entity name], [sp name]]];
                storedProceduresAreConsistent = NO;
            } else {
                int j, jcount;
                int k, kcount;

                for (j = 0, jcount = [pkAttrs count]; j < jcount; j++) {
                    EOAttribute *pkAtt = [pkAttrs objectAtIndex:i];
                    EOAttribute *argIndex;
                    EOAttribute *matchingArg = nil;
                    
                    for (k = 0, kcount = [args count]; k < kcount && !matchingArg; k++) {
                        argIndex = [args objectAtIndex:k];
                        if ([[argIndex name] isEqual:[pkAtt name]])
                            matchingArg = argIndex;
                    }
                    if (!matchingArg) {
                        [doc appendConsistencyCheckErrorText:[NSMutableAttributedString mutableAttributedStringWithBoldSubstitutionsWithFormat:@"Stored procedure %@ is used by entity %@ for DELETE, but it does not have an argument named %@ to match the primary key attribute %@.\n", [sp name], [entity name], [pkAtt name], [pkAtt name]]];
                        storedProceduresAreConsistent = NO;
                    } else { // we have a match
                        [pkAttrs removeObjectIdenticalTo:pkAtt];
                        j--;
                        [args removeObjectIdenticalTo:matchingArg];
                    }
                }
            }

            [pkAttrs release];
            [args release];
        }
    }
}

- (void)checkModelNotification:(NSNotification *)notification
{
    EOModel *model = [[notification userInfo] objectForKey:EOMConsistencyModelObjectKey];
    EOModelerDocument *doc = [notification object];

    if (![[model entities] count]) {
        [doc appendConsistencyCheckErrorText:[[NSAttributedString alloc] initWithString:@"This model does not have any entities!\n"]];
        modelIsConsistent = NO;
    }
}

@end
