/*
**  PCREParser.m
**
**  Copyright (c) 2002, 2003
**
**  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 "PCREParser.h"

@implementation RangeObject

- (id) init
{
  self = [super init];
  [self setLocation: NSNotFound];
  [self setLength: 0];
  return self;
}

- (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;
{
  NSRange _range = NSMakeRange(_location, _length);
  return _range;
}

- (void) setRange: (NSRange) range;
{
  [self setLocation: range.location];
  [self setLength: range.length];
}

- (NSString *) description
{
  return [NSString stringWithFormat: @"location = %u, length = %u", _location, _length];
}
@end

static NSMutableDictionary *patternsCache;

@implementation PCREPattern

+ (PCREPattern *) pcrePattern: (NSString *) pattern
{
  id object;

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

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

  object = [[PCREPattern alloc] initWithPattern: pattern
                                        options: PCRE_STUDY];

  [patternsCache setObject: object forKey: pattern];

  return AUTORELEASE(object);
}

- (void) dealloc
{
  pcre_free([self pattern]);
  pcre_free([self patternExtra]);
  [super dealloc];
}

- (id) initWithPattern: (NSString *) pattern options: (unsigned int) mask
{
  self = [super init];
  _mask = 0;

  if (mask & NSCaseInsensitiveSearch)
    {
      _mask |= PCRE_CASELESS;
    }
  if (mask & NSAnchoredSearch)
    {
      _mask |= PCRE_ANCHORED;
    }

  _pcre = pcre_compile([pattern cString], _mask, &error, &erroffset, NULL);

  if (_pcre == NULL)
    {
      NSLog(@"PCRE compilation failed at offset %d: %s",
            erroffset, error);
    }

  if (mask & PCRE_STUDY)
    {
      _pcreExtra = pcre_study(_pcre, 0, &error);

      if (_pcreExtra == NULL)
        {
          if (error != NULL)
            NSLog(@"PCRE study failed: %s", error);
        }
    }

  return self;
}

- (pcre *) pattern
{
  return _pcre;
}

- (pcre_extra *) patternExtra
{
  return _pcreExtra;
}

/** Don't count the full match string.
 *  Ex. "(dog|cat)" will return 1, "dog|cat" will return 0
 */
- (int) numberOfSubpattern
{
  int result, number;

  result = pcre_fullinfo(_pcre, _pcreExtra, PCRE_INFO_CAPTURECOUNT, &number);
  if (result != 0)
    return 0;

  return number;
}

@end

@implementation PCREParser

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

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

+ (NSRange) rangeOfString: (NSString *) pattern
                 inString: (NSString *) string
                    range: (NSRange) range;
{
  return [PCREParser rangeOfPattern: [PCREPattern pcrePattern: pattern]
                           inString: string
                              range: range];
}

+ (NSRange) rangeOfPattern: (PCREPattern *) pattern
                  inString: (NSString *) string
                     range: (NSRange) range
{
  int result;
  int location, length;
  int mask = 0;
  int ovector[3];

  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 | PCRE_NOTBOL;
  if ((range.location + range.length) != [string length])
    mask = mask | PCRE_NOTEOL;
   
  result = pcre_exec([pattern pattern], [pattern patternExtra],
                     [string cString], NSMaxRange(range), range.location,
                     0, ovector, 3);
  if (result < 0)
    {
      return NSMakeRange(NSNotFound, 0);
    }

  location = ovector[0];
  length = ovector[1]-ovector[0];
  NSAssert (length >= 1, @"\nRegEx can't return null string");

  return NSMakeRange(location, length);
}


#if 0
- (id) initWithPattern: (NSString *) string options: (unsigned int)mask
{
  PCREPattern *pattern = [[PCREPattern alloc] initWithPattern: string options: mask];
  return [self initWithPattern: pattern];
}

- (id) initWithPattern: (PCREPattern *) 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];
}

- (PCREPattern *) pattern
{
  return _pattern;
}

- (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);
}

- (NSRange) rangeOfNextMatch
{
  NSRange range;

  if (_lastMatch.location != NSNotFound) // Not a first match
    {
      _range.location = NSMaxRange(_lastMatch) + 1;
      _range.length = NSMaxRange(_originalRange) - _range.location;
      if (_range.location > NSMaxRange(_originalRange)) // The end of string
        {
          return NSMakeRange(NSNotFound,0);
        }
    }

  range = [[self string] rangeOfPattern: _pattern range: _range];

  if (range.location != NSNotFound)
    {
      _lastMatch = range;
    }

  return range;
}

- (NSRange) rangeOfPreviousMatch
{
  NSRange range;

  if (_lastMatch.location != NSNotFound) // Not a first match
    {
      _range.location = _originalRange.location;
      _range.length = _lastMatch.location - _originalRange.location + 1;
    }

  range = [[self string] rangeOfLastPattern: _pattern range: _range];

  if (range.location != NSNotFound)                                                    {
      _lastMatch = range;   
    }
  
  return range;
}
     
- (NSString *) stringOfNextMatch
{
  NSRange range;

  range = [self rangeOfNextMatch];
  if (range.location == NSNotFound)
    return nil;

  return [[self string] substringFromRange: range];
}

- (NSString *) stringOfPreviousMatch
{
  NSRange range;

  range = [self rangeOfPreviousMatch];
  if (range.location == NSNotFound)
    return nil;

  return [[self string] substringFromRange: range];
}

- (NSArray *) rangesOfNextSubpattern
{
  NSRange range = [self rangeOfNextMatch];
  NSArray *rangeArray = [[self string] rangesOfSubpattern: _pattern
                                                    range: range];
  return rangeArray;
}

- (NSArray *) rangesOfPreviousSubpattern
{
  NSRange range = [self rangeOfPreviousMatch];
  NSArray *rangeArray = [[self string] rangesOfSubpattern: _pattern
                                                    range: range];
  return rangeArray;
}

- (NSArray *) stringsOfNextSubpattern
{
  int i, count;
  NSRange range;
  NSMutableArray *stringArray = [NSMutableArray new];
  NSArray *rangeArray = [self rangesOfNextSubpattern];

  RETAIN(rangeArray);
  count = [rangeArray count];
  for (i = 0; i < count; i++)
    {
      range = [[rangeArray objectAtIndex: i] range];
      if (range.location == NSNotFound)
        [stringArray addObject: @""];
      else
        [stringArray addObject: [[self string] substringFromRange: range]];
    }
  RELEASE(rangeArray);
  AUTORELEASE(stringArray);
  return stringArray;
}

- (NSArray *) stringsOfPreviousSubpattern
{
  int i, count;
  NSRange range;
  NSMutableArray *stringArray = [NSMutableArray new];
  NSArray *rangeArray = [self rangesOfPreviousSubpattern];

  RETAIN(rangeArray);
  count = [rangeArray count];
  for (i = 0; i < count; i++)
    {
      range = [[rangeArray objectAtIndex: i] range];
      if (range.location == NSNotFound)
        [stringArray addObject: @""];
      else
        [stringArray addObject: [[self string] substringFromRange: range]];
    }
  RELEASE(rangeArray);
  AUTORELEASE(stringArray);
  return stringArray;
}

#endif

@end
