/*
    ArchivedObject.m

    Implementation of the ArchivedObject class for the KeyArcher
    application.

    Copyright (C) 2006  Saso Kiselkov

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#import "ArchivedObject.h"

#import <Foundation/NSArray.h>
#import <Foundation/NSBundle.h>
#import <Foundation/NSDictionary.h>

#import "ObjectGraph.h"

@implementation ArchivedObject

- (void) dealloc
{
  TEST_RELEASE (attributes);
  TEST_RELEASE (relationships);

  TEST_RELEASE (sortedAttributeNames);
  TEST_RELEASE (sortedRelationshipNames);

  [super dealloc];
}

- initWithObjectGraph: (ObjectGraph *) parent
{
  if ((self = [self init]) != nil)
    {
      owner = parent;
    }

  return self;
}

- initWithDescription: (NSDictionary *) description
          objectGraph: (ObjectGraph *) parent
{
  if ((self = [self initWithObjectGraph: parent]) != nil)
    {
      NSMutableDictionary * attrs = [NSMutableDictionary dictionary],
                          * rels = [NSMutableDictionary dictionary];

      classUID = [[[description objectForKey: @"$class"]
        objectForKey: @"CF$UID"] intValue];

      // now to select the proper algorithm - we need a special one
      // for arrays and dictionaries:

      // a dictionary
      if ([description objectForKey: @"NS.keys"] &&
          [description objectForKey: @"NS.objects"] &&
          [description count] == 3)
        {
          NSEnumerator * e,     // for keys
                       * ee;    // for objects
          NSString * key;
          id value;

          e = [[description objectForKey: @"NS.keys"] objectEnumerator];
          ee = [[description objectForKey: @"NS.objects"] objectEnumerator];

          while ((key = [e nextObject]) != nil &&
                 (value = [ee nextObject]) != nil)
            {
              if ([value isKindOfClass: [NSDictionary class]])
                {
                  id uid = [value objectForKey: @"CF$UID"];

                  if (uid == nil)
                    {
                      NSLog (_(@"Error found in object: missing CF$UID "
                        @"key in relationship dictionary"));
                    }
                  else
                    {
                      [rels setObject: uid forKey: key];
                    }
                }
              else
                {
                  [attrs setObject: value forKey: key];
                }
              
            }
        }
      // an array
      else if ([description objectForKey: @"NS.objects"] &&
               [description count] == 2)
        {
          unsigned int i;
          NSEnumerator * e;
          id object;

          e = [[description objectForKey: @"NS.objects"] objectEnumerator];

          for (i = 0; (object = [e nextObject]) != nil; i++)
            {
              NSString * key = [NSString stringWithFormat: @"#%i", i];

              if ([object isKindOfClass: [NSDictionary class]])
                {
                  id uid = [object objectForKey: @"CF$UID"];

                  if (uid == nil)
                    {
                      NSLog (_(@"Error found in object: missing CF$UID "
                        @"key in relationship dictionary"));
                    }
                  else
                    {
                      [rels setObject: uid forKey: key];
                    }
                }
              else
                {
                  [attrs setObject: object forKey: key];
                }
            }
        }
      // all other objects
      else
        {
          NSString * key;
          NSEnumerator * e;

          e = [description keyEnumerator];

          while ((key = [e nextObject]) != nil)
            {
              if (![key isEqualToString: @"$class"])
                {
                  id value = [description objectForKey: key];

                  if ([value isKindOfClass: [NSDictionary class]])
                    {
                      id uid = [value objectForKey: @"CF$UID"];

                      if (uid == nil)
                        {
                          NSLog (_(@"Error found in object: missing CF$UID "
                            @"key in relationship dictionary"));
                        }
                      else
                        {
                          [rels setObject: uid forKey: key];
                        }
                    }
                  else
                    {
                      [attrs setObject: value forKey: key];
                    }
                }
            }
        }

      ASSIGNCOPY (attributes, attrs);
      ASSIGNCOPY (relationships, rels);
    }

  return self;
}

- (void) setupRelationships
{
  NSMutableDictionary * newAttributes = [attributes mutableCopy],
                      * newRelationships = [relationships mutableCopy];
  NSEnumerator * e;
  id key;

  e = [attributes keyEnumerator];
  while ((key = [e nextObject]) != nil)
    {
      if ([key isKindOfClass: [NSDictionary class]])
        {
          unsigned uid = [[key objectForKey: @"CF$UID"] intValue];
          id newKey;

          newKey = [owner objectForUID: uid];

          if ([newKey isKindOfClass: [NSString class]])
            {
              [newAttributes setObject: [attributes objectForKey: key]
                                forKey: newKey];
            }
          else
            {
              [newAttributes setObject: [attributes objectForKey: key]
                                forKey: [newKey description]];
            }
          [newAttributes removeObjectForKey: key];
        }
    }

  ASSIGNCOPY (attributes, newAttributes);

  e = [relationships keyEnumerator];
  while ((key = [e nextObject]) != nil)
    {
      if ([key isKindOfClass: [NSDictionary class]])
        {
          unsigned uid = [[key objectForKey: @"CF$UID"] intValue];
          id newKey;

          newKey = [owner objectForUID: uid];

          if ([newKey isKindOfClass: [NSString class]])
            {
              [newRelationships setObject: [relationships objectForKey: key]
                                   forKey: newKey];
            }
          else
            {
              [newRelationships setObject: [relationships objectForKey: key]
                                   forKey: [newKey description]];
            }
          [newRelationships removeObjectForKey: key];
        }
    }

  ASSIGNCOPY (relationships, newRelationships);
}

- (unsigned int) classUID
{
  return classUID;
}

- (void) setClassUID: (unsigned int) aUID
{
  classUID = aUID;
}

- (NSArray *) attributeNames
{
  if (sortedAttributeNames == nil)
    {
      ASSIGN (sortedAttributeNames, [[attributes allKeys]
        sortedArrayUsingSelector: @selector (caseInsensitiveCompare:)]);
    }

  return sortedAttributeNames;
}

- valueOfAttribute: (NSString *) attribute
{
  return [attributes objectForKey: attribute];
}

- (void) setAttributes: (NSDictionary *) attrs
{
  ASSIGNCOPY (attributes, attrs);
  DESTROY (sortedAttributeNames);
}

- (NSArray *) relationshipNames
{
  if (sortedRelationshipNames == nil)
    {
      ASSIGN (sortedRelationshipNames, [[relationships allKeys]
        sortedArrayUsingSelector: @selector (caseInsensitiveCompare:)]);
    }

  return sortedRelationshipNames;
}

- (unsigned int) destinationUIDOfRelationship: (NSString *) relationship
{
  return [[relationships objectForKey: relationship] intValue];
}

- (void) setRelationships: (NSDictionary *) rels
{
  ASSIGNCOPY (relationships, rels);
  DESTROY (sortedRelationshipNames);
}

@end
