/*!
  @header ECXMLControl
  @abstract Module of Encore
 
  @availability OS X, GNUstep
  @copyright 2004, 2005, 2006 Oliver Langer

  Author: Oliver Langer

  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

  27.11.05 ola     initial version
  22.08.06 ola     license changed
  -------------------------------------------------------------------------
  </pre>
*/

#include <Encore/ECMemory.h>
#include <Encore/ECXMLControl.h>
#include <Encore/ECXMLControlEvent.h>
#include <Encore/ECXMLControlContext.h>
#include <Encore/ECXMLControlDefaultAllocatorRule.h>
#include <Encore/ECXMLControlDefaultRule.h>
#include <Encore/ECXMLControlState.h>
#include <Encore/ECXMLControlRuleSelector.h>
#include <Encore/ECXMLControlReferenceRule.h>

@implementation ECXMLControl

- internalInit {
  self->state = [[ECXMLControlState alloc] init];
  self->ruleSelector = [[ECXMLControlRuleSelector alloc] init];
  self->defaultRulesAdded = NO;
  self->defaultRulesEnabled = YES;

  self->errorHandler = nil;
  self->errorHandlerMethod = NULL;
  return self;
}


- initWithContentsOfURL: (NSURL *) anURL {
  self = [super init];
  self->parser = [[NSXMLParser alloc] initWithContentsOfURL: anURL];
  
  [self internalInit];
  
  return self;
}


- initWithData: (NSData *) data {
  self = [super init];
  self->parser = [[NSXMLParser alloc] initWithData: data];
  
  [self internalInit];
  
  return self;
}


- (void) dealloc {
  if( nil != self->parser ) {
    [self->parser release];
  }
  
  if( nil != self->ruleSelector ) {
    [self->ruleSelector release];
  }
  
  [self->errorHandler release];
  
  [super dealloc];
}


- (NSArray *) userObjects {
  NSArray *toReturn = nil;
  
  if( nil == self->state ) {
    toReturn = [[[NSMutableArray alloc] init] autorelease];
  } else {
    if( nil == [self->state allUserObjects] ) {
      toReturn = [[[NSMutableArray alloc] init] autorelease];
    } else {
      toReturn = [self->state allUserObjects];
    }
  }
  
  return toReturn;
}


- addDefaultRules {
  /* add rule which is responsible for allocating objects */
  [self->ruleSelector addRule:
    [[[ECXMLControlDefaultAllocatorRule alloc] init] autorelease]
    forEvent: [ECXMLControlEvent eventId_elementStarted]];

  /* add a rule which allows the usage of id and id-ref like mechanism by
   * which objects can be references see ECControlReferenceRule for more details
   */
  [self->ruleSelector addRule:
    [[[ECXMLControlReferenceRule alloc] init] autorelease]
    forEvent: [ECXMLControlEvent eventId_addAttribute]];
    
  // add rule which is responsible to lookup and call appropriate setting
  // methods for given attributes    
  [self->ruleSelector addRule:
    [[[ECXMLControlSetAttributeRule alloc] init] autorelease]
    forEvent: [ECXMLControlEvent eventId_addAttribute]];
    
  // add a rule which links nested objects together
  [self->ruleSelector addRule:
    [[[ECXMLControlLinkObjectRule alloc] init] autorelease]
    forEvent: [ECXMLControlEvent eventId_elementEnded]];
  
  self->defaultRulesAdded = YES;
  
  return self;
}


- enableDefaultRules: (BOOL) enable {
  self->defaultRulesEnabled = enable;
  
  return self;
}


- parseXML {
  NSAssert( nil != self->parser, @"ECXMLControl::parseXML: Controller"\
    " not correctly installed!" );
 
  EC_AUTORELEASEPOOL_BEGIN
  
  [self->parser setDelegate: self];
  [self->parser setShouldResolveExternalEntities: NO];
  [self->parser setShouldProcessNamespaces: NO];
  
  if( (!self->defaultRulesAdded) && (self->defaultRulesEnabled)) {
    [self addDefaultRules];
    self->defaultRulesAdded = YES;
  }
  
  [self->parser parse];
  EC_AUTORELEASEPOOL_END
  
  return self;
}


- (void) parser: (NSXMLParser*)aParser
  didEndElement: (NSString*)anElementName
  namespaceURI: (NSString*)aNamespaceURI
  qualifiedName: (NSString*)aQualifierName {

  EC_AUTORELEASEPOOL_BEGIN
  ECXMLControlEvent *event = [[[ECXMLControlEvent alloc] 
    initElementEndedEventWithName: anElementName] autorelease];
  ECXMLControlContext *context;
  
  context = [[[ECXMLControlContext alloc] 
    initWithState: self->state
    causedEvent: event] autorelease];
    
  [self->ruleSelector performRulesForContext: context];
  
  EC_AUTORELEASEPOOL_END
  
  return;
}


- (void) parser: (NSXMLParser*) aParser
  didStartElement: (NSString*) anElementName
  namespaceURI: (NSString*) aNamespaceURI
  qualifiedName: (NSString*) aQualifierName
  attributes: (NSDictionary*) anAttributeDict {

  EC_AUTORELEASEPOOL_BEGIN
  NSEnumerator *allAttributes;
  id attributeName;
  
  ECXMLControlEvent *event = [[[ECXMLControlEvent alloc] 
    initElementStartedEventWithName: anElementName
    withAttributes: anAttributeDict ] autorelease];
  ECXMLControlContext *context;
  
  context = [[[ECXMLControlContext alloc] 
    initWithState: self->state
    causedEvent: event] autorelease];
  [self->ruleSelector performRulesForContext: context];
  
  // now fire events where each event specifies an attribute
  allAttributes = [anAttributeDict keyEnumerator];
  while( (attributeName = [allAttributes nextObject]) ) {
    event = [[[ECXMLControlEvent alloc]
      initAddAttributeEventWithName: anElementName
      withAttributeName: attributeName
      withAttributeValue: [anAttributeDict objectForKey: attributeName]]
      autorelease];
    context = [[[ECXMLControlContext alloc]
    initWithState: self->state
    causedEvent: event] autorelease];
    
    [self->ruleSelector performRulesForContext: context];
  }
  EC_AUTORELEASEPOOL_END
}


- (void)parser:(NSXMLParser *)aParser parseErrorOccurred:(NSError *)parseError {
  if( nil != self->errorHandler ) {
    if( NULL != self->errorHandlerMethod ) {
      [self->errorHandler 
        performSelector: self->errorHandlerMethod
        withObject: parseError];
    }
  }
}


- (id) rootUserObject {
  NSArray *userObjects = [self userObjects];
  id toReturn = nil;
  
  if( 0 < [userObjects count] ) {
    toReturn = [userObjects objectAtIndex: 0];
  }
  
  return toReturn;
}


- (ECXMLControlRuleSelector *) ruleSelector {
  return self->ruleSelector;
}


- delegateErrorsTo: (id) anErrorHandler usingMethod: (SEL) aMethod {
  id tmp = self->errorHandler;
  
  self->errorHandler = [anErrorHandler retain];
  [tmp release];
  
  self->errorHandlerMethod = aMethod;
  
  return self;
}
@end