// See instructions in weak.h

#include "test.h"
#include "weak.h"

// Subclass of superclass that isn't there
@interface MyMissingSuper : MissingSuper
+(int) method;
@end
@implementation MyMissingSuper
+(int) method { return 1+[super method]; }
+(void) load { state++; }
@end

// Subclass of subclass of superclass that isn't there
@interface MyMissingSub : MyMissingSuper
+(int) method;
@end
@implementation MyMissingSub
+(int) method { return 1+[super method]; }
+(void) load { state++; }
@end

// Subclass of real superclass 
@interface MyNotMissingSuper : NotMissingSuper
+(int) method;
@end
@implementation MyNotMissingSuper
+(int) method { return 1+[super method]; }
+(void) load { state++; }
@end

// Subclass of subclass of superclass that isn't there
@interface MyNotMissingSub : MyNotMissingSuper
+(int) method;
@end
@implementation MyNotMissingSub
+(int) method { return 1+[super method]; }
+(void) load { state++; }
@end

// Categories on all of the above
@interface MissingRoot (MissingRootExtras)
+(void)load;
+(int) cat_method;
@end
@implementation MissingRoot (MissingRootExtras)
+(void)load { state++; }
+(int) cat_method { return 40; }
@end

@interface MissingSuper (MissingSuperExtras)
+(void)load;
+(int) cat_method;
@end
@implementation MissingSuper (MissingSuperExtras)
+(void)load { state++; }
+(int) cat_method { return 1+[super cat_method]; }
@end

@interface MyMissingSuper (MyMissingSuperExtras)
+(void)load;
+(int) cat_method;
@end
@implementation MyMissingSuper (MyMissingSuperExtras)
+(void)load { state++; }
+(int) cat_method { return 1+[super cat_method]; }
@end

@interface MyMissingSub (MyMissingSubExtras)
+(void)load;
+(int) cat_method;
@end
@implementation MyMissingSub (MyMissingSubExtras)
+(void)load { state++; }
+(int) cat_method { return 1+[super cat_method]; }
@end


@interface NotMissingRoot (NotMissingRootExtras)
+(void)load;
+(int) cat_method;
@end
@implementation NotMissingRoot (NotMissingRootExtras)
+(void)load { state++; }
+(int) cat_method { return 30; }
@end

@interface NotMissingSuper (NotMissingSuperExtras)
+(void)load;
+(int) cat_method;
@end
@implementation NotMissingSuper (NotMissingSuperExtras)
+(void)load { state++; }
+(int) cat_method { return 1+[super cat_method]; }
@end

@interface MyNotMissingSuper (MyNotMissingSuperExtras)
+(void)load;
+(int) cat_method;
@end
@implementation MyNotMissingSuper (MyNotMissingSuperExtras)
+(void)load { state++; }
+(int) cat_method { return 1+[super cat_method]; }
@end

@interface MyNotMissingSub (MyNotMissingSubExtras)
+(void)load;
+(int) cat_method;
@end
@implementation MyNotMissingSub (MyNotMissingSubExtras)
+(void)load { state++; }
+(int) cat_method { return 1+[super cat_method]; }
@end


#if WEAK_FRAMEWORK
#   define TESTIVAR(cond) testassert(cond)
#else
#   define TESTIVAR(cond) /* rdar */
#endif

static BOOL classInList(__unsafe_unretained Class classes[], const char *name)
{
    for (int i = 0; classes[i] != nil; i++) {
        if (0 == strcmp(class_getName(classes[i]), name)) return YES;
    }
    return NO;
}

static BOOL classInNameList(const char **names, const char *name)
{
    const char **cp;
    for (cp = names; *cp; cp++) {
        if (0 == strcmp(*cp, name)) return YES;
    }
    return NO;
}

int main(int argc __unused, char **argv)
{
    BOOL weakMissing;
    if (strstr(argv[0], "-not-missing.exe")) {
        weakMissing = NO;
    } else if (strstr(argv[0], "-missing.exe")) {
        weakMissing = YES;
    } else {
        fail("executable name must be weak*-missing.exe or weak*-not-missing.exe");
    }

    // class and category +load methods
    if (weakMissing) testassert(state == 8);
    else testassert(state == 16);
    state = 0;

    // classes
    testassert([NotMissingRoot class]);
    testassert([NotMissingSuper class]);
    testassert([MyNotMissingSuper class]);
    testassert([MyNotMissingSub class]);
    if (weakMissing) {
        testassert([MissingRoot class] == nil);
        testassert([MissingSuper class] == nil);
        testassert([MyMissingSuper class] == nil);
        testassert([MyMissingSub class] == nil);
    } else {
        testassert([MissingRoot class]);
        testassert([MissingSuper class]);
        testassert([MyMissingSuper class]);
        testassert([MyMissingSub class]);
    }
    
    // objc_getClass
    testassert(objc_getClass("NotMissingRoot"));
    testassert(objc_getClass("NotMissingSuper"));
    testassert(objc_getClass("MyNotMissingSuper"));
    testassert(objc_getClass("MyNotMissingSub"));
    if (weakMissing) {
        testassert(objc_getClass("MissingRoot") == nil);
        testassert(objc_getClass("MissingSuper") == nil);
        testassert(objc_getClass("MyMissingSuper") == nil);
        testassert(objc_getClass("MyMissingSub") == nil);
    } else {
        testassert(objc_getClass("MissingRoot"));
        testassert(objc_getClass("MissingSuper"));
        testassert(objc_getClass("MyMissingSuper"));
        testassert(objc_getClass("MyMissingSub"));
    }

    // class list
    union {
        Class *c;
        void *v;
    } classes;
    classes.c = objc_copyClassList(NULL);
    testassert(classInList(classes.c, "NotMissingRoot"));
    testassert(classInList(classes.c, "NotMissingSuper"));
    testassert(classInList(classes.c, "MyNotMissingSuper"));
    testassert(classInList(classes.c, "MyNotMissingSub"));
    if (weakMissing) {
        testassert(! classInList(classes.c, "MissingRoot"));
        testassert(! classInList(classes.c, "MissingSuper"));
        testassert(! classInList(classes.c, "MyMissingSuper"));
        testassert(! classInList(classes.c, "MyMissingSub"));
    } else {
        testassert(classInList(classes.c, "MissingRoot"));
        testassert(classInList(classes.c, "MissingSuper"));
        testassert(classInList(classes.c, "MyMissingSuper"));
        testassert(classInList(classes.c, "MyMissingSub"));
    }
    free(classes.v);

    // class name list
    const char *image = class_getImageName(objc_getClass("NotMissingRoot"));
    testassert(image);
    const char **names = objc_copyClassNamesForImage(image, NULL);
    testassert(names);
    testassert(classInNameList(names, "NotMissingRoot"));
    testassert(classInNameList(names, "NotMissingSuper"));
    if (weakMissing) {
        testassert(! classInNameList(names, "MissingRoot"));
        testassert(! classInNameList(names, "MissingSuper"));
    } else {
        testassert(classInNameList(names, "MissingRoot"));
        testassert(classInNameList(names, "MissingSuper"));
    }
    free(names);

    image = class_getImageName(objc_getClass("MyNotMissingSub"));
    testassert(image);
    names = objc_copyClassNamesForImage(image, NULL);
    testassert(names);
    testassert(classInNameList(names, "MyNotMissingSuper"));
    testassert(classInNameList(names, "MyNotMissingSub"));
    if (weakMissing) {
        testassert(! classInNameList(names, "MyMissingSuper"));
        testassert(! classInNameList(names, "MyMissingSub"));
    } else {
        testassert(classInNameList(names, "MyMissingSuper"));
        testassert(classInNameList(names, "MyMissingSub"));
    }
    free(names);
    
    // methods
    testassert(20 == [NotMissingRoot method]);
    testassert(21 == [NotMissingSuper method]);
    testassert(22 == [MyNotMissingSuper method]);
    testassert(23 == [MyNotMissingSub method]);
    if (weakMissing) {
        testassert(0 == [MissingRoot method]);
        testassert(0 == [MissingSuper method]);
        testassert(0 == [MyMissingSuper method]);
        testassert(0 == [MyMissingSub method]);
    } else {
        testassert(10 == [MissingRoot method]);
        testassert(11 == [MissingSuper method]);
        testassert(12 == [MyMissingSuper method]);
        testassert(13 == [MyMissingSub method]);
    }
    
    // category methods
    testassert(30 == [NotMissingRoot cat_method]);
    testassert(31 == [NotMissingSuper cat_method]);
    testassert(32 == [MyNotMissingSuper cat_method]);
    testassert(33 == [MyNotMissingSub cat_method]);
    if (weakMissing) {
        testassert(0 == [MissingRoot cat_method]);
        testassert(0 == [MissingSuper cat_method]);
        testassert(0 == [MyMissingSuper cat_method]);
        testassert(0 == [MyMissingSub cat_method]);
    } else {
        testassert(40 == [MissingRoot cat_method]);
        testassert(41 == [MissingSuper cat_method]);
        testassert(42 == [MyMissingSuper cat_method]);
        testassert(43 == [MyMissingSub cat_method]);
    }

    // allocations and ivars
    id obj;
    NotMissingSuper *obj2;
    MissingSuper *obj3;
    testassert((obj = [[NotMissingRoot alloc] init])); 
    RELEASE_VAR(obj);
    testassert((obj2 = [[NotMissingSuper alloc] init]));
    TESTIVAR(obj2->ivar == 200); 
    RELEASE_VAR(obj2);
    testassert((obj2 = [[MyNotMissingSuper alloc] init]));
    TESTIVAR(obj2->ivar == 200); 
    RELEASE_VAR(obj2);
    testassert((obj2 = [[MyNotMissingSub alloc] init]));
    TESTIVAR(obj2->ivar == 200); 
    RELEASE_VAR(obj2);
    if (weakMissing) {
        testassert([[MissingRoot alloc] init] == nil);
        testassert([[MissingSuper alloc] init] == nil);
        testassert([[MyMissingSuper alloc] init] == nil);
        testassert([[MyMissingSub alloc] init] == nil);
    } else {
        testassert((obj = [[MissingRoot alloc] init])); 
        RELEASE_VAR(obj);
        testassert((obj3 = [[MissingSuper alloc] init])); 
        TESTIVAR(obj3->ivar == 100); 
        RELEASE_VAR(obj3);
        testassert((obj3 = [[MyMissingSuper alloc] init])); 
        TESTIVAR(obj3->ivar == 100); 
        RELEASE_VAR(obj3);
        testassert((obj3 = [[MyMissingSub alloc] init])); 
        TESTIVAR(obj3->ivar == 100); 
        RELEASE_VAR(obj3);
    }

    *strrchr(argv[0], '.') = 0;
    succeed(basename(argv[0]));
    return 0;
}

