/*!
  @header FTFileStoreTEST
  @abstract Module of FT
 
  @availability OS X, GNUstep
  @copyright 2004, 2005 Free Software Foundation, Inc.
  
  Author: Oliver Langer

  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  <pre>
  -------------------------------------------------------------------------
  Modification history

  13.11.05 ola     initial version
  -------------------------------------------------------------------------
  </pre>
*/

#include <FTFileStoreTEST.h>
#include <FT/FTDictionaryService.h>


@implementation FTFileStoreTEST
- (NSString *) createPathOfFilename: (NSString *) aFilename {
  NSArray *pathComponents = [aFilename pathComponents];
  NSMutableString *toReturn = nil;
  int slashCounter = 0;
  int i;
  
  toReturn = [[NSMutableString alloc] init];
  for( i = 0; i < [pathComponents count]-1; i++ ) {
    if( slashCounter > 1 ) {
      [toReturn appendString: @"/"];
    } else {
      slashCounter++;
    }
    
    [toReturn appendString: [pathComponents objectAtIndex: i]];
  }
  
  return toReturn;
}


- (NSString *) popPathStack: (NSMutableArray *) aPathStack 
  untilFoundPath: (NSString *) aPath {
  unsigned int index;
  NSString *topElement = nil;
  
  index = [aPathStack indexOfObject: aPath];
  
  if( NSNotFound == index ) {
    NSLog( @"Path \"%@\" not found in stack of paths!",
      aPath );
    
    topElement = nil;
  } else {
    // now pop all elements at a higher index than "index":
    int count = [aPathStack count] - index - 1;    
    
    for( ; count > 0; count-- ) {
      [aPathStack removeObjectAtIndex: index+1];
    }
    
    topElement = [aPathStack objectAtIndex: [aPathStack count]-1];
  }

  return topElement;
}


- iterateBeginingAtPath: (NSString *) aPath 
  excludeSubDirectories: (NSSet *) excludes
    sendingEventsTo: (id <FTDirectoryStructureEvents>) eventHandler {
  EC_AUTORELEASEPOOL_BEGIN
      NSFileManager *fileManager = [NSFileManager defaultManager];
      NSEnumerator *directoryContent
        = [fileManager enumeratorAtPath: aPath];
      NSString *filename;
      NSMutableArray *directoryStack 
        = [[[NSMutableArray alloc] init] autorelease];
      NSString *currentDirectory = aPath;
      
      NSLog( @"FTFileStoreTEST::iterate: Beginning with path: %@", aPath );
      
      [directoryStack addObject: currentDirectory];
      [eventHandler addDirectory: currentDirectory 
        ofParentDirectory: nil];
        
      while( (filename = [directoryContent nextObject]) ) {
        NSString *absoluteFileName = [[NSString alloc]
          initWithFormat: @"%@/%@", aPath, filename];
        BOOL ignoreFile = NO;
        
        if( nil != excludes ) {
          NSScanner *scanner = nil;
          NSEnumerator *excludeDirs;
          NSString *excludeDir;
          
          excludeDirs = [excludes objectEnumerator];
          
          while( (excludeDir = [excludeDirs nextObject]) ) { 
            scanner = [[NSScanner alloc] initWithString: filename];
            ignoreFile = [scanner scanString: excludeDir intoString: NULL];
            
            [scanner release];
            
            if( ignoreFile ) {
              break;
            }
          }
        }
        
        if( ignoreFile ) {
          NSLog( @"FTFileStoreTEST::iterate: Ignoring file: %@", absoluteFileName );
        } else {
          NSLog( @"FTFileStoreTEST::iterate: Next file is: %@", absoluteFileName );
          BOOL isADirectory;
          /* if it is a directory, then push it on the stack.
           * if is is a file then check to which directory it belongs
           */
          [fileManager fileExistsAtPath: absoluteFileName isDirectory: &isADirectory];
          if( isADirectory ) {
            // a directory, so push it:
            NSLog( @"FTFileStoreTEST::iterate: %@  is a directory", absoluteFileName );
            [directoryStack addObject: absoluteFileName];
            
            [eventHandler addDirectory: absoluteFileName 
              ofParentDirectory: currentDirectory];
              
            currentDirectory = absoluteFileName;
          } else {
            NSString *pathOfCurrentFile;
            
            NSLog( @"FTFileStoreTEST::iterate: %@  is a file", 
              absoluteFileName );
              
            pathOfCurrentFile = [self createPathOfFilename: absoluteFileName];
            
            if( ![pathOfCurrentFile isEqual: currentDirectory] ) {
              currentDirectory = [self popPathStack: directoryStack
                untilFoundPath: pathOfCurrentFile];
            }
            
            [eventHandler addFile: absoluteFileName 
              ofParentDirectory: currentDirectory];
              
            [pathOfCurrentFile release];
          }
        }
        
        [absoluteFileName release];
      }
  EC_AUTORELEASEPOOL_END
  
  return self;
}


- setObject: (id <NSCoding>) anObject forKey: (id <NSCoding>) aKey
  forNode: (id <FTNode>) aNode {
  id <FTDictionaryService> dictionaryService;

  NSLog( @"KEY=%@", aKey);
  dictionaryService = (id <FTDictionaryService>) 
    [aNode serviceWithId: @"FTDictionaryService"];
  ECAssertTrue( nil != dictionaryService, @"Unable to get service "\
    "FTDictionaryService!" );
    
  [dictionaryService setObject: anObject forKey: aKey ];
  
  [dictionaryService close];
  return self;
}


- objectForKey: (id <NSCoding>) aKey forNode: (id <FTNode>) aNode {
  id <FTDictionaryService> dictionaryService;
  id toReturn = nil;
 
   dictionaryService = (id <FTDictionaryService>) 
    [aNode serviceWithId: @"FTDictionaryService"];
  ECAssertTrue( nil != dictionaryService, @"Unable to get service "\
    "FTDictionaryService!" );
    
  NSLog( @"KEY=%@", aKey);
  toReturn = [dictionaryService objectForKey: aKey ];
  
  [dictionaryService close];

  return toReturn;
}


- testFileStore {
  id <FTGraphManager> graphManager;
  id <FTGraph> graph;
  id <FTId> graphId;
  id <FTObjectToIdMapper> objectToIdMapper;
  id <FTTransaction> transaction;
  id <FTObjectToIdMapper> graphIdMapper;
  NSDictionary *storedNamesToContent;
  
  NSMutableSet *excludeDirs;
  
  NSLog( @"FTFileStoreTEST::testServices: BEGINNING..." );
  
  EC_AUTORELEASEPOOL_BEGIN
  objectToIdMapper = [session defaultObjectToIdMapper];
  graphManager = [session graphManager];

  NSLog( @"Now creating graph..." );
  transaction = [session beginTransactionWithParent: nil
    withSettings: nil];
  
  graphId = [objectToIdMapper mapObject: @"fileStoreGraph"];
  graph = [graphManager createGraphWithId: graphId];
  ECAssertTrue( nil != graph, @"Graph creation failed!" );
  
  graphIdMapper = [graph objectToIdMapper];

  excludeDirs = [[[NSMutableSet alloc] init] autorelease];
  [excludeDirs addObject: @"testdir"];

  [self iterateBeginingAtPath:
    [[NSFileManager defaultManager] currentDirectoryPath]
    //[[[NSProcessInfo processInfo] environment] objectForKey: @"PWD"]
    excludeSubDirectories: excludeDirs
    sendingEventsTo: [[[FTStoreDirectoryStructure alloc] 
      initWithGraph: graph
      withObjectIdMapper: graphIdMapper] autorelease]];

  [transaction commit]; 

  //NSLog( @"FTFileStoreTEST::testServices: Reading all nodes and  
  transaction = [session beginTransactionWithParent: nil
    withSettings: nil];
  storedNamesToContent = [self readGraph: graph
    withObjectToIdMapper: graphIdMapper];


  [self iterateBeginingAtPath:
    [[NSFileManager defaultManager] currentDirectoryPath]
    //[[[NSProcessInfo processInfo] environment] objectForKey: @"PWD"]
    excludeSubDirectories: excludeDirs
    sendingEventsTo: [[[FTCompareDirectoryStructure alloc] 
      initWithNamesToContentMapping: storedNamesToContent] autorelease]];

  NSLog( @"FTFileStoreTEST::testServices: Closing FTDictionaryService..." );
  
  [transaction commit];
  [graph close];
  
  EC_AUTORELEASEPOOL_END
  NSLog( @"FTFileStoreTEST::testServices: FINISHED!" );
  return self;
}


- (NSDictionary *) readGraph: (id <FTGraph>) graph 
  withObjectToIdMapper: (id <FTObjectToIdMapper>) objectToIdMapper {
  ECStack *dirNodes;
  id <FTNode> currentNode;
  id <FTId> currentId;
  NSMutableDictionary *nameToContent;
  id <FTDictionaryService> dictionaryService;
  id <ECIterator> outgoingNodes;
  
  EC_AUTORELEASEPOOL_BEGIN
  
  nameToContent = [[NSMutableDictionary alloc] init];
  dirNodes = [[[ECStack alloc] init] autorelease];
    
  // read root node and begin iteration with it:
  currentId = [objectToIdMapper mapObject: @"ROOTDIR"];
  currentNode = [graph nodeWithId: currentId];
  
  NSAssert( nil != currentNode, @"Unable to find node with id=ROOTDIR!" );
  [dirNodes pushObject: currentNode];
  
  while( ![dirNodes isEmpty] ) {
    NSString *dirName;
    currentNode = [dirNodes popObject];
    
    dictionaryService = (id <FTDictionaryService>) 
      [currentNode serviceWithId: @"FTDictionaryService"];
    
    dirName = [dictionaryService objectForKey: @"NAME" ];
    [nameToContent setObject: @"DIR" forKey: dirName];
    
    NSLog( @"FTFileStoreTEST::readGraph: Got directory: %@", dirName );
    [dictionaryService close];
  
    outgoingNodes = [currentNode outgoingNodes];
    while( [outgoingNodes hasNext] ) {
      NSString *filename;
      NSString *filetype;
      currentNode = [outgoingNodes next];
      
      dictionaryService = (id <FTDictionaryService>) 
        [currentNode serviceWithId: @"FTDictionaryService"];
      
      filename = [dictionaryService objectForKey: @"NAME"];
      filetype = [dictionaryService objectForKey: @"ContentType"];
      
      if( [filetype isEqual: @"DIR"] ) {
        [dirNodes pushObject:  currentNode];
      } else {
        NSData *fileContent;
        
        NSLog( @"FTFileStoreTEST::readGraph: Got file \"%@\" within directory "\
          "\"%@\"", filename, dirName );
        
        fileContent = [dictionaryService objectForKey: @"ContentOfFile"];
        [nameToContent setObject: [[[NSNumber alloc] 
          initWithUnsignedInt: [fileContent hash]] autorelease]
          forKey: filename];
      }
      
      [dictionaryService close];
    }    
  }
  
  EC_AUTORELEASEPOOL_END
  
  return nameToContent;
}
@end


@implementation FTStoreDirectoryStructure
- initWithGraph: (id <FTGraph>) aGraph 
  withObjectIdMapper: (id <FTObjectToIdMapper>) anObjectToIdMapper {
  self = [super init];

  self->graph = [aGraph retain];
  self->objectToIdMapper = [anObjectToIdMapper retain];
  
  self->directoryToNode = [[NSMutableDictionary alloc] init];
  return self;
}

- (void) dealloc {
  [self->graph release];
  [self->objectToIdMapper release];
  [self->directoryToNode release];
  
  [super dealloc];
}


- addDirectory: (NSString *) directory ofParentDirectory: (NSString *) parent {
  id <FTId> nodeId;
  id <FTNode> node;
  id <FTNode> parentNode;
  id <FTDictionaryService> dictionaryService;
  
  EC_AUTORELEASEPOOL_BEGIN
  
  NSLog( @"FTStoreDirectoryStructure: Adding directory \"%@\" to parent "\
    "directory \"%@\"", directory, parent );
  
  nodeId = [self->objectToIdMapper mapObject: directory];
  node = [self->graph createNodeWithId: nodeId];
  
  [self->directoryToNode setObject: node forKey: directory];
  
  if( nil == parent ) {
    id <FTId> rootId = [self->objectToIdMapper 
      mapObject: @"ROOTDIR"];
      
    parentNode = [self->graph createNodeWithId: rootId];
    dictionaryService = (id <FTDictionaryService>) 
      [parentNode serviceWithId: @"FTDictionaryService"];
    
    [dictionaryService setObject: @"DIR" forKey: @"ContentType" ];
    [dictionaryService setObject: directory forKey: @"NAME" ];
  
    [dictionaryService close];
  } else {
    parentNode = [self->directoryToNode objectForKey: parent];
  }
  
  if( nil != parentNode ) {
    [parentNode createAndAppendEdgeWithId: nodeId withTargetNode: node];
  }
  
  dictionaryService = (id <FTDictionaryService>) 
    [node serviceWithId: @"FTDictionaryService"];
    
  [dictionaryService setObject: @"DIR" forKey: @"ContentType" ];
  [dictionaryService setObject: directory forKey: @"NAME" ];
  
  [dictionaryService close];
  
  EC_AUTORELEASEPOOL_END
  return self;
}


- addFile: (NSString *) aFile ofParentDirectory: (NSString *) parent {
  id <FTNode> parentNode;
  id <FTNode> fileNode;
  id <FTId> nodeId;
  id <FTDictionaryService> dictionaryService;
  NSData *fileContent;
  
  EC_AUTORELEASEPOOL_BEGIN
  
  NSLog( @"FTStoreDirectoryStructure: Adding file \"%@\" to parent "\
    "directory \"%@\"", aFile, parent );

  parentNode = [self->directoryToNode objectForKey: parent];
  if( nil == parentNode ) {
    NSLog( @"FTStoreDirectoryStructure::addFile: Unable to find parent node: "\
      "\"%d\"", parentNode );
    [NSException raise: @"IllegalStateException" 
      format: @"Unable to find parent node: \"%d\"", parentNode];
  }
  
  nodeId = [self->objectToIdMapper mapObject: aFile];
  fileNode = [self->graph createNodeWithId: nodeId];
  
  [parentNode createAndAppendEdgeWithId: nodeId withTargetNode: fileNode];
  
  NSLog( @"FTStoreDirectoryStructure: Now storing content..." );
  dictionaryService = (id <FTDictionaryService>) 
    [fileNode serviceWithId: @"FTDictionaryService"];
    
  fileContent = [[[NSData alloc] initWithContentsOfFile: aFile]
    autorelease];
    
  [dictionaryService setObject: fileContent forKey: @"ContentOfFile" ];
  [dictionaryService setObject: @"FILE" forKey: @"ContentType" ];
  [dictionaryService setObject: aFile forKey: @"NAME" ];
  
  [dictionaryService close];
  
  EC_AUTORELEASEPOOL_END
  return self;
}
@end

@implementation FTCompareDirectoryStructure
- initWithNamesToContentMapping: (NSDictionary*) someNamesToContent {
  self = [super init];

  self->namesToContent = [someNamesToContent retain];
  return self;
}

- (void) dealloc {
  [self->namesToContent release];
  
  [super dealloc];
}


- addDirectory: (NSString *) directory ofParentDirectory: (NSString *) parent {
  EC_AUTORELEASEPOOL_BEGIN
  if( nil == [self->namesToContent objectForKey: directory] ) {
    [NSException raise: @"IllegalStateException" 
      format: @"Unable to find directory \"%d\" in database.", parent];
  }
  
  NSLog( @"FTCompareDirectoryStructure: Got directory \"%@\" of parent "\
    "directory \"%@\"", directory, parent );
  
  EC_AUTORELEASEPOOL_END
  return self;
}


- addFile: (NSString *) aFile ofParentDirectory: (NSString *) parent {
  if( nil == [self->namesToContent objectForKey: aFile] ) {
    [NSException raise: @"IllegalStateException" 
      format: @"Unable to find file \"%d\" in database.", aFile];
  }
  
  NSLog( @"FTCompareDirectoryStructure: Found file \"%@\" to parent "\
    "directory \"%@\"", aFile, parent );

  return self;
}

@end

