/*!
  @header FTImportDefaultXMLRule
  @abstract Module of FT
 
  @availability OS X, GNUstep
  @copyright 2004, 2005, 2006 Free Software Foundation, Inc.
  
  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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA

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

  05.08.06 ola     initial version
  23.08.06 ola     license changed
  -------------------------------------------------------------------------
  </pre>
*/

#include <FTImportDefaultXMLRule.h>
#include <FTImport.h>

#define __nr_bundled_transactions 10

@implementation FTImportDefaultXMLRule

- initForImport: (FTImport *) anImport {
  self = [super init];
  
  self->import = [anImport retain];
  self->graph = nil;
  self->graphObjectToIdMapper = nil;
  self->currentNodeId = nil;
  self->currentServiceNodeId = nil;

  return self;
}


- (void) dealloc {
  [self->import release];
  [self releaseResources];
  [self->currentNodeId release];
  [self->currentServiceNodeId release];

  [super dealloc];
}


- addGraphForEvent: (ECXMLControlContext *) context {
  NSMutableDictionary *attributes;
  NSString *doCreateGraphEntry;
  BOOL doCreateGraph = NO;
  id <FTObjectToIdMapper> defaultObjectToIdMapper;
  id <FTGraphManager> graphManager;
  id <FTId> graphId;
  id <FTTransaction> transaction;

  if( nil != self->graph ) {
    return self;
  }

  if( [[FTImport log] isTraceEnabled] ) {
    [[FTImport log]
      trace: @"FTImportDefaultXMLRule::addGraph"];
  }
  
  EC_AUTORELEASEPOOL_BEGIN
  
  attributes = [[context event] attributes];
  doCreateGraphEntry = [self 
    attribute: @"createGraph" 
    withinContext: context];
 
  if( nil != doCreateGraphEntry ) {
    if( NSOrderedSame == [doCreateGraphEntry 
      caseInsensitiveCompare: @"YES"] ) {
      doCreateGraph = YES;
    }
  }
  
  defaultObjectToIdMapper = [[self->import session] defaultObjectToIdMapper];
  graphManager = [[self->import session] graphManager];
  transaction = [[self->import session] 
    beginTransactionWithParent: nil
    withSettings: nil];
    
  graphId = [defaultObjectToIdMapper mapObject: [self->import targetGraphName]];
    
  if( YES == doCreateGraph ) {
    if( [[FTImport log] isDebugEnabled] ) {
      [[FTImport log] debug: @"FTImportDefaultXMLRule::addGraphForEvent: "\
        "Creating graph %@...", [import targetGraphName]];
    }
      
    self->graph = [[graphManager createGraphWithId: graphId] retain];
    
    if( nil == self->graph ) {
      [[FTImport log] error:  @"FTImportDefaultXMLRule::addGraphForEvent:"\
        "Graph could NOT be created!"];
    } else {
      if( [[FTImport log] isDebugEnabled] ) {
        [[FTImport log] debug: @"FTImportDefaultXMLRule::addGraphForEvent: "\
          "Graph created!"];
      }
    }
  } else {
    if( [[FTImport log] isDebugEnabled] ) {
      [[FTImport log] debug: @"FTImportDefaultXMLRule::addGraphForEvent: "\
        "Loading graph %@...", [import targetGraphName]];
    }
    
    self->graph = [[graphManager graphWithId: graphId] retain];
    
    if( nil == self->graph ) {
      [[FTImport log] error:  @"FTImportDefaultXMLRule::addGraphForEvent:"\
        "Graph could NOT be loaded!"];
    } else {
      if( [[FTImport log] isDebugEnabled] ) {
        [[FTImport log] debug: @"FTImportDefaultXMLRule::addGraphForEvent: "\
          "Graph created!"];
      }
    }
  }
  
  self->graphObjectToIdMapper =
    [[self->graph objectToIdMapper] retain];

  [transaction commit];
  EC_AUTORELEASEPOOL_END

  return self;
}


- addNodeIdForEvent: (ECXMLControlContext *) context {
  NSString *nodeIdString;

  if( [[FTImport log] isTraceEnabled] ) {
    [[FTImport log]
      trace: @"FTImportDefaultXMLRule::addNodeId"];
  }

  if( nil == self->graph ) {
    return self;
  }

  nodeIdString = [self
    attribute: @"nodeId"
    withinContext: context];

  if( nil == nodeIdString ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addNodeIdForEvent: "\
        "nodeId not specified for node: %@",
        [[context event] elementName]];
    return self;
  }

  EC_AUTORELEASEPOOL_BEGIN
  id <FTTransaction> transaction;
  id <FTId> nodeId;

  if( [[FTImport log] isDebugEnabled] ) {
    [[FTImport log]
      debug: @"FTImportDefaultXMLRule::addNodeIdForEvent: "\
        "adding node with id=%@", nodeIdString];
  }
 
  transaction = [[self->import session] 
    beginTransactionWithParent: nil
    withSettings: nil];
 
  nodeId = [self->graphObjectToIdMapper
    mapObject: nodeIdString];
 
  [self->graph createNodeWithId: nodeId];

  [transaction commit];

  EC_AUTORELEASEPOOL_END
  return self;
}


- addNodeReference: (ECXMLControlContext *) context {
  NSString *targetNodeIdString;
  NSString *edgeIdString;

  if( [[FTImport log] isTraceEnabled] ) {
    [[FTImport log]
      trace: @"FTImportDefaultXMLRule::addNodeReference"];
  }

  if( nil == self->graph ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addNodeReference:"\
        "No graph open!"];
    return self;
  }

  if( nil == self->currentNodeId ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addNodeReference:"\
        "No current node given!"];
    return self;
  }

  targetNodeIdString = [self
    attribute: @"targetNodeId"
    withinContext: context];

  edgeIdString = [self
    attribute: @"edgeId"
    withinContext: context];

  if( nil == targetNodeIdString ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addNodeReference: "\
        "targetNodeId not specified for node: %@", self->currentNodeId];

    return self;
  }

  if( nil == edgeIdString ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addNodeReference: "\
        "edgeId not specified for node: %@", self->currentNodeId];

    return self;
  }

  EC_AUTORELEASEPOOL_BEGIN
  id <FTTransaction> transaction;
  id <FTId> srcNodeId;
  id <FTId> targetNodeId;
  id <FTId> edgeId;
  id <FTNode> srcNode, targetNode;

  if( [[FTImport log] isDebugEnabled] ) {
    [[FTImport log]
      debug: @"FTImportDefaultXMLRule::addNodeReference: "\
        "adding reference from=%@ to=%@ with edgeId=%@",
        self->currentNodeId, targetNodeIdString, edgeIdString];
  }
 
  transaction = [[self->import session] 
    beginTransactionWithParent: nil
    withSettings: nil];
 
  srcNodeId = [self->graphObjectToIdMapper
    mapObject: self->currentNodeId];
 
  targetNodeId = [self->graphObjectToIdMapper
    mapObject: targetNodeIdString];

  edgeId = [self->graphObjectToIdMapper
    mapObject: edgeIdString];

  srcNode = [self->graph nodeWithId: srcNodeId];
  targetNode = [self->graph nodeWithId: targetNodeId];

  if( nil == srcNode ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addNodeReference: "\
        "Source node with id=%@ could not be found!",
        self->currentNodeId];
    [transaction abort];

    EC_AUTORELEASEPOOL_RELEASE;
    return self;
  }

  if( nil == targetNode ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addNodeReference: "\
        "Target node with id=%@ could not be found!",
        targetNodeId ];
    [transaction abort];

    EC_AUTORELEASEPOOL_RELEASE;
    return self;
  }


  [srcNode 
    createAndAppendEdgeWithId: edgeId
    withTargetNode: targetNode];

  [transaction commit];

  EC_AUTORELEASEPOOL_END

  return self;
}


- addNodeReferenceList: (ECXMLControlContext *) context {
  if( [[FTImport log] isTraceEnabled] ) {
    [[FTImport log]
      trace: @"FTImportDefaultXMLRule::addNodeReferenceList"];
  }

  if( nil == self->graph ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addNodeReferenceList:"\
        " No graph opened!" ];
    return self;
  }

  if( nil != self->currentNodeId ) {
    [self->currentNodeId release];
    self->currentNodeId = nil;
  }

  self->currentNodeId = [[self
    attribute: @"ofNodeId"
    withinContext: context] retain];

  if( [[FTImport log] isDebugEnabled] ) {
    [[FTImport log]
      debug: @"FTImportDefaultXMLRule::addNodeReferenceList:"\
        "Examining references of nodeId=%@", self->currentNodeId ];
  }


  return self;
}


- addServiceData: (ECXMLControlContext *) context {
  if( [[FTImport log] isTraceEnabled] ) {
    [[FTImport log]
      trace: @"FTImportDefaultXMLRule::addServiceData"];
  }

  if( nil == self->graph ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addServiceData:"\
        " No graph opened!" ];
    return self;
  }

  if( nil != self->currentServiceNodeId ) {
    [self->currentServiceNodeId release];
    self->currentServiceNodeId = nil;
  }

  self->currentServiceNodeId = [[self
    attribute: @"nodeId"
    withinContext: context] retain];

  if( nil == self->currentServiceNodeId) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addServiceData: target node id not "\
        "given"];
  } else {
    if( [[FTImport log] isDebugEnabled] ) {
      [[FTImport log]
        debug: @"FTImportDefaultXMLRule::addServiceData:"\
          "Examining service data of nodeId=%@", self->currentServiceNodeId ];
    }
  }

  return self;
}


- addServiceDataItem: (ECXMLControlContext *) context {
  NSString *serviceName;
  NSString *fileName;

  if( [[FTImport log] isTraceEnabled] ) {
    [[FTImport log]
      trace: @"FTImportDefaultXMLRule::addServiceDataItem"];
  }

  if( nil == self->graph ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addServiceDataItem:"\
        " No graph opened!" ];

    return self;
  }

  if( nil == self->currentServiceNodeId ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addServiceDataItem: No target node id "\
        "set. Ignoring current data item!"];
    return self;
  }

  serviceName = [self 
    attribute: @"forService"
    withinContext: context];

  if( nil == serviceName ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addServiceDataItem: No service "\
        "name set. Ignoring current data item!"];

    return self;
  }

  fileName = [self 
    attribute: @"file"
    withinContext: context];

  if( nil == fileName ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addServiceDataItem: No file name "\
        "set. Ignoring current data item!"];

    return self;
  }

  EC_AUTORELEASEPOOL_BEGIN
  id <FTTransaction> transaction;
  id <FTId> targetNodeId;
  id <FTNode> targetNode;
  id <FTDictionaryService> service;
  NSString *filePath;
  NSData *fileContent;
  NSString *key;
  NSKeyedUnarchiver *unarchiver;
  id <NSCoding> content;

  if( [[FTImport log] isDebugEnabled] ) {
    [[FTImport log]
      debug: @"FTImportDefaultXMLRule::addServiceDataItem: "\
        "adding file with name=%@ to node with id=%@",
        fileName, self->currentServiceNodeId];
  }
 
  transaction = [[self->import session] 
    beginTransactionWithParent: nil
    withSettings: nil];
 
  targetNodeId = [self->graphObjectToIdMapper
    mapObject: self->currentServiceNodeId];

  targetNode = [self->graph nodeWithId: targetNodeId];

  if( nil == targetNode ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addServiceDataItem: "\
        "Target node with id=%@ could not be found!",
        targetNodeId ];
    [transaction abort];

    EC_AUTORELEASEPOOL_RELEASE;
    return self;
  }

  service = (id <FTDictionaryService>) [targetNode
    serviceWithId: serviceName];
  
  if( nil == service ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addServiceDataItem: "\
        "Unable to get service with name=%@", serviceName];
    [transaction abort];
    EC_AUTORELEASEPOOL_RELEASE;

    return self;
  }

  filePath = [[[NSString alloc]
    initWithFormat: @"%@/%@", [self->import sourceBaseDirectory],
      fileName] autorelease];

  if( ![[NSFileManager defaultManager]
    fileExistsAtPath: filePath] ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addServiceDataItem: "\
        "file does not exist: %@", filePath ];
    [transaction abort];
    EC_AUTORELEASEPOOL_RELEASE;

    return self;
  }

  fileContent = [NSData 
    dataWithContentsOfFile: filePath];

  if( nil == fileContent ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addServiceDataItem: "\
        "Unable to read content from file: %@", filePath ];
    [transaction abort];
    EC_AUTORELEASEPOOL_RELEASE;

    return self;
  }

  unarchiver = [[[NSKeyedUnarchiver alloc]
    initForReadingWithData: fileContent]
    autorelease];

  key = [unarchiver decodeObjectForKey: @"key"];
  if( nil == key ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addServiceDataItem: "\
        "Data file is corrupt. Unable to fetch KEY"];
    [unarchiver finishDecoding];
    [transaction abort];
    EC_AUTORELEASEPOOL_RELEASE;

    return self;
  }

  content = [unarchiver decodeObjectForKey: @"value"];
  if( nil == content ) {
    [[FTImport log]
      error: @"FTImportDefaultXMLRule::addServiceDataItem: "\
        "Data file is corrupt. Unable to fetch content for key=value"];
    [unarchiver finishDecoding];
    [transaction abort];
    EC_AUTORELEASEPOOL_RELEASE;

    return self;
  }

  [service 
    setObject: content
    forKey: key];

  if( [[FTImport log] isDebugEnabled] ) {
    [[FTImport log]
      debug: @"FTImportDefaultXMLRule::addServiceDataItem: "\
        "adding content for key=%@ to dictionary of node with id=%@",
        key, self->currentServiceNodeId];
  }
 
  [transaction commit];

  [unarchiver finishDecoding];
  EC_AUTORELEASEPOOL_END

  return self;
}


- (NSString *) attribute: (NSString *) attributeName
  withinContext: (ECXMLControlContext *) context {
  NSMutableDictionary *attributes;

  attributes = [[context event] attributes];
  return [attributes objectForKey: attributeName];
}


- (void) releaseResources {
  [self->currentNodeId release];
  self->currentNodeId = nil;

  [self->graphObjectToIdMapper release];
  [self->graph close];
  [self->graph release];
  self->graph = nil;
}
@end

