/*********************************************************************
 * This file is part of the cpplibs suite.
 *
 * Copyright (C) 2001 Topi Mäenpää
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.2 $
 *********************************************************************/

#include "Regex.h"
#include <regex.h>

#define substr(bfr,src,start,end) { delete[] bfr; bfr = new char[end-start+1]; strncpy(bfr,src+start,end-start); bfr[end-start]=0; }

using namespace std;

namespace util
{
	int Regex::find(const std::string& haystack,
									const std::string& needle,
									bool ignoreCase,
									int startIndex)
	{
		regex_t pattern;
		int mask = REG_EXTENDED;
		if (ignoreCase) mask |= REG_ICASE;
		if (regcomp(&pattern,needle.c_str()+startIndex,mask))
			{
				regfree(&pattern);
				return -1;
			}
		regmatch_t matches[1];
		int result = regexec(&pattern,haystack.c_str(),1,matches,0);
		regfree(&pattern);

		if (result)
			return -1;
		return matches[0].rm_so;
	}
	
	string Regex::replace(const std::string& haystack,
												const std::string& needle,
												const std::string& replacement,
												bool ignoreCase,
												int max)
	{
		regex_t pattern;
		int mask = REG_EXTENDED;
		if (ignoreCase) mask |= REG_ICASE;
		if (regcomp(&pattern,needle.c_str(),mask))
			{
				regfree(&pattern);
				return haystack;
			}
		string result(haystack.size(),0);
		const char* bfr = haystack.c_str();
		regmatch_t matches[REGEX_MAX_SUBSTRINGS];
		char *tmp = NULL;
		int matchCount = 0;
		bool needsSubstring = true;

		//Loop until there are no more matches or until the maximum match
		//count is exceeded.
		while (!regexec(&pattern,bfr,REGEX_MAX_SUBSTRINGS,matches,0) &&
					 (max < 1 || matchCount < max))
			{
				//Append to the result the string that preceded the match
				substr(tmp,bfr,0,matches[0].rm_so);
				result.append(tmp);

				//If substring replacement is needed, construct one by
				//replacing all characters below REGEX_MAX_SUBSTRING by the
				//corresponding substring matches.
				if (needsSubstring)
					{
						needsSubstring = false;
						string realReplacement;
						for (unsigned int i=0;i<replacement.size();i++)
							{
								//If the replacement character is a special substring code
								if (replacement[i] <= REGEX_MAX_SUBSTRINGS)
									{
										needsSubstring = true;
										//If there is such a match
										if (matches[replacement[i]].rm_so != -1)
											{
												substr(tmp, bfr, matches[replacement[i]].rm_so, matches[replacement[i]].rm_eo);
												realReplacement.append(tmp);
											}
									}
								else //Just add the character
									realReplacement += replacement[i];
							}
						//Replace the regexp match with the constructed replacement
						//string.
						result.append(realReplacement);
					}
				else
					result.append(replacement);

				//Advance search start pointer
				bfr += matches[0].rm_eo;
				matchCount++;
			}
		//Release substring template buffer
		delete[] tmp;
		//Release pattern space
		regfree(&pattern);

		//Append the remaining string to the result
		if (*bfr)
			result.append(bfr);
		return result;
	}

	
	void Regex::tokenize(const std::string& str,
											 const std::string& regex,
											 List<std::string>& result,
											 bool ignoreCase,
											 int max)
	{
		regex_t pattern;
		int mask = REG_EXTENDED;
		if (ignoreCase) mask |= REG_ICASE;
		if (regcomp(&pattern,regex.c_str(),mask))
			{
				regfree(&pattern);
				result += str;
				return;
			}
		regmatch_t matches[1];
		const char* bfr = str.c_str();
		char* tmp = NULL;
		int matchCount = 0;
		//Loop until no more matches are found or the maximum match count
		//is exceeded.
		while (!regexec(&pattern,bfr,1,matches,0) &&
					 (max < 1 || matchCount < max))
			{
				substr(tmp, bfr, 0, matches[0].rm_so);
				result += string(tmp);
				bfr += matches[0].rm_eo;
				matchCount++;
			}

		delete[] tmp;
		regfree(&pattern);

		//Append the rest as a whole.
		if (*bfr)
			result += string(bfr);
	}
}
