/*!
  @header ECParameterString
  @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

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

#include <Encore/ECExceptions.h>
#include <Encore/ECMemory.h>
#include <Encore/ECParameterString.h>


/**
 * Specifies the maximum number of variable parameters
 */
#define _MAX_NR_OF_PARAMETERS 10


@implementation ECParameterString
- initWithFormat: (NSString *) aFormatString
  parameterEvaluater: (id <ECParameterEvaluater>) aParameterEvaluater {
  self = [super init];
  
  if( nil == aParameterEvaluater ) {
    [[[ECIllegalArgumentException alloc]
      initWithArgumentInfo: @"ECParameterString::initWithFormat: "\
        "parameterValue may not equal nil!"]
      raise];
  }
  
  self->parameterEvaluater = [aParameterEvaluater retain];
  self->parameters = [[NSMutableArray alloc] init];

  [self parseFormatString: aFormatString];

  return self;
}


- (void) dealloc {
  if( nil != formatString ) {
    [formatString release];
  }
  
  if( nil != self->parameterEvaluater ) {
    [self->parameterEvaluater release];
  }
  
  
  if( nil != self->parameters ) {
    [self->parameters release];
  }
  
  [super dealloc];
}


- (id) parseFormatString: (NSString *) aFormatString {
  NSCharacterSet *variableDeclEND;
  NSString *variableDeclSTART;
  NSScanner *scanner;
  NSString *interimString;
  int nrOfParameters = 0;
  
  self->formatString = [[NSMutableString alloc] init]; 
  
  EC_AUTORELEASEPOOL_BEGIN

  variableDeclEND = [NSCharacterSet characterSetWithCharactersInString: @"}"];
  variableDeclSTART = @"${";
  
  scanner = [NSScanner scannerWithString: aFormatString];
  [scanner setCharactersToBeSkipped: nil];
 
  while( NO == [scanner isAtEnd] ) {
    /**
     * Read all characters up to "${":
     */
    if( [scanner scanUpToString: variableDeclSTART
      intoString: &interimString ] ) {
      [self->formatString appendString: interimString];
    }

    /**
     * Read (and thereby skip) "${"
     */
    if( [scanner scanString: variableDeclSTART intoString: NULL] ) {
      /**
       * Read variable name:
       */
      if( [scanner scanUpToString: @"}" intoString: &interimString] ) {
        /**
         * now check wether the substring at the current scan position
         * equals "}". If not then the rest of the string can be added
         * to the result string. Otherwise we have the variable name and
         * should skip the "}"
         */
        if( [scanner scanString: @"}" intoString: NULL] ) {
          /**
           * got variable name:
           */
          [self->formatString appendString: @"%@"];
          [self->parameters addObject: interimString];
          nrOfParameters++;
          if( _MAX_NR_OF_PARAMETERS < nrOfParameters ) {
            [[[ECIllegalArgumentException alloc]
              initWithArgumentInfo: @"ECParameterString::substituteVariables"\
                "Too many variable parameters given! At "\
                "present only 10 variable parameters are supported!"]
              raise];
        }
        } else {
          [self->formatString appendString: interimString];
        }
      }
    }
  }
  
  EC_AUTORELEASEPOOL_END
  return self;
}


- (NSString *) substituteVariables {
  NSDictionary *parameterValues;
  NSString *toReturn = nil;

  if( nil != self->parameters ) {
    parameterValues = [self->parameterEvaluater 
      evaluateParameters:self->parameters];

    /**
     * The following lines might look strange but i do not know
     * a different approach for manually handing over va_args in a
     * compatible way...
     */
    switch( [self->parameters count] ) {
      case 0:
        toReturn = self->formatString;
        break;
      case 1: 
        toReturn = [[[NSString alloc] initWithFormat: self->formatString,
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 0]]] autorelease];
        break;
      case 2: 
        toReturn = [[[NSString alloc] initWithFormat: self->formatString,
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 0]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 1]]] autorelease];
        break;
      case 3: 
        toReturn = [[[NSString alloc] initWithFormat: self->formatString,
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 0]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 1]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 2]]] autorelease];
        break;
      case 4: 
        toReturn = [[[NSString alloc] initWithFormat: self->formatString,
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 0]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 1]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 2]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 3]]] autorelease];
        break;
      case 5: 
        toReturn = [[[NSString alloc] initWithFormat: self->formatString,
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 0]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 1]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 2]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 3]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 4]]] autorelease];
        break;
      case 6: 
        toReturn = [[[NSString alloc] initWithFormat: self->formatString,
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 0]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 1]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 2]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 3]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 4]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 5]]] autorelease];
        break;
      case 7: 
        toReturn = [[[NSString alloc] initWithFormat: self->formatString,
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 0]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 1]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 2]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 3]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 4]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 5]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 6]]] autorelease];
        break;
      case 8: 
        toReturn = [[[NSString alloc] initWithFormat: self->formatString,
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 0]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 1]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 2]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 3]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 4]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 5]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 6]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 7]]] autorelease];
        break;
      case 9: 
        toReturn = [[[NSString alloc] initWithFormat: self->formatString,
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 0]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 1]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 2]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 3]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 4]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 5]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 6]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 7]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 8]]] autorelease];
        break;
      case 10: 
        toReturn = [[[NSString alloc] initWithFormat: self->formatString,
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 0]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 1]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 2]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 3]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 4]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 5]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 6]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 7]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 8]],
          [parameterValues objectForKey: 
            [self->parameters objectAtIndex: 9]]] autorelease];
        break;
      default:
        [[[ECIllegalArgumentException alloc]
          initWithArgumentInfo: @"ECParameterString::substituteVariables: "\
            "Too many variable parameters given! At "\
            "present only 10 variable parameters are supported!"]
          raise];
    }
  }
  return toReturn;
}
@end


// --------------------------------------------- ECDefaultParameterEvaluatorImpl
@implementation ECDefaultParameterEvaluaterImpl
  
- init {
  self = [super init];
  self->parameterToValue = [[NSMutableDictionary alloc] init];
  
  return self;
}


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


- (NSDictionary *) evaluateParameters: (NSArray *) parameters {
  return [self parameterValues];
}


- (NSDictionary *) parameterValues {
  return [NSDictionary dictionaryWithDictionary: self->parameterToValue];
}


- removeAllVariables {
  if( nil != self->parameterToValue ) {
    [self->parameterToValue release];
  }
  
  self->parameterToValue = [[NSMutableDictionary alloc] init];
  
  return self;
}


- setParameterValues: (NSDictionary *) dictionary {
  if( nil == dictionary ) {
    [self removeAllVariables];

    return self;
  }
  
  if( nil != self->parameterToValue ) {
    [self->parameterToValue release];
  }
  
  self->parameterToValue = [[NSMutableDictionary alloc]
    initWithDictionary: dictionary];
    
  return self;
}


- setValue: (NSString *) aValue forVariable: (NSString *) aVariable {
  [self->parameterToValue setObject: aValue forKey: aVariable];
  
  return self;
}
@end


