/* -*-objc-*- */

/* 
  $Header$
  $Author: dglattin $
  $Date$
  $Log$
 */

#include  <objc.h>
#include  <objc-proto.h>

#include  <Object.h>
#include  <SubClass1.h>
#include  <SubClass2.h>
#include  <SubClass3.h>
#include  <SubClass4.h>
#include  <SubClass5.h>
#include  <Confuse.h>
#include  <ConfuseMore.h>

#include  <assert.h>
#include  <fcntl.h>
#include  <limits.h>
#include  <stdlib.h>
#include  <stdio.h>
#include  <sys/file.h>
#include  <time.h>


void  func (void) {

}


/*
 * These tests should be self explanatory but I'll
 *  narrate anyway.
 */
int main (int argc, char* argv[]) {

  Object      *myObject = NULL,
              *aCopy = NULL;
  SubClass1*  sc1;
  SubClass2*  sc2;
  SubClass3*  sc3;
  SubClass4*  sc4;
  SubClass5*  sc5;
  Ivar_t      aIvar;
  SEL         aSel;// = @selector (newOther);
  int         fd;


objcInitCleanup();
  aSel = @selector (newOther);

  printf( "Start of tests.  If you get here you're doing well.\n");

  /* Preposing tests. */
  assert([ SubClass2 superClass ] == [ SubClass1 superClass ]);

  /* Should be interesting. */
  [ SubClass4 poseAs:[ SubClass1 class ]];


  /* Make a object then copy it.
    They should be different. */
  myObject = [ Object new ];
  aCopy = [ myObject copy ];
  assert(myObject != aCopy);
  
  /* I hope nobody returned NULL. */
  assert(myObject);
  assert(aCopy);

  /* Get Object's class struct. */
  assert([ Object class ]); 
  
  /* Object's class name should be "Object". */ 
  assert(!strcmp (class_getClassName ([ Object class ]), "Object"));
  
  /* Object's super class is NULL  (no super class). */
  assert(![ Object superClass ]);

  /* Send more factory methods to Object.
    See if they respond  (or don't respond) to some methods. 
    Instances don't respond to factory methods.
    They respond to instance methods.
    They should not respond to methods not in their specification. */
  assert(![ Object instancesRespondTo:@selector (new)]);
  assert([ Object instancesRespondTo:@selector (name)]);
  assert(![Object instancesRespondTo:@selector (__not_probable_method__)]);

  /* See if we can set and interrogate a class's
    version. */
  [ Object setVersion:217 ];
  assert(217 == [ Object version ]);
  
  /* self should work. */
  assert(myObject == [ myObject self ]);

  /* Test a couple of paths to Object's class name. */
  assert(!strcmp ([ myObject name ], "Object"));
  assert(!strcmp ([ myObject name ], object_getClassName (myObject)));
  assert(!strcmp ((char*)[ myObject perform:@selector (name)], [ myObject name ]));

  /* Class of factory object and its instance should be
    the same. */
  assert([ Object class ] == [ myObject class]);

  /* Super class of Object is NULL. */
  assert([ Object superClass ] == [ myObject superClass ]);
  assert(![myObject superClass ]);
  assert(![ Object superClass ]);

  /* A instance of Object is a kindOf Object and
    a member of its hierarchy. */
  assert([ myObject isKindOf:[ Object class ]]);
  assert([ myObject isKindOfGivenName:"Object" ]);
  assert([ myObject isMemberOf:[ Object class ]]);
  assert([ myObject isMemberOfGivenName:"Object" ]);

  /* Like the factory object, instances can
    be tested for responding to selecotrs too. */
  assert(![ myObject respondsTo:@selector (new)]);
  assert([ myObject respondsTo:@selector (hash)]);
  assert(![ myObject respondsTo:@selector (__not_probable_method__)]);

  /* Test common features between a object and its copy. */
  assert([ aCopy class ] == [ myObject class ]);
  assert([ aCopy superClass ] == [ myObject superClass ]);

  /* -free methods are suppose to return nil. */
  assert(![ aCopy free ]);
  
  /* Check some differences between Object factory
    and a subclass's factory. */
  assert([ SubClass1 version ] != [ Object version ]);
  assert([ SubClass1 class ] != [ Object class ]);
  assert([ SubClass1 superClass ] != [ Object superClass ]);
  assert(![ Object instancesRespondTo:@selector (hokeyMethod)]);
  assert([ SubClass1 instancesRespondTo:@selector (hokeyMethod)]);

  /* Create a subclass instances and free it. */
  sc1 = [ SubClass1 new ];
  assert(sc1);
  assert(![ sc1 free ]);
  
  /* uh, make another. */
  sc1 = [ SubClass1 newOther ];
  assert(sc1);
  
  /* hokey returns self.  Is it the same as self? */
  assert([ sc1 self ] == [ sc1 hokeyMethod ]);
  
  /* Indirectly tell the subclass to do something. */
  [ sc1 perform:@selector (print) ];
  [ sc1 perform:@selector (print:) with: (id)"this is a message sent to " 
    "a instance of SubClass1 in a weird way" ];

  /* Get the address of some of instance variables of
    the subclass.  Some of the variables we're looking for
    aren't defined in the class.  test that too. */
  aIvar = object_getIvarAddress (sc1, "isa");
  assert(aIvar);
  aIvar = object_getIvarAddress (sc1, "shit");
  assert(!aIvar);
  aIvar = object_getIvarAddress (sc1, "dumb");
  assert(aIvar);

  /* More name checking */
  assert(!strcmp ([ sc1 name ], "SubClass1"));
  assert(!strcmp ([ sc1 name ], object_getClassName (sc1)));
  assert(!strcmp ((char*)[ sc1 perform:@selector (name)], [ sc1 name ]));

  /* Have the tester check this info. S/he isn't doing anything. */
  printf ("obj=%s, ivar=%s, offset=%d, obj size=%d\n", 
    [ sc1 name ], aIvar->ivar_name, 
    aIvar->ivar_offset, [ sc1 class ]->instance_size );


  /* Different subclass of Object. */
  
  /* Check some factory differences between Subclass2,
    Subclass1 and Object. */
  assert([ SubClass2 version ] != [ Object version ]);
  assert([ SubClass1 version ] == [ SubClass2 version ]);
  assert([ SubClass2 class ] != [ Object class ]);
  assert([ SubClass2 class ] != [ SubClass1 class ]);
  assert([ SubClass2 superClass ] != [ Object superClass ]);
  assert([ SubClass2 superClass ] != [ SubClass1 superClass ]);
  assert([ SubClass2 instancesRespondTo:@selector (print:with:)]);
  assert(![ SubClass1 instancesRespondTo:@selector (print::)]);

  /* Create a instance.  SubClass2 doesn't define +new
    but it should be found in a super class. */
  sc2 = [ SubClass2 new ];
  assert(sc2);
  
  /* More name checking */
  assert(!strcmp ([ sc2 name ], "SubClass2"));
  assert(!strcmp ([ sc2 name ], object_getClassName (sc2)));
  assert(!strcmp ((char*)[ sc2 perform:@selector (name)], [ sc2 name ]));
  
  /* Free the instance and makke sure its gone.
    Subclass2 doesn't define -free so its super class
    handles this one too. */
  assert(![ sc2 free ]);
  
  /* Subclass2 defines this factory method to create instances. */
  sc2 = [ SubClass2 newOther ];
  assert(sc2);

  /* More name checking */
  assert(!strcmp ([ sc2 name ], "SubClass2"));
  assert(!strcmp ([ sc2 name ], object_getClassName (sc2)));
  assert(!strcmp ((char*)[ sc2 perform:@selector (name)], [ sc2 name ]));

  /* Super class call to self should return the same
    value as hokey. */
  assert([ sc2 self ] == [ sc2 hokeyMethod ]);
  
  /* These should be different. */
  assert([ sc2 self ] != [ sc1 self ]);
  
  /* More name checking */
  assert(!strcmp ([ sc2 name ], "SubClass2"));
  assert(!strcmp ([ sc2 name ], object_getClassName (sc2)));
  assert(!strcmp ((void*)[ sc2 perform:@selector (name)], [ sc2 name ]));

  /* Indirectly tell SubClass2 to do something. */
  [ sc2 perform:@selector (print) ];
  [ sc2 perform:@selector (print:) with: (id)"a message" ];
  [ sc2 perform:@selector (print:) with: (id)"this is a message sent to " 
    "a instance of SubClass2 in a weird way" ];

  /* Get the address of some of instance variables of
    the subclass.  Some of the variables we're looking for
    aren't defined in the class.  test that too. */
  aIvar = object_getIvarAddress (sc2, "isa");
  assert(aIvar);
  aIvar = object_getIvarAddress (sc2, "dumb");
  assert(aIvar);
  
  /* Isn't the tester person doing anything yet? */
  printf ("obj=%s, ivar=%s, offset=%d, obj size=%d\n", 
    [ sc2 name ], aIvar->ivar_name, 
    aIvar->ivar_offset, [ sc2 class ]->instance_size );


  /* Test a subclass of a subclass. */

  /* Factory method difference tests. */
  assert([ SubClass3 version ] != [ Object version ]);
  assert([ SubClass3 version ] == [ SubClass2 version ]);
  assert([ SubClass3 class ] != [ SubClass2 class ]);
  assert([ SubClass3 class ] != [ SubClass1 class ]);
  assert([ SubClass3 class ] != [ Object class ]);
  assert([ SubClass2 superClass ] != [ Object superClass ]);
  assert([ SubClass3 superClass ] != [ SubClass2 superClass ]);
  assert([ SubClass3 superClass ] != [ SubClass1 superClass ]);
  assert([ SubClass3 instancesRespondTo:@selector (print:with:)]);
  assert([ SubClass3 instancesRespondTo:@selector (print:)]);
  
  /* SubClass defines +new but its super class doesn't. */
  sc3 = [ SubClass3 new ];
  assert(sc3);
  
  /* More name checking */
  assert(!strcmp ([ sc3 name ], "SubClass3"));
  assert(!strcmp ([ sc3 name ], object_getClassName (sc3)));
  assert(!strcmp ((char*)[ sc3 perform:@selector (name)], [ sc3 name ]));

  /* GO AWAY! */
  assert(![ sc3 free ]);
  
  /* WAIT!  COME BACK! */
  sc3 = [ SubClass3 new ];
  assert(sc3);

  /* Check self. */
  assert([ sc3 self ] == [ sc3 hokeyMethod ]);
  assert([ sc3 self ] != [ sc1 self ]);
  assert([ sc3 self ] != [ sc2 self ]);
  
  /* Indirectly tell SubClass3 to do something. */
  [ sc3 perform:@selector (print) ];
  [ sc3 perform:@selector (print:) with: (id)"a message" ];
  [ sc3 perform:@selector (print:) with: (id)"this is a message sent to " 
    "a instance of SubClass2 in a weird way" ];

  /* Check to see if some instance variables exist. */
  aIvar = object_getIvarAddress (sc3, "isa");
  assert(aIvar);
  aIvar = object_getIvarAddress (sc3, "dumb");
  assert(aIvar);
  aIvar = object_getIvarAddress (sc3, "smart");
  assert(aIvar);
  
  /* Asleep yet? */
  printf ("obj=%s, ivar=%s, offset=%d, obj size=%d\n", 
    [ sc3 name ], aIvar->ivar_name, 
    aIvar->ivar_offset, [ sc3 class ]->instance_size );


  /* Category testing. */
  assert([ SubClass1 instancesRespondTo:@selector (additionalMethod1)]);
  assert([ SubClass1 instancesRespondTo:@selector (additionalMethod2)]);
  assert([ SubClass1 instancesRespondTo:@selector (additionalMethod3)]);
  assert([ SubClass1 instancesRespondTo:@selector (additionalMethod4)]);
  assert([ SubClass1 instancesRespondTo:@selector (additionalMethod5)]);
  assert([ SubClass1 instancesRespondTo:@selector (additionalMethod_2)]);
  assert([ SubClass1 instancesRespondTo:@selector (additionalMethod_3)]);
  assert([ sc1 respondsTo:@selector (additionalMethod1)]);
  assert([ sc1 respondsTo:@selector (additionalMethod2)]);
  assert([ sc1 respondsTo:@selector (additionalMethod3)]);
  assert([ sc1 respondsTo:@selector (additionalMethod4)]);
  assert([ sc1 respondsTo:@selector (additionalMethod5)]);
  assert([ sc1 respondsTo:@selector (additionalMethod_2)]);
  assert([ sc1 respondsTo:@selector (additionalMethod_3)]);
  
  assert([ SubClass2 instancesRespondTo:@selector (additionalMethod1)]);
  assert([ SubClass2 instancesRespondTo:@selector (additionalMethod2)]);
  assert([ SubClass2 instancesRespondTo:@selector (additionalMethod3)]);
  assert([ SubClass2 instancesRespondTo:@selector (additionalMethod4a)]);
  assert([ SubClass2 instancesRespondTo:@selector (additionalMethod5a)]);
  assert([ SubClass2 instancesRespondTo:@selector (additionalMethod6a)]);
  assert([ SubClass2 instancesRespondTo:@selector (additionalMethod7a)]);
  assert([ SubClass2 instancesRespondTo:@selector (additionalMethod8a)]);
  assert([ SubClass2 instancesRespondTo:@selector (additionalMethod_1)]);
  assert([ SubClass2 instancesRespondTo:@selector (additionalMethod_2)]);
  assert([ SubClass2 instancesRespondTo:@selector (additionalMethod_3)]);
  assert([ sc2 respondsTo:@selector (additionalMethod1)]);
  assert([ sc2 respondsTo:@selector (additionalMethod2)]);
  assert([ sc2 respondsTo:@selector (additionalMethod3)]);
  assert([ sc2 respondsTo:@selector (additionalMethod4a)]);
  assert([ sc2 respondsTo:@selector (additionalMethod5a)]);
  assert([ sc2 respondsTo:@selector (additionalMethod6a)]);
  assert([ sc2 respondsTo:@selector (additionalMethod7a)]);
  assert([ sc2 respondsTo:@selector (additionalMethod8a)]);
  assert([ sc2 respondsTo:@selector (additionalMethod_1)]);
  assert([ sc2 respondsTo:@selector (additionalMethod_2)]);
  assert([ sc2 respondsTo:@selector (additionalMethod_3)]);

  assert([ SubClass3 instancesRespondTo:@selector (additionalMethod1a)]);
  assert([ SubClass3 instancesRespondTo:@selector (additionalMethod2a)]);
  assert([ SubClass3 instancesRespondTo:@selector (additionalMethod_1a)]);
  assert([ SubClass3 instancesRespondTo:@selector (additionalMethod_2a)]);

  assert([ sc3 respondsTo:@selector (additionalMethod1a)]);
  assert([ sc3 respondsTo:@selector (additionalMethod2a)]);
  assert([ sc3 respondsTo:@selector (additionalMethod_1a)]);
  assert([ sc3 respondsTo:@selector (additionalMethod_2a)]);

  /* YEA YOU!  Wake up! */
  [ SubClass3 additionalClassMethod ];
  [ SubClass3 additionalClassMethodTwo ];


  /* Relationship tests. 
    This sequence tests isKindOfGivenName: too. */
  assert([ myObject isKindOf:[ Object class ]]);
  assert([ sc1 isKindOf:[ Object class ]]);
  assert([ sc2 isKindOf:[ Object class ]]);
  assert([ sc3 isKindOf:[ Object class ]]);
  assert([ sc1 isKindOf:[ SubClass1 class ]]);
  assert([ sc2 isKindOf:[ SubClass2 class ]]);
  assert([ sc3 isKindOf:[ SubClass3 class ]]);
  assert([ sc3 isKindOf:[ SubClass2 class ]]);
  assert(![ sc2 isKindOf:[ SubClass1 class ]]);

  /* More relationship tests.
    This sequence tests isMemberOfGivenName: too. */
  assert([ myObject isMemberOf:[ Object class ]]);
  assert([ sc1 isMemberOf:[ SubClass1 class ]]);
  assert([ sc2 isMemberOf:[ SubClass2 class ]]);
  assert([ sc3 isMemberOf:[ SubClass3 class ]]);

  /* More relationship tests. */
  assert(![ myObject isMemberOf:[ SubClass1 class ]]);
  assert(![ myObject isMemberOf:[ SubClass2 class ]]);
  assert(![ myObject isMemberOf:[ SubClass3 class ]]);
  assert(![ sc1 isMemberOf:[ Object class ]]);
  assert(![ sc1 isMemberOf:[ SubClass2 class ]]);
  assert(![ sc1 isMemberOf:[ SubClass3 class ]]);
  assert(![ sc2 isMemberOf:[ Object class ]]);
  assert(![ sc2 isMemberOf:[ SubClass1 class ]]);
  assert(![ sc2 isMemberOf:[ SubClass3 class ]]);
  assert(![ sc3 isMemberOf:[ Object class ]]);
  assert(![ sc3 isMemberOf:[ SubClass1 class ]]);
  assert(![ sc3 isMemberOf:[ SubClass2 class ]]);


  /* Archiving */
  fd = open  ("/tmp/shit.arcv", O_CREAT | O_TRUNC | O_RDWR, 0666);
  assert  (fd >= 0);
  sc1 = [ SubClass1 new ];
  sc2 = [ SubClass2 new ];
  sc3 = [ SubClass3 new ];
  sc4 = [ SubClass4 new ];
  sc5 = [ SubClass5 new ];
  
  [ sc5 storeOn:fd ];
  [ sc3 storeOn:fd ];
  [ sc1 storeOn:fd ];
  [ sc4 storeOn:fd ];
  [ sc2 storeOn:fd ];
  
  lseek  (fd, 0, L_SET);
  assert  (fd >= 0);
  
  sc5 = [ SubClass5 readFrom:fd ];
  sc3 = [ SubClass3 readFrom:fd ];
  sc1 = [ SubClass1 readFrom:fd ];
  sc4 = [ SubClass4 readFrom:fd ];
  sc2 = [ SubClass2 readFrom:fd ];

  assert  (!strcmp ([ sc1 name ], "SubClass1"));
  assert  (!strcmp ([ sc2 name ], "SubClass2"));
  assert  (!strcmp ([ sc3 name ], "SubClass3"));
  assert  (!strcmp ([ sc4 name ], "SubClass4"));
  assert  (!strcmp ([ sc5 name ], "SubClass5"));

  close  (fd);


  /* Posing tests */
  assert([ SubClass1 return12 ] == 13);
  assert([ SubClass4 return12 ] == 13);
  sc1 = [ SubClass1 new ];
  assert([ sc1 return15 ] == 15);
  assert([ sc1 return24 ] == 24);
  assert([ SubClass5 return12 ] == 12);
  sc5 = [ SubClass5 new ];
  assert([ sc5 return15 ] == 15);
  assert([ sc5 return24 ] == 24);
  assert([ sc5 return33 ] == 34);
  assert([ sc5 return45 ] == 45); /* this will work but is an
                                      error.  */


  printf ("Exit, the tests worked! "
    " (you did compile with -DDEBUG -UNDEBUG didn't you?)\n");


  /* Performance measurement stuff. */
  printf ("\n\nSome performance data:\n\n");
  
  { /* Coloring alogorithm of the compiler should make
      this fast. */
#define ITERATIONS  1000000

    int         i;
    time_t      startTime, diffTime;
    id          aObj;

    startTime = time (NULL);
    for (i = 0; i < ITERATIONS; ++i)
      func ();
    diffTime = time (NULL) - startTime ;
    printf ("for (i = 0; i < ITERATIONS; ++i)\n");
    printf ("  func ();\n");
    printf ("%d iterations, %d sec  (%g/sec)\n\n",
      ITERATIONS, diffTime, (double)ITERATIONS/diffTime);

#if 0
    startTime = time (NULL);
    for (i = 0; i < ITERATIONS; ++i)
      [[ Object new ] free ];
    diffTime = time (NULL) - startTime ;
    printf ("for (i = 0; i < ITERATIONS; ++i)\n");
    printf ("  [[ Object new ] free ];\n");
    printf ("%d iterations, %d sec  (%g/sec)\n\n",
      ITERATIONS, diffTime, (double)ITERATIONS/diffTime);

    startTime = time (NULL);
    for (i = 0; i < ITERATIONS; ++i)
      [[ SubClass2 new ] free ];
    diffTime = time (NULL) - startTime ;
    printf ("for (i = 0; i < ITERATIONS; ++i)\n");
    printf ("  [[ SubClass2 new ] free ];\n");
    printf (" (super class must be searched for +new)\n");
    printf ("%d iterations, %d sec  (%g/sec)\n\n",
      ITERATIONS, diffTime, (double)ITERATIONS/diffTime);
  
    startTime = time (NULL);
    for (i = 0; i < ITERATIONS; ++i)
      [[ SubClass2 newOther ] free ];
    diffTime = time (NULL) - startTime ;
    printf ("for (i = 0; i < ITERATIONS; ++i)\n");
    printf ("  [[ SubClass2 newOther ] free ];\n");
    printf (" (+newOther calls +new in its super class)\n");
    printf ("%d iterations, %d sec  (%g/sec)\n\n",
      ITERATIONS, diffTime, (double)ITERATIONS/diffTime);
#endif

    aObj = [ Object new ];
    startTime = time (NULL);
    for (i = 0; i < ITERATIONS; ++i)
      [ aObj self ];
    diffTime = time (NULL) - startTime ;
    printf ("aObj = [ Object new ];\n");
    printf ("for (i = 0; i < ITERATIONS; ++i)\n");
    printf ("  [ aObj self ];\n");
    printf ("%d iterations, %d sec  (%g/sec)\n\n",
      ITERATIONS, diffTime, (double)ITERATIONS/diffTime);
    [ aObj free ];

    aObj = [ SubClass3 new ];
    startTime = time (NULL);
    for (i = 0; i < ITERATIONS; ++i)
      [ aObj self ];
    diffTime = time (NULL) - startTime ;
    printf ("aObj = [ SubClass3 new ];\n");
    printf ("for (i = 0; i < ITERATIONS; ++i)\n");
    printf ("  [ aObj self ];\n");
    printf (" (-self is implemented two classes up)\n");
    printf ("%d iterations, %d sec  (%g/sec)\n\n",
      ITERATIONS, diffTime, (double)ITERATIONS/diffTime);
    [ aObj free ];

    aObj = [ SubClass3 new ];
    startTime = time (NULL);
    for (i = 0; i < ITERATIONS; ++i)
      [[ aObj self ] self ];
    diffTime = time (NULL) - startTime ;
    printf ("aObj = [ SubClass3 new ];\n");
    printf ("for (i = 0; i < ITERATIONS; ++i)\n");
    printf ("  [[ aObj self ] self ];\n");
    printf ("%d iterations, %d sec  (%g/sec)\n\n",
      ITERATIONS, diffTime, (double)ITERATIONS/diffTime);
    [ aObj free ];
  }
  
  
  return 0;
}
