/*
**  Folder.m
**
**  Copyright (c) 2001, 2002, 2003
**
**  Author: Ludovic Marcotte <ludovic@Sophos.ca>
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**  
**  This library 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
**  Lesser General Public License for more details.
**  
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include <Pantomime/Folder.h>

#include <Pantomime/Constants.h>
#include <Pantomime/Container.h>
#include <Pantomime/Flags.h>
#include <Pantomime/Message.h>
#include <Pantomime/NSString+Extensions.h>
#include <Pantomime/Store.h>

#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSDebug.h>

@implementation Folder 

//
//
//
- (id) initWithName: (NSString *) theName
{
  self = [super init];

  allMessages = [[NSMutableArray alloc] init];
  allVisibleMessages = nil;

  // By default, we don't do message threading so we don't
  // initialize this ivar for no reasons
  allContainers = nil;
  allVisibleContainers = nil;
  mode = PantomimeUnknownMode;

  [self setName: theName];
  [self setShowDeleted: NO];
  [self setShowRead: YES];

  return self;
}


//
//
//
- (void) dealloc
{
  RELEASE(name);

  TEST_RELEASE(allContainers);
  RELEASE(allMessages);

  TEST_RELEASE(allVisibleContainers);
  TEST_RELEASE(allVisibleMessages);

  TEST_RELEASE(cacheManager);

  [super dealloc];
}


//
//
//
- (NSString *) name
{
  return name;
}


//
//
//
- (void) setName: (NSString *) theName
{
  RETAIN(theName);
  RELEASE(name);
  name = theName;
}


//
// This method is used to append a message to our current folder.
//
- (void) appendMessage: (Message *) theMessage
{
  if ( theMessage )
    {
      [allMessages addObject: theMessage];
      
      if ( allVisibleMessages )
	{
	  [allVisibleMessages addObject: theMessage];
	}

      // FIXME
      // If we've done message threading, we simply append the message
      // to the end of our containers array. We might want to place
      // it in the right thread in the future.
      if ( allContainers )
	{
	  Container *aContainer;

	  aContainer = [[Container alloc] init];
	  aContainer->message = theMessage;
	  [allContainers addObject: aContainer];
	  RELEASE(aContainer);

	  if ( allVisibleContainers )
	    {
	      [allVisibleContainers addObject: aContainer];
	    }
	}
    }
}


//
//
//
- (void) appendMessageFromRawSource: (NSData *) theData
                              flags: (Flags *) theFlags
{
  [self subclassResponsibility: _cmd];
}


//
//
//
- (NSArray *) allContainers
{
  return allContainers;
}


//
// This method is used to return an array containing all the messages
// of the current folder. Not that the messages MIGHT NOT been all
// completely initialized.
//
- (NSArray *) allMessages
{
  int i;
 
  if ( allVisibleMessages == nil )
    {
      allVisibleMessages = [[NSMutableArray alloc] initWithCapacity: [allMessages count]];
      
      for (i = 0; i < [allMessages count]; i++)
	{
	  Message *aMessage;
	  
	  aMessage = [allMessages objectAtIndex: i];
      
	  // We show or hide deleted messages
	  if ( [self showDeleted] )
	    {
	      [allVisibleMessages addObject: aMessage];
	    }
	  else
	    {
	      if ( [[aMessage flags] contain: DELETED] )
		{
		  // Do nothing
		  continue;
		}
	      else
		{
		  [allVisibleMessages addObject: aMessage];
		}
	    }

	  // We show or hide read messages
	  if ( [self showRead] )
	    {
	      if ( ![allVisibleMessages containsObject: aMessage] )
		{
		  [allVisibleMessages addObject: aMessage];
		}
	    }
	  else
	    {
	      if ( [[aMessage flags] contain: SEEN] )
		{
		  if ( ![[aMessage flags] contain: DELETED] )
		    {
		      [allVisibleMessages removeObject: aMessage];
		    }
		}
	      else if ( ![allVisibleMessages containsObject: aMessage] )
		{
		  [allVisibleMessages addObject: aMessage];
		}
	    }
	}
    }

  return allVisibleMessages;
}


//
//
//
- (void) setMessages: (NSArray *) theMessages
{
  if ( theMessages )
    {
      RELEASE(allMessages);
      allMessages = [[NSMutableArray alloc] initWithArray: theMessages];

      if ( allContainers )
	{
	  [self thread];
	}
    }
  else
    {
      DESTROY(allMessages);
    }

  TEST_RELEASE(allVisibleMessages);
  allVisibleMessages = nil;
}


//
// This method is used to obtain a message using an index of the
// message in the folder.
//
// This method might return nil if it doesn't make sense to obtain
// a message by using an index in a context of a Folder.
//
- (Message *) messageAtIndex: (int) theIndex
{
  if (theIndex < 0 || theIndex >= [self count])
    {
      return nil;
    }
  
  return [[self allMessages] objectAtIndex: theIndex];
}


//
// This method is used to return the number of message present in this
// folder.
//
- (int) count
{
  return [[self allMessages] count];
}


//
// This method is used to close the Folder. The subclasses of Folder MUST 
// implement this method.
//
- (void) close
{
  [self subclassResponsibility: _cmd];
  return;
}


//
// This method is used to remove permanently all the messages that have been marked as
// deleted in this folder. ALL THE RETURNED MESSAGES ARE IN RAW SOURCE.
//
- (NSArray *) expunge: (BOOL) returnDeletedMessages
{
  [self subclassResponsibility: _cmd];
  return nil;
}


//
//
//
- (Store *) store
{
  return store;
}


//
// No need to retain the store here since our store object
// retains our folder object.
//
- (void) setStore: (Store *) theStore
{
  store = theStore;
}


//
// This method removes permenantly a message from
// this folder.
//
// This method is used when transferring message between folders
// in order to update the view or when expunge delete messages
// from a view.
//
- (void) removeMessage: (Message *) theMessage
{
  if ( theMessage )
    {
      [allMessages removeObject: theMessage];
      
      if ( allVisibleMessages )
	{
	  [allVisibleMessages removeObject: theMessage];
	}

      // FIXME - We must go through our allContainers ivar in order
      //         to find the message that has just been removed from
      //         this folder. We must go through all levels.
      //         Right now, we simply do again our message threading algo
      if ( allContainers )
	{
	  [self thread];
	}
    }
}


//
//
//
- (BOOL) showDeleted
{
  return showDeleted;
}


//
//
//
- (void) setShowDeleted: (BOOL) theBOOL
{
  if (theBOOL != showDeleted)
    {
      // FIXME
      int i;
      showDeleted = theBOOL;

      DESTROY(allVisibleMessages);
      DESTROY(allVisibleContainers);
      
      for (i = 0; i < [allContainers count]; i++)
	{
	  Container *aContainer, *c;

	  aContainer = [allContainers objectAtIndex: i];
	  
	  // We iterate in its children
	  c = aContainer->child;

	  while ( c )
	    {
	      if ( [[c->message flags] contain: DELETED] )
		{
		  c->visible = theBOOL;
		}
	      
	      c = c->next;
	    }
	}
    }
}


//
//
//
- (BOOL) showRead
{
  return showRead;
}


//
//
//
- (void) setShowRead: (BOOL) theBOOL
{
  if (theBOOL != showRead)
    {
      showRead= theBOOL;

      DESTROY(allVisibleMessages);
      DESTROY(allVisibleContainers);
    }
}


//
//
//
- (int) numberOfDeletedMessages
{
  int i, count;
  
  count = 0;

  for (i = 0; i < [allMessages count]; i++)
    {
      if ( [[[allMessages objectAtIndex: i] flags] contain: DELETED] )
	{
	  count++;
	}
    }

  return count;
}


//
//
//
- (int) numberOfUnreadMessages
{
  int i, count;
  
  count = 0;
  
  for (i = 0; i < [allMessages count]; i++)
    {
      if ( ![[[allMessages objectAtIndex: i] flags] contain: SEEN] )
	{
	  count++;
	}
    }

  return count;
}


//
//
//
- (long) size;
{
  long size;
  int i;

  size = 0;
  
  for (i = 0; i < [allMessages count]; i++)
    {
      size += [(Message *)[allMessages objectAtIndex: i] size];
    }

  return size;
  
}


//
// This method is used to update our cache (allVisibleMessages).
// Applications can call this method if they set the DELETED flags to
// messages inside this folder. If not called, the cache won't be updated
// the messages having the flag DELETED will still be visible.
//
- (void) updateCache
{
  TEST_RELEASE(allVisibleMessages);
  allVisibleMessages = nil;
}


//
// This method implements Jamie Zawinski's message threading algorithm.
// The full algorithm is available here:
//
// http://www.jwz.org/doc/threading.html
//
// FIXME: Use NSMap for id_table and subject_table
//
- (void) thread
{
  NSMutableDictionary *id_table, *subject_table;
  NSAutoreleasePool *pool;
  int i;

  // We clean up ...
  TEST_RELEASE(allContainers);

  // We create our local autorelease pool
  pool = [[NSAutoreleasePool alloc] init];


  NSLog(@"Beginning of message threading...");

  // Build id_table and our containers mutable array
  id_table = [[NSMutableDictionary alloc] init];
  allContainers = [[NSMutableArray alloc] initWithArray: [id_table allValues]];

  //
  // 1. A., B. and C.
  //
  for (i = 0; i < [allMessages count]; i++)
    {
      Container *aContainer;
      Message *aMessage;
      
      NSString *aReference;
      int j;

      // So that gcc shutup
      aMessage = nil;
      aReference = nil;

      NSDebugLog(@"--------------");
      aMessage = [allMessages objectAtIndex: i];
      
      // We skip messages that don't have a valid Message-ID
      if ( ![aMessage messageID] )
      	{
	  aContainer = [[Container alloc] init];
	  aContainer->message = aMessage;
	  NSDebugLog(@"No message ID for |%@|", [aMessage subject]);
	  [allContainers addObject: aContainer];
	  RELEASE(aContainer);
      	  continue;
      	}
      
      NSDebugLog(@"Message ID = |%@|", [aMessage messageID]);

      //
      // A.
      //
      aContainer = [id_table objectForKey: [aMessage messageID]];
      
      if ( aContainer )
	{
	  //aContainer->message = aMessage;
	  
	  if ( aContainer->message != aMessage )
	    {
	      aContainer = [[Container alloc] init];
	      aContainer->message = aMessage;
	      [id_table setObject: aContainer
		    forKey: [aMessage messageID]];
	      DESTROY(aContainer);
	    }
	}
      else
	{
	  aContainer = [[Container alloc] init];
	  aContainer->message = aMessage;
	  [id_table setObject: aContainer
		    forKey: [aMessage messageID]];
	  DESTROY(aContainer);
	}
      
      //
      // B. For each element in the message's References field:
      //
      for (j = 0; j < [[aMessage allReferences] count]; j++)
	{
	  // We get a Message-ID
	  aReference = [[aMessage allReferences] objectAtIndex: j];

	  // Find a container object for the given Message-ID
	  NSDebugLog(@"aReference = |%@|", aReference);
	  aContainer = [id_table objectForKey: aReference];
	  
	  if ( aContainer )
	    {
	      // We found it. We use that.
	      NSDebugLog(@"B. We found a container object.");
	    }
	  // Otherwise, make (and index) one (new Container) with a null Message
	  else 
	    {
	      NSDebugLog(@"B. We have NOT found a container object.");
	      aContainer = [[Container alloc] init];
	      [id_table setObject: aContainer
			forKey: aReference];
	      RELEASE(aContainer);
	    }
	  
	  // NOTE:
	  // aContainer is valid here. It points to the message (could be a nil message)
	  // that has a Message-ID equals to the current aReference value.
	  
	  // If we are currently using the last References's entry of our list,
	  // we simply break the loop since we are gonna set it in C.
	  //if ( j == ([[aMessage allReferences] count] - 1) )
	  //  {
	  //    break;
	  // }

	  // Link the References field's Containers together in the order implied by the References header.
	  // The last references
	  if ( j == ([[aMessage allReferences] count] - 1) &&
	       aContainer->parent == nil )
	    {
	      // We grab the container of our current message
	      NSDebugLog(@"We set the parent of |%@| to: |%@|", [aMessage messageID], aReference);
	      [((Container *)[id_table objectForKey: [aMessage messageID]]) setParent: aContainer];
	    }
	  
	  // We set the child
	  //if ( aContainer->message != aMessage &&
	  //     aContainer->child == nil )
	  //  {
	  //    NSDebugLog(@"We set the child of |%@| to: |%@|", aReference, [aMessage messageID]);
	  //    [aContainer setChild: [id_table objectForKey: [aMessage messageID]]];
	  //  }	      

	} // for (j = 0; ...
      
      // NOTE: The loop is over here. It was an ascending loop so
      //       aReference points to the LAST reference in our References list

      //
      // C. Set the parent of this message to be the last element in References. 
      //
      // NOTE: Again, aReference points to the last Message-ID in the References list
      NSDebugLog(@"C. We set the parent of this message to |%@|", aReference);
      
      // We get the container for the CURRENT message
      aContainer =  (Container *)[id_table objectForKey: [aMessage messageID]];
      
      // If we have no References and no In-Reply-To fields, we simply set a
      // the parent to nil since it can be the message that started the thread.
      if ( [[aMessage allReferences] count] == 0 &&
	   [aMessage headerValueForName: @"In-Reply-To"] == nil )
	{
	  [aContainer setParent: nil];
	}
      // If we have no References but an In-Reply-To field, that becomes our parent.
      else if ( [[aMessage allReferences] count] == 0 &&
		[aMessage headerValueForName: @"In-Reply-To"] )
	{
	  NSDebugLog(@"Setting the parent from In-Reply-To");
	  [aContainer setParent: (Container *)[id_table objectForKey: [aMessage headerValueForName: @"In-Reply-To"]]];
	  // FIXME, should we really do that? or should we do it in B?
	  [(Container *)[id_table objectForKey: [aMessage headerValueForName: @"In-Reply-To"]] setChild: aContainer];
	}
      else
	{
	  //NSDebugLog(@"aReference in step C. = |%@|", aReference); 
	  [aContainer setParent: (Container *)[id_table objectForKey: aReference]];
	  [(Container *)[id_table objectForKey: aReference] setChild: aContainer];
	}
      
      NSDebugLog(@"---------------");
    } // for (i = 0; ...

  //
  // 2. Find the root set.
  //
  NSDebugLog(@"Finding the root set.");
  [allContainers addObjectsFromArray: [id_table allValues]];

  //while (NO)
  for (i = ([allContainers count] - 1); i >= 0; i--)
    {
      Container *aContainer;
      
      aContainer = [allContainers objectAtIndex: i];
      
      if ( aContainer->parent != nil )
	{
	  NSDebugLog(@"|%@| has a parent.", [aContainer->message messageID]);
	  [allContainers removeObjectAtIndex: i];
	}
      else
	{
	  NSDebugLog(@"Keeping |%@| in the root set", [aContainer->message messageID]);
	}
    }

  //
  // 3. Discard id_table.
  //
  DESTROY(id_table);

  
  //
  // 4. Prune empty containers.
  //
  //while (NO)
  NSDebugLog(@"Pruning empty containers...");
  for (i = ([allContainers count] - 1); i >= 0; i--)
    {
      Container *aContainer;

      NSDebugLog(@"Getting container at index = %d", i);
      aContainer = [allContainers objectAtIndex: i];
      
      // Recursively walk all containers under the root set.
      while ( aContainer )
	{
	  NSDebugLog(@"Recursively walking in children...");
	  
	  NSDebugLog(@"Current container = |%@| |%@| = %d", [aContainer->message messageID],
		[aContainer->message subject], (aContainer->message ? YES : NO));

	  // A. If it is an empty container with no children, nuke it
	  if ( aContainer->message == nil &&
	       aContainer->child == nil )
	    {
	      // We nuke it
	      // FIXME: Won't work for non-root containers.
	      [allContainers removeObject: aContainer];
	    }
	  
	  // B. If the Container has no Message, but does have children, remove this container but 
	  //    promote its children to this level (that is, splice them in to the current child list.)
	  //    Do not promote the children if doing so would promote them to the root set 
	  //    -- unless there is only one child, in which case, do. 
	  // FIXME: We promote to the root no matter what :)
	  if ( aContainer->message == nil &&
	       aContainer->child )
	    
	    {
	      Container *c;
	      
	      NSDebugLog(@"Promoting...");

	      c = aContainer;
	      RETAIN(c);
	      NSDebugLog(@"Removing the parent of |%@|", [c->child->message subject]);
	      [c->child setParent: nil];
	      NSDebugLog(@"Done.");
	      [allContainers removeObject: c];
	      [allContainers addObject: c->child]; // We promote the the root for now
	      NSDebugLog(@"Promoted |%@|", [c->child->message subject]);
	      
	      // We go to our child and we continue to loop
	      //aContainer = aContainer->child;
	      aContainer = [aContainer childAtIndex: ([aContainer count]-1)];
	      RELEASE(c);
	      continue;
	    }
	  
	  //aContainer = aContainer->child;
	  aContainer = [aContainer childAtIndex: ([aContainer count]-1)];
	}

      NSDebugLog(@"Done with %d", i);
    }
  
  //
  // 5. Group root set by subject.
  //
  // A. Construct a new hash table, subject_table, which associates subject 
  //    strings with Container objects.
  subject_table = [[NSMutableDictionary alloc] init];
  
  //
  // B. For each Container in the root set:
  //

  NSDebugLog(@"threading by subject B.");
  
  //while (NO)
  for (i = 0; i < [allContainers count]; i++)
    {
      Container *aContainer;
      Message *aMessage;
      NSString *aString;

      aContainer = [allContainers objectAtIndex: i];
      aMessage = aContainer->message;
      aString = [aMessage subject];
      
      if ( aString )
	{
	  aString = [aMessage baseSubject];

	  NSDebugLog(@"stripped subject |%@|", aString);
	  
	  // If the subject is now "", give up on this Container.
	  if ( [aString length] == 0 )
	    {
	      //aContainer = aContainer->child;
	      continue;
	    }
	  
	  // We set the new subject
	  //[aMessage setSubject: aString];
	  
	  // Add this Container to the subject_table if:
	  // o There is no container in the table with this subject, or
	  // o This one is an empty container and the old one is not: 
	  //   the empty one is more interesting as a root, so put it in the table instead.
	  // o The container in the table has a ``Re:'' version of this subject, 
	  //   and this container has a non-``Re:'' version of this subject. 
	  //   The non-re version is the more interesting of the two.
	  if ( ![subject_table objectForKey: aString] )
	    {
	      NSDebugLog(@"Subject table has no container for |%@|, adding.", aString);
	      [subject_table setObject: aContainer
			     forKey: aString];
	    }
	  else
	    {
	      NSString *aSubject;
	      
	      // We obtain the subject of the message of our container.
	      aSubject = [((Container *)[subject_table objectForKey: aString])->message subject];
	      
	      if ( [aSubject hasREPrefix] &&
		   ![[aMessage subject] hasREPrefix] )
		{
		  // We replace the container
		  NSDebugLog(@"Replacing container...");
		  [subject_table removeObjectForKey: aString];
		  [subject_table setObject: aContainer
				 forKey: [aMessage subject]];
		}
	      else
		{
		  NSDebugLog(@"We are NOT replacing: |%@|", aSubject);
		}
	    }
	  
	} // if ( aString )
    }
  NSDebugLog(@"threading by subject B. Done.");
  
  //
  // C. Now the subject_table is populated with one entry for each subject which occurs in 
  //    the root set. Now iterate over the root set, and gather together the difference.
  //
  NSDebugLog(@"threading by subject C.");
  //while (NO)
  for (i = ([allContainers count]-1); i >= 0; i--)
    {
      Container *aContainer, *containerFromTable;
      NSString *aSubject, *aString;

      aContainer = [allContainers objectAtIndex: i];
      
      // Find the subject of this Container (as above.)
      aSubject = [aContainer->message subject];
      aString = [aContainer->message baseSubject];
      
      NSDebugLog(@"Current subject = |%@|, aString = |%@|", aSubject, aString);
      
      // Look up the Container of that subject in the table.
      // If it is null, or if it is this container, continue.
      containerFromTable = [subject_table objectForKey: aString];
      if ( !containerFromTable || containerFromTable == aContainer ) 
	{
	  NSDebugLog(@"null container or it is this container.");
	  continue; 
	}
      
      // If that container is a non-empty, and that message's subject does 
      // not begin with ``Re:'', but this message's subject does, then make this be a child of the other.
      if ( ![[containerFromTable->message subject] hasREPrefix] &&
	   [aSubject hasREPrefix] )
	{
	  NSDebugLog(@"Making child... of |%@|", [containerFromTable->message subject]);
	  [aContainer setParent: containerFromTable];
	  [containerFromTable setChild: aContainer]; 
	  [allContainers removeObject: aContainer];
	}
      // If that container is a non-empty, and that message's subject begins with ``Re:'', 
      // but this  message's subject does not, then make that be a child of this one -- 
      // they were misordered. (This happens somewhat implicitly, since if there are two
      // messages, one with Re: and one without, the one without will be in the hash table,
      // regardless of the order in which they were seen.)
      else if ( [[containerFromTable->message subject] hasREPrefix] &&
		![aSubject hasREPrefix] )
	{
	  NSDebugLog(@"Making child... B");
	  [containerFromTable setParent: aContainer];
	  [aContainer setChild: containerFromTable]; 
	  [allContainers removeObject: containerFromTable];
	}
      // Otherwise, make a new empty container and make both msgs be a child of it. 
      // This catches the both-are-replies and neither-are-replies cases, and makes them 
      // be siblings instead of asserting a hierarchical relationship which might not be true.
      else
	{
#if 0
	  // FIXME - not so sure about that step.
	  Container *aNewContainer;
	  
	  //NSDebugLog(@"Creating a new container...");
	  
	  aNewContainer = [[Container alloc] init];
	  
	  [aContainer setParent: aNewContainer];
	  [containerFromTable setParent: aNewContainer];
	  
	  [aNewContainer setChild: aContainer]; 
	  [aNewContainer setChild: containerFromTable];
	  [allContainers addObject: aNewContainer];
	  RELEASE(aNewContainer);
	  
	  // We remove ..
	  [allContainers removeObject: aContainer];
	  [allContainers removeObject: containerFromTable];
#endif
	}
    }
  NSDebugLog(@"threading by subject C. Done.");
  
  RELEASE(subject_table);

  //
  // 6.  Now you're done threading!
  //
  //     Specifically, you no longer need the ``parent'' slot of the Container object, 
  //     so if you wanted to flush the data out into a smaller, longer-lived structure, you 
  //     could reclaim some storage as a result. 
  //
  // GNUMail.app DOES USE the parent slot so we keep it.

  //
  // 7.  Now, sort the siblings.
  //     
  //     At this point, the parent-child relationships are set. However, the sibling ordering 
  //     has not been adjusted, so now is the time to walk the tree one last time and order the siblings 
  //     by date, sender, subject, or whatever. This step could also be merged in to the end of step 4, 
  //     above, but it's probably clearer to make it be a final pass. If you were careful, you could 
  //     also sort the messages first and take care in the above algorithm to not perturb the ordering,
  //      but that doesn't really save anything. 
  //
  // By default we at least sort everything by number.
  [allContainers sortUsingSelector: @selector(compareAccordingToNumber:)];

  NSLog(@"End of message threading.");

  RELEASE(pool);
}


//
//
//
- (NSArray *) search: (NSString *) theString
                mask: (int) theMask
             options: (int) theOptions
{
  [self subclassResponsibility: _cmd];
  return nil;
}


//
//
//
- (id) cacheManager
{
  return cacheManager;
}

- (void) setCacheManager: (id) theCacheManager
{
  if ( theCacheManager )
    {
      RETAIN(theCacheManager);
      RELEASE(cacheManager);
      cacheManager = theCacheManager;
    }
  else
    {
      DESTROY(cacheManager);
    }
}


//
//
//
- (int) mode
{
  [self subclassResponsibility: _cmd];
  return PantomimeUnknownMode;
}


//
//
//
- (void) setFlags: (Flags *) theFlags
         messages: (NSArray *) theMessages
{
  int i;

  for (i = 0; i < [theMessages count]; i++)
    {
      [[theMessages objectAtIndex: i] setFlags: theFlags];
    }
}

@end



