/* Test use of -awakeAfterUsingCoder:

   Copyright (C) 2005 Free Software Foundation, Inc.

  Copying and distribution of this file, with or without modification,
  are permitted in any medium without royalty provided the copyright
  notice and this notice are preserved.
   
   This file is part of the GNUstep Base Library.

   This is a rather complicated, strenuous test, borrowing elements
   from the fref.m test.
 */


/* Beginning of some parameters to vary. */
/* 0 and 0 works.
   1 and 0 works for the first test, but has no way of getting the
     super_foo in second test right, because we haven't resolved the
     forward reference when -awakeAfterUsingCoder is called; this is
     an inherent problem of decoding.
   0 and 1 crashes, as does NeXT's (for reasons related to forward
     references, having nothing to do with awakeAfterUsingCoder). */

/* Use GNU Archiving features, if they are available. */
#define TRY_GNU_ARCHIVING 0

/* In the forward self-reference test, -initWithCoder substitutes
   another object for self. */
#define SELF_REF_INITWITHCODER_SUBSTITUTES 0

/* End of some parameters to vary. */

#ifdef NX_CURRENT_COMPILER_RELEASE
#include <foundation/NSArchiver.h>
#include <foundation/NSArray.h>
#include <foundation/NSAutoreleasePool.h>
#else
#include <Foundation/NSArchiver.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSAutoreleasePool.h>
#endif
#include <Foundation/NSDebug.h>

#define GNU_ARCHIVING \
(TRY_GNU_ARCHIVING && defined(GNUSTEP_BASE_MAJOR_VERSION))

#if GNU_ARCHIVING
#include <GNUstepBase/Archiver.h>
#endif /* GNU_ARCHIVING */


/* The -initWithCoder methods substitutes another object for self. */
int initWithCoder_substitutes;
int Foo_awakeAfterUsingCoder_substitutes = 0;
int SubFoo_awakeAfterUsingCoder_substitutes = 1;

/* This object encodes an -encodeConditionalObject: reference to a Foo. */
@interface SubFoo : NSObject
{
  id super_foo;
  int label;
}
@end

/* This object encodes an -encodeObject: reference to a SubFoo */
/* Object may offer a replacement of itself using -awakeAfterUsingCoder: */
@interface Foo : NSObject
{
  id sub_foo;
  int label;
}
- (int) label;
- (void) setLabel: (int)l;
@end


/* Object may offer a replacement of itself using -awakeAfterUsingCoder: */
@implementation SubFoo

- initWithSuperFoo: o label: (int)l
{
  [super init];
  super_foo = o;
  label = l;
  return self;
}

- awakeAfterUsingCoder: coder
{
  if (SubFoo_awakeAfterUsingCoder_substitutes)
    {
      SubFoo *replacement = [[[self class] alloc]
			      initWithSuperFoo: super_foo
			      label: label + 100];
      /* NOTE: We can't use the ivar SUPER_FOO here because it won't have
	 been resolved by the Decoder yet. */
      [self release];
      return replacement;
    }
  return self;
}

- superFoo
{
  return super_foo;
}

- (void) encodeWithCoder: coder
{
  printf ("In [SubFoo encodeWithCoder:]\n");
#if GNU_ARCHIVING
  [coder encodeObjectReference: super_foo
	 withName: @"super foo"];
#else
  [coder encodeConditionalObject: super_foo];
#endif
  [coder encodeValueOfObjCType: @encode(int)
	 at: &label];
}

- initWithCoder: coder
{
  if (initWithCoder_substitutes)
    {
      id o = self;
      self = [[[self class] alloc] init];
      [o release];
    }
#if GNU_ARCHIVING
  [coder decodeObjectAt: &super_foo
	 withName: NULL];
#else
  super_foo = [coder decodeObject];
#endif
  [coder decodeValueOfObjCType: @encode(int)
	 at: &label];
  return self;
}

- (void) print
{
  printf ("SubFoo label = %d, super label = %d\n",
	  label, [super_foo label]);
}

@end


@implementation Foo

- init
{
  [super init];
  sub_foo = nil;
  label = 0;
  return self;
}

- awakeAfterUsingCoder: coder
{
  if (Foo_awakeAfterUsingCoder_substitutes)
    {
      Foo *replacement = [[[self class] alloc] init];
      [replacement setLabel: label + 100];
      /* NOTE: We can't use the ivar SUPER_FOO here because it won't have
	 been resolved by the Decoder yet. */
      [self release];
      return replacement;
    }
  return self;
}

- (void) setSubFoo: o
{
  sub_foo = o;
}

- subFoo
{
  return sub_foo;
}

- (void) encodeWithCoder: coder
{
  printf ("In [Foo encodeWithCoder:]\n");
  [coder encodeObject: sub_foo];
  [coder encodeValueOfObjCType: @encode(int)
	 at: &label];
}

- initWithCoder: coder
{
  if (initWithCoder_substitutes)
    {
      id o = self;
      self = [[[self class] alloc] init];
      [o release];
    }
  sub_foo = [coder decodeObject];
  [coder decodeValueOfObjCType: @encode(int)
	 at: &label];
  return self;
}

- (int) label
{
  return label;
}

- (void) setLabel: (int)l
{
  label = l;
}

- (void) print
{
  printf ("   Foo label = %d,   sub label = %d\n",
	  label, [sub_foo label]);
}

@end

/* Test the use of -encodeConditional to encode a forward reference
   to an object. */
void
test_fref ()
{
  id array;
  id foo, sub_foo;
  printf ("\nTest awakeAfterUsingCoder substitution of objects that will\n"
	  "   satisfy backward references\n");
  initWithCoder_substitutes = 0;

  array = [[NSMutableArray alloc] init];
  foo = [[Foo alloc] init];
  [foo setLabel: 4];
  sub_foo = [[SubFoo alloc] initWithSuperFoo: nil label: 3];
  [array addObject: foo];
  [array addObject: sub_foo];

#if GNU_ARCHIVING
  [Archiver archiveRootObject: array toFile: @"fref.dat"];
#else
  [NSArchiver archiveRootObject: array toFile: @"fref.dat"];
#endif
  printf ("Encoded:  ");
  [sub_foo print];
  [foo release];
  [sub_foo release];
  [array release];

  array = [NSUnarchiver unarchiveObjectWithFile: @"fref.dat"];
  foo = [array objectAtIndex: 0];
  sub_foo = [array objectAtIndex: 1];
  printf ("Decoded:  ");
  [sub_foo print];
}

/* Test awakeAfterUsingCoder of a self-referential forward reference. */
void
test_self_fref ()
{
  id foo, sub_foo;
  printf ("\nTest awakeAfterUsingCoder substitution of objects that\n"
	  "   will satisfy self-referential forward references\n");
  initWithCoder_substitutes = SELF_REF_INITWITHCODER_SUBSTITUTES;

  foo = [[Foo alloc] init];
  [foo setLabel: 4];
  sub_foo = [[SubFoo alloc] initWithSuperFoo: foo label: 3];
  [foo setSubFoo: sub_foo];

#if GNU_ARCHIVING
  [Archiver archiveRootObject: foo toFile: @"fref.dat"];
#else
  [NSArchiver archiveRootObject: foo toFile: @"fref.dat"];
#endif
  printf ("Encoded:  ");
  [sub_foo print];
  [foo release];
  [sub_foo release];

  foo = [NSUnarchiver unarchiveObjectWithFile: @"fref.dat"];
  sub_foo = [foo subFoo];
  printf ("Decoded:  ");
  [sub_foo print];
}

int
main ()
{
  id arp;

  arp = [NSAutoreleasePool new];
  [NSAutoreleasePool enableDoubleReleaseCheck:YES];
  GSDebugAllocationActive(YES);

#if TEXTCSTREAM
  [Archiver setDefaultCStreamClass: [TextCStream class]];
#endif

  [arp release];
  arp = [NSAutoreleasePool new];
  printf ("Decoded SubFoo label's should be 100 more than Encoded.\n");
  test_fref ();
  [arp release];
  arp = [NSAutoreleasePool new];
  test_self_fref ();
  [arp release];
  printf("Object allocation info -\n%s\n", GSDebugAllocationList(0));
  exit (0);
}
