/*
**  RegEx.m
**
**  Copyright (c) 2002, 2003, 2004
**
**  Author: Yen-Ju Chen <yjchenx@hotmail.com>
**
**  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
*/

#include "RegEx.h"
#include "GNUstep.h"

@implementation RangeObject

- (id) init
{
  self = [super init];
  _location = NSNotFound;
  _length = 0;
  return self;
}

- (void) dealloc
{
  [super dealloc];
}

- (unsigned int) location
{
  return _location;
}

- (void) setLocation: (unsigned int) location;
{
  _location = location;
}

- (unsigned int) length
{
  return _length;
}

- (void) setLength: (unsigned int) length;
{
  _length = length;
}

- (NSRange) range;
{
  return NSMakeRange(_location, _length);
}

- (void) setRange: (NSRange) range;
{
  _location = range.location;
  _length = range.length;
}
@end

static NSMutableDictionary *patternsCache;

@implementation RegExPattern

+ (RegExPattern *) regexPattern: (NSString *) pattern
{
  id object;
  
  if ((pattern == nil) || ([pattern length] == 0)) return nil;

  if (patternsCache == nil)
    {
      patternsCache = [NSMutableDictionary new];
    }

  if ((object = [patternsCache objectForKey: pattern]))
    {
      return object;
    }

  object = [[RegExPattern alloc] initWithPattern: pattern
                                         options: REG_EXTENDED];

  [patternsCache setObject: object forKey: pattern];

  return AUTORELEASE(object);
}

+ (RegExPattern *) regexCaseInsensitivePattern: (NSString *) pattern
{
  id object;

  if ((pattern == nil) || ([pattern length] == 0)) return nil;

  if (patternsCache == nil)
    {
      patternsCache = [NSMutableDictionary new];
    }

  if ((object = [patternsCache objectForKey: pattern]))
    {
      return object;
    }

  object = [[RegExPattern alloc] initWithPattern: pattern
                                         options: (REG_ICASE | REG_EXTENDED)];

  [patternsCache setObject: object forKey: pattern];

  return AUTORELEASE(object);
}

- (void) dealloc
{
  regfree(preg);
  /* Not sure to release preg */
  free(preg);
  [super dealloc];
}

- (id) initWithPattern: (NSString *) pattern options: (unsigned int) mask
{
  int result;
  char errbuf[255];
  _mask = mask;

  if ((pattern == nil) && ([pattern length] == 0)) return nil;

  preg = malloc(sizeof(regex_t));
  result = regcomp(preg, [pattern cString], mask);

  if (result != 0)
    {
      regerror(result, preg, errbuf, 255);
      NSLog(@"RegEx Error: Couldn't compile regex %@: %s", pattern, errbuf);

      regfree(preg);
      return nil;
    }

  self =  [super init];
  return self;
}

- (regex_t *) pattern
{
  return preg;
}

@end

static  regmatch_t pmatch[1];
static  char errbuf[255];

@implementation RegExParser

+ (NSRange) rangeOfString:(NSString *) pattern
                 inString: (NSString *) string
{
  return [RegExParser rangeOfString: pattern
                           inString: string
                              range: NSMakeRange(0, [string length])];
}

+ (NSRange) rangeOfCaseInsensitiveString: (NSString *) pattern
                                inString: (NSString *) string
{
  if ((pattern == nil) || ([pattern length] == 0)) 
    return NSMakeRange(NSNotFound, 0);

  return [RegExParser rangeOfPattern: [RegExPattern regexCaseInsensitivePattern: pattern]
                            inString: string
			       range: NSMakeRange(0, [string length])];
}

+ (NSRange) rangeOfPattern: (RegExPattern *) pattern
                  inString: (NSString *) string
{
  return [RegExParser rangeOfPattern: pattern
                            inString: string
                               range: NSMakeRange(0, [string length])];
}

+ (NSRange) rangeOfString: (NSString *) pattern
                 inString: (NSString *) string
                    range: (NSRange) range;
{
  if ((pattern == nil) || ([pattern length] == 0)) 
    return NSMakeRange(NSNotFound, 0);

  return [RegExParser rangeOfPattern: [RegExPattern regexPattern: pattern]
                            inString: string
                               range: range];
}

+ (NSRange) rangeOfPattern: (RegExPattern *) pattern
                  inString: (NSString *) string
                     range: (NSRange) range
{
  int result;
  int location, length;
  int mask = 0;

/* Declare static above
   regmatch_t pmatch[1];
   char errbuf[255];
 */
 
  
  NSAssert(range.location != NSNotFound, @"\nRange can't be NSNotFound");
  NSAssert(range.location + range.length <= [string length], @"\nRange can't longer than string length");
  NSAssert(range.location >= 0, @"\nRange can't less than 0");

  /* Considering the situation of beginning line */
  if (range.location != 0)
    mask = mask | REG_NOTBOL;
  if ((range.location + range.length) != [string length])
    mask = mask | REG_NOTEOL;
   
  result = regexec([pattern pattern], 
                   [[string substringWithRange: range] cString],
                   1, pmatch, mask);
  if (result != 0)
    {
      if (result != REG_NOMATCH) 
        {
          regerror(result, [pattern pattern], errbuf, 255);
          NSLog(@"RegEx Error: Couldn't match RegEx %s", errbuf);
        }
      return NSMakeRange(NSNotFound, 0);
    }

  location = range.location + pmatch->rm_so;
  length = pmatch->rm_eo - pmatch->rm_so;
  NSAssert (length >= 1, @"\nRegEx can't return null string");

  return NSMakeRange(location, length);
}

- (id) initWithPattern: (NSString *) string options: (unsigned int)mask
{
  RegExPattern *pattern = [[RegExPattern alloc] initWithPattern: string 
                                                        options: mask];
  AUTORELEASE(pattern);
  return [self initWithPattern: pattern];
}

- (id) initWithPattern: (RegExPattern *) pattern
{
  self = [super init];

  ASSIGN(_pattern, pattern);

  _string = nil;
  _range = NSMakeRange(NSNotFound, 0);
  _originalRange = NSMakeRange(NSNotFound, 0);
  _lastMatch = NSMakeRange(NSNotFound, 0);

  return self;
}

- (void) dealloc
{
  RELEASE(_pattern);
  RELEASE(_string);
  [super dealloc];
}

- (RegExPattern *) pattern
{
  return _pattern;
}

- (void) setString: (NSString *) string
{
  [self setString: string range: NSMakeRange(0, [string length])];
}

- (void) setString: (NSString *) string range: (NSRange) range
{
  ASSIGN(_string, string);
  _originalRange = range;
  [self reset];
}

- (NSString *) string
{
  return _string;
}

- (NSRange) range
{
  return _originalRange;
}

- (void) reset
{
  _range = _originalRange;
  _lastMatch = NSMakeRange(NSNotFound, 0);
}

@end
