//
// SyntaxState.C
//
// written by Michael Riedel <Michael.Riedel@gmx.de>
//

#include <stdlib.h>
#include <ctype.h>

#include <qpainter.h>
#include <qvector.h>
#include <qmsgbox.h>

#include <kapp.h>

#include "SyntaxState.h"


	
//////////////////////////////////////////////////////////////////
int findState(StateList& states, const QString& stateId)
{
	for(unsigned int index = 0; index < states.count(); index++)
	{
		SyntaxState* s = states[index];
		if(s->id() == stateId)
			return index;
	}
	return 0;
}
//////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////
inline QFont* FontInfo::cachedFont(const QFont& baseFont) const
{
	if(FontDirty)
	{
		if(CachedFont)
			delete CachedFont;
			
		FontInfo* f = (FontInfo*)this;			// superb! 
		f->CachedFont = new QFont(baseFont);
		f->CachedFont->setWeight(weight());
		f->CachedFont->setItalic(italic());
	}
	return CachedFont;	
}
//////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////
void FontInfo::setDrawingAttrs(QPainter& p, const QFont& baseFont) const
{
	QFont* font = cachedFont(baseFont);

	p.setPen(color());
	font->setWeight(weight());
	font->setItalic(italic());
	p.setFont(*font);
	
}
//////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////
SyntaxState::SyntaxState(const SyntaxState& ss)
	: FontInfo(ss)
{
	Name = ss.Name;
	Id = ss.Id;
}
//////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////
SyntaxState::SyntaxState(const QString& initStr)
	: FontInfo()
{
	char* s = new char[initStr.length() + 1];
	strcpy(s, initStr);
	
		// get name of state:
	Name = strtok(s, ",");
	Name = Name.stripWhiteSpace();
//	warning("Name:<%s>", Name.data());

		// get id:
	Id = strtok(0, ",");
	Id = Id.stripWhiteSpace();
//	warning("Id:<%s>", Id.data());
	
		// get (optional) color:
	char* hlp = strtok(0, ",");
	if(!hlp) goto delS;
	ColName = QString(hlp).stripWhiteSpace();
	if(isalpha(ColName[0]))	// first char is letter, then assume color def in rc file
		Color = QColor(ColName);
	else
		Color = strtoul(hlp, 0, 0);
//	warning("Color:%x", Color.rgb());
	
		// get (optional) weight:
	hlp = strtok(0, ",");
	if(!hlp) goto delS;
	Weight = strtol(hlp, 0, 0);
//	warning("Weight:%d", Weight);
	
		// get (optional) italic flag:
	hlp = strtok(0, ",");
	if(!hlp) goto delS;
	Italic = strtol(hlp, 0, 0);
//	warning("Italic:%d", Italic);

delS:	
	delete[] s;
}
//////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////
int SyntaxUnit::Match(const QString& string, int start, int& len, int prevState) const
{ 
	if(prevState == ValidPrevState)
		return match(string, start, len);
	else
		return -1;
}
//////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////
RegExpSyntaxUnit::RegExpSyntaxUnit(const QString& initStr, QVectorT<SyntaxState>& states)
	: SyntaxUnit(0, 0, 0)
{ 
	char* s = new char[initStr.length() + 1];
	strcpy(s, initStr);
	
		// get id:
	char* hlp = strtok(s, ",");
	
		// read regular expression:
/*		
	QString re;
	bool wasEscapedComma;
	do 
	{
		hlp = strtok(0, ",");
		if(!hlp) goto err; 
		re += hlp;
		wasEscapedComma = re[re.length()-1] == '\\';
		if(wasEscapedComma)
		{
			re = re.left(re.length()-1) + ',';
		}
	}
	while(wasEscapedComma);	
*/	

	QString re;
	QString string(strtok(0, "\n"));	// this will catch the rest
	int second;
	
	int first = string.find('"');		// searches for "-enclosed string
	if(first != -1)						// with possible \"-sequences
	{
		second = first;
		do
			second = string.find('"', second + 1);
		while(second != -1 && string[second - 1] == '\\');
			
		if(second != -1)
			re = string.mid(first + 1, second - first - 1);
		else
		{
			Error = RE_ERR_RESEPARATORS;
			goto err;
		}
	}
	else
	{
		Error = RE_ERR_RESEPARATORS;
		goto err;
	}
	hlp = strchr(&hlp[second + 1], ','); // search the next comma
	setSplittedREs(re);
//	warning("**************************");
//	warning("re:<%s>", RE.pattern());
//	warning("startRE:<%s>", REStart.pattern());
		
	hlp = strtok(hlp + 1, ",");
	if(!hlp) { Error = RE_ERR_INVALIDLINE; goto err; }
	//	warning("ph:%s", hlp);
	ValidPrevState = findState(states, QString(hlp).stripWhiteSpace());
//	warning("prev:%x", ValidPrevState);
	
	hlp = strtok(0, ",");
	if(!hlp) { Error = RE_ERR_INVALIDLINE; goto err; }
//	warning("ih:%s", hlp);
	InnerState = findState(states, QString(hlp).stripWhiteSpace());
//	warning("inner:%d", InnerState);
	
	hlp = strtok(0, ",");
	if(!hlp) { Error = RE_ERR_INVALIDLINE; goto err; }
//	warning("nh:%s", hlp);
	StateAfterwards = findState(states, QString(hlp).stripWhiteSpace());
//	warning("next:%d", StateAfterwards);

	goto delS2;

err:
	warning("an error occurred in the line <%s>.", initStr.data());
	QMessageBox::information(0, "Error", initStr.data(), 0, 1);
	
delS2:
	delete[] s;
}
//////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////
int RegExpSyntaxUnit::StartMatch(const QString& string, int start, int& l) const
{
	return REStart.match(string, start, &l);
}
//////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////
int RegExpSyntaxUnit::searchREEntity(const char* hlp, bool& isValid)
{
	int x;
	isValid = true;
	
	switch(hlp[0])
	{
		case '[':
		{
			char* rb = strchr(hlp, ']');
			if(rb)
				x = rb - hlp + 1;
			else
				x = strlen(hlp);
			break;
		}
	
		case '\\':
		{
			if(hlp[1] == '0' || hlp[1] == 'x')
				x = 4;	// two additional chars for \xXX or \0XX codes
			else
				x = 2;
			break;
		}
	
		default:
			x = 1;
			break;
	}
	
		// reset previously validation 
	if(hlp[x] == '*' || hlp[x] == '?')
		isValid = false;
		
	if(hlp[x] == '*' || hlp[x] == '?' || hlp[x] == '+')
		return x + 1;
	else
		return x;

}
//////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////
void RegExpSyntaxUnit::setSplittedREs(const char* hlp)
{
	int splitIx = 0;
	bool validSplitting = false;
	int strlength = strlen(hlp);

	do
		splitIx += searchREEntity(hlp + splitIx, validSplitting);
	while(!validSplitting && splitIx < strlength);

	if(splitIx <= strlength)
	{
		REStart = QString(hlp).left(splitIx);
//		RE 		= QString(hlp).right(strlength - splitIx);
	}
	else
	{
		REStart = QString(hlp).left(strlength);	
//		RE = "";
	}
	RE = hlp;	
}		
//////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////
int FSyntaxUnit::match(const QString& string, int start, int& len) const
{
	int first = string.find('"', start);
	if(first != -1)
	{
		int second = first;
		do
			second = string.find('"', second + 1);
		while(second != -1 && string[second - 1] == '\\');
			
		if(second != -1)
			len = second - first + 1;
		else
			len = string.length() - first;
	}
	else
		len = 0;

	return first;
}
//////////////////////////////////////////////////////////////////

