/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

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 3 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, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
// hsStringTokenizer.cpp

#include "hsStringTokenizer.h"
#include "hsUtils.h"

// String Tokenizer routines
hsStringTokenizer::hsStringTokenizer(const char *string, const char *seps) :
fQAsTok(true),
fInQuote(false),
fString(nil),
fSeps(nil),
fLastTerminator(nil)
{
	Reset(string,seps);
}

hsStringTokenizer::~hsStringTokenizer() 
{
	delete [] fString;
	delete [] fSeps;
}

hsBool hsStringTokenizer::HasMoreTokens() 
{
	return (*fTok != 0);
}

inline hsBool hsStringTokenizer::IsSep(char c) 
{
	if (!fQAsTok || !fInQuote) 
	{
		if ( fCheckAlphaNum || !isalnum(c) )
		{
			for (Int32 i=0; i<fNumSeps; i++) 
			{
				if (fSeps[i] == c) 
					return true;
			}
		}
	}
	if (fQAsTok && c=='\"') 
	{
		fInQuote = !fInQuote;
		return true;
	}
	return false;
}

char *hsStringTokenizer::next() 
{
	if (*fTok == 0) 
		return nil;

	char *cur = fTok;
	while (*fTok != 0 && !IsSep(*fTok)) 
		fTok++;

	if (*fTok != 0) 
	{
		fLastRep = *fTok;
		fLastTerminator = fTok;

		*fTok = 0;
		fTok++;
	}
	while (*fTok != 0 && IsSep(*fTok)) 
		fTok++;

	return cur;
}

// Slightly more loop-friendly version of next
hsBool	hsStringTokenizer::Next( char *token, UInt32 maxTokLen )
{
	char *t = next();
	if( t == nil )
		return false;

	hsStrncpy( token, t, maxTokLen );
	return true;
}

// Restores the last character replaced to generate a terminator
void	hsStringTokenizer::RestoreLastTerminator( void )
{
	if( fLastTerminator != nil )
	{
		*fLastTerminator = fLastRep;
		fLastTerminator = nil;
	}
}

void hsStringTokenizer::Reset(const char *string, const char *seps) 
{
	if (fString)
		delete [] fString;
	fString = string ? hsStrcpy(string) : nil;

	if (fSeps)
		delete [] fSeps;
	fSeps = seps ? hsStrcpy(seps) : nil;
	fNumSeps = fSeps ? strlen(fSeps) : 0;
	fCheckAlphaNum = false;
	for (Int32 i=0; i<fNumSeps; i++)
	{
		if (isalnum(fSeps[i]))
		{
			fCheckAlphaNum=true;
			break;
		}
	}

	fTok = fString;

	fLastTerminator = nil;
	fLastRep = 0;

	// don't skip empty fields.
//	if (fTok && IsSep(*fTok))
//		next();
}

void hsStringTokenizer::ParseQuotes(hsBool qAsTok)
{
	fQAsTok = qAsTok;
}

///////////////////////////////////////////////////////////////////////////////

// String Tokenizer routines
hsWStringTokenizer::hsWStringTokenizer(const wchar *string, const wchar *seps) :
	fQAsTok(true),
	fInQuote(false),
	fString(nil),
	fSeps(nil),
	fLastTerminator(nil)
{
	Reset(string,seps);
}

hsWStringTokenizer::~hsWStringTokenizer() 
{
	delete [] fString;
	delete [] fSeps;
}

hsBool hsWStringTokenizer::HasMoreTokens() 
{
	return (*fTok != L'\0');
}

inline hsBool hsWStringTokenizer::IsSep(wchar c) 
{
	if (!fQAsTok || !fInQuote) 
	{
		if ( fCheckAlphaNum || !iswalnum(c) )
		{
			for (Int32 i=0; i<fNumSeps; i++) 
			{
				if (fSeps[i] == c) 
					return true;
			}
		}
	}
	if (fQAsTok && c==L'\"') 
	{
		fInQuote = !fInQuote;
		return true;
	}
	return false;
}

wchar *hsWStringTokenizer::next() 
{
	if (*fTok == L'\0') 
		return nil;

	wchar *cur = fTok;
	while (*fTok != L'\0' && !IsSep(*fTok)) 
		fTok++;

	if (*fTok != L'\0') 
	{
		fLastRep = *fTok;
		fLastTerminator = fTok;

		*fTok = L'\0';
		fTok++;
	}
	while (*fTok != L'\0' && IsSep(*fTok)) 
		fTok++;

	return cur;
}

// Slightly more loop-friendly version of next
hsBool	hsWStringTokenizer::Next( wchar *token, UInt32 maxTokLen )
{
	wchar *t = next();
	if( t == nil )
		return false;

	wcsncpy( token, t, maxTokLen - 1 );
	token[maxTokLen - 1] = L'\0';
	return true;
}

// Restores the last character replaced to generate a terminator
void	hsWStringTokenizer::RestoreLastTerminator( void )
{
	if( fLastTerminator != nil )
	{
		*fLastTerminator = fLastRep;
		fLastTerminator = nil;
	}
}

void hsWStringTokenizer::Reset(const wchar *string, const wchar *seps) 
{
	if (fString)
		delete [] fString;
	if (string)
	{
		int	count = wcslen(string);
		fString = TRACKED_NEW(wchar[count + 1]);
		wcscpy(fString, string);
		fString[count] = L'\0';
	}
	else
		fString = nil;

	if (fSeps)
		delete [] fSeps;
	if (seps)
	{
		int	count = wcslen(seps);
		fSeps = TRACKED_NEW(wchar[count + 1]);
		wcscpy(fSeps, seps);
		fSeps[count] = L'\0';
	}
	else
		fSeps = nil;

	fNumSeps = fSeps ? wcslen(fSeps) : 0;
	fCheckAlphaNum = false;
	for (Int32 i=0; i<fNumSeps; i++)
	{
		if (iswalnum(fSeps[i]))
		{
			fCheckAlphaNum=true;
			break;
		}
	}

	fTok = fString;

	fLastTerminator = nil;
	fLastRep = 0;

	// don't skip empty fields.
	//	if (fTok && IsSep(*fTok))
	//		next();
}

void hsWStringTokenizer::ParseQuotes(hsBool qAsTok)
{
	fQAsTok = qAsTok;
}