/* a basic c(++) code to rtf converter
   John Donoghue, 1999.
  
  uses singleton patterns

  This code is released under the GPL licencing agreement.
	See www.gnu.org for the current licence.

  FIX: return and default werenot showing as keywords
 */

#include <iostream>
#include <fstream>
#include <cstring>

#define NORMALSTYLE 	"\\plain\\cf0\\fs20 "
#define BROWNSTYLE  	"\\plain\\cf1\\fs20 "
#define GREENSTYLE  	"\\plain\\cf2\\fs20 "
#define GREYITALICSTYLE "\\plain\\cf3\\i\\fs20 "
#define BLUESTYLE  	"\\plain\\cf4\\fs20 "
#define BOLDSTYLE	"\\plain\\cf0\\b\\fs20 "

#define MAX_BUFF 2048

/* check if the character is withing a array of chars */
bool IsIn(char c,char *check,int size)
{
	for(int i=0;i<size;i++)
		if(c==check[i]) return true;

	return false;
}
// sort of the same func for strings
char * IsIn(char *c,char *check[],int size)
{
	for(int i=0;i<size;i++)
		if(strncmp(c,check[i],strlen(check[i]))==0) return check[i];

	return NULL;
}
static char numbers_array[]="0123456789xabcdefXABCDEF.";
#define IsNumber(c) IsIn(c,numbers_array,sizeof(numbers_array)-1)

static char delim_array[]="\n\r\t\'.,:;[]{}()-+<>*/%&|!= ";
#define IsDelim(c) IsIn(c,delim_array,sizeof(delim_array))

static char space_array[]="\n\r\t ";
#define IsSpace(c) IsIn(c,space_array,sizeof(space_array))

static char *reserved_words[]={
	"int","float","double","long","char","short","void",
	"unsigned","signed","static","const","extern",
	"for","while","do","goto",
	"public","private","protected","virtual",
	"if","else","case","switch","break","default",
		"return",
	"class","struct","enum","typedef","sizeof",
	"bool","true","false","NULL","this",
	"throw", "catch", "try", "using", "template",
	"sizeof", "inline", "delete", "friend", "new",
	"typedef","operator","namespace",

	// some c biulder types
	"__fastcall","__published",
};

// ret the number of chars in the reserved word, or 0 if none found
int IsReservedWord(char *str) 
{
	char *c;  
	int i;

	c=IsIn(str,reserved_words,sizeof(reserved_words)/sizeof(void *));
	if(c!=NULL && IsDelim(str[strlen(c)])) {

		return(strlen(c));
	}
	return 0;
}  


class InitState;
class CState;

class CState {
	protected:
		CState() {}
		static char statebuff[MAX_BUFF];
		//static Style currstyle;
		static int currpos;
		static char lastchar,lastchar2;
	public:
		static CState *getState(CState *curr) { 
			//static CState state;
			//return &state; 
			return NULL; // should never be called 
		}
		virtual void flushBuffer();

		char getLastChar() {
			return lastchar;
		}
		char get2ndLastChar() {
			return lastchar2;
		}

		virtual void putChar(char c) {
			if(currpos>=MAX_BUFF) // flush if full
				flushBuffer();

			statebuff[currpos]=c;
			currpos++; 
			//}
			lastchar2=lastchar; 
			lastchar=c;
		}
		char killLastChar() {
			if(currpos==0) return 0;
			currpos--;
			return statebuff[currpos];
		}

		virtual CState *addChar(CState *curr,char c);
};

int CState::currpos=0;
//Style CState::currstyle=NORMAL;
char CState::lastchar=0;
char CState::lastchar2=0;
char CState::statebuff[MAX_BUFF];

class InitState : public CState
{ 
	private:
		InitState() {}
	public:
		static CState *getState(CState *curr) { 
			static InitState state;

			// write out the required stuff
			std::cout<<"{\\rtf1\\ansi\\ansicpg1252\\deff0\n";
			std::cout<<"{\\fonttbl\n";
			std::cout<<"{\\f0\\fnil\\fcharset0\\fprq0\\fttruetype Courier;}}\n";
			std::cout<<"{\\colortbl\n";
			std::cout<<"\\red0\\green0\\blue0;\\red200\\green60\\blue60;";
			std::cout<<"\\red0\\green160\\blue0;\\red120\\green40\\blue40;";
			std::cout<<"\\red0\\green0\\blue255;\\red100\\green0\\blue100;}\n";
			std::cout<<"\\f0\\fs20\n";

			return (CState *)&state;
		} 
		virtual CState *addChar(CState *curr,char c);
};
class EndState : public CState
{ 
	private:
		EndState() {}
	public:
		static CState *getState(CState *curr) { 

			// write out the required stuff
			if(curr) curr->flushBuffer();

			std::cout<<"}\n";

			return (CState *)NULL;
		} 
		// always end with NULL state
		virtual CState *addChar(CState *curr,char c) { return NULL; }
};
class NewLineState : public CState
{ 
	private:
		NewLineState() {}
	public:
		static CState *getState(CState *curr) { 
			static NewLineState state;
			if(curr!=&state) {
				//cerr<<"** newline state **\n";
				if(curr) curr->flushBuffer();
			}
			// write out the required stuff
			return (CState *)&state;
		} 
		// always end with NULL state
		virtual CState *addChar(CState *curr,char c);
};

class NormalState : public CState
{ 
	private:
		NormalState() {}
	public:
		static CState *getState(CState *curr) { 
			static NormalState state;
			if(curr!=&state) {
				//cerr<<"** normal state **\n";
				if(curr) curr->flushBuffer();
				std::cout<<NORMALSTYLE;
			}
			// write out the required stuff
			return (CState *)&state;
		} 
		// always end with NULL state
		virtual CState *addChar(CState *curr,char c);
		virtual void flushBuffer(); // overload normal flush
};
class StringState : public CState
{ 
	private:
		StringState() {}
	public:
		static CState *getState(CState *curr) { 
			static StringState state;
			if(curr!=&state) {
				//cerr<<"** dstring state **\n";
				if(curr) curr->flushBuffer();
				std::cout<<GREENSTYLE;
			}
			// write out the required stuff
			return (CState *)&state;
		} 
		// always end with NULL state
		virtual CState *addChar(CState *curr,char c);
};
class QuoteState : public CState
{ 
	private:
		QuoteState() {}
	public:
		static CState *getState(CState *curr) { 
			static QuoteState state;
			if(curr!=&state) {
				//cerr<<"** quote state **\n";
				if(curr) curr->flushBuffer();
				std::cout<<GREENSTYLE;
			}
			// write out the required stuff
			return (CState *)&state;
		} 
		virtual CState *addChar(CState *curr,char c);
};
class NumberState : public CState
{ 
	private:
		NumberState() {}
	public:
		static CState *getState(CState *curr) { 
			static NumberState state;
			if(curr!=&state) {
				//cerr<<"** number state **\n";
				if(curr) curr->flushBuffer();
				std::cout<<BROWNSTYLE;
			}
			// write out the required stuff
			return (CState *)&state;
		} 
		// always end with NULL state
		virtual CState *addChar(CState *curr,char c);
};
class LineCommentState : public CState
{ 
	private:
		LineCommentState() {}
	public:
		static CState *getState(CState *curr) { 
			static LineCommentState state;

			if(curr!=&state) {
				//cerr<<"** line state **\n";
				if(curr) curr->flushBuffer();
				std::cout<<GREYITALICSTYLE;

			}

			// write out the required stuff
			return (CState *)&state;
		} 
		// always end with NULL state
		virtual CState *addChar(CState *curr,char c);
};

class BlockCommentState : public CState
{ 
	private:
		BlockCommentState() {}
	public:
		static CState *getState(CState *curr) { 
			static BlockCommentState state;
			if(curr!=&state) {
				//cerr<<"** block state **\n";
				if(curr) curr->flushBuffer();
				std::cout<<GREYITALICSTYLE;
			}

			// write out the required stuff
			return (CState *)&state;
		} 
		// always end with NULL state
		virtual CState *addChar(CState *curr,char c);
};

class PreState : public CState
{ 
	private:
		PreState() {}
	public:
		static CState *getState(CState *curr) { 
			static PreState state;

			if(curr!=&state) {
				//cerr<<"** pre state **\n";
				if(curr) curr->flushBuffer();
				std::cout<<BLUESTYLE;

			}

			// write out the required stuff
			return (CState *)&state;
		} 
		// always end with NULL state
		virtual CState *addChar(CState *curr,char c);
};


class CtoRTF {
	private:
		CState *currstate;
	public:
		CtoRTF() {
			// init the class and set up the first state
			currstate=InitState::getState(NULL);
		}
		~CtoRTF() {
			if(currstate) currstate=EndState::getState(currstate);
		}
		bool addChar(char c)
		{
			if(currstate) currstate=currstate->addChar(currstate,c);
			return true;
		}
};

int main()
{
	char c;
	CtoRTF formatter;

	std::cin.get(c);
	while(!std::cin.eof()) {
		//std::cout<<c;
		formatter.addChar(c);
		std::cin.get(c);
	}
}

CState *CState::addChar(CState *curr,char c)
{
	// if we are here, call the init state
	CState *temp=InitState::getState(curr);

	return temp->addChar(curr,c);
}
CState *InitState::addChar(CState *curr,char c)
{
	// we are in init state - got to newline state since we start
	// on a new line
	CState *temp=NewLineState::getState(curr);

	return temp->addChar(curr,c);
}
CState *NewLineState::addChar(CState *curr,char c)
{
	// we are in newline state
	// if c is not a newline or space char - go into normal mode
	if(c=='#') {
		// a leading # - preprocessor command?
		CState *temp=PreState::getState(curr);
		return temp->addChar(temp,c);      
	}
	else if(!IsSpace(c)) {
		CState *temp=NormalState::getState(curr);
		return temp->addChar(curr,c);
	}
	else putChar(c);
	return this;
}
CState *NormalState::addChar(CState *curr,char c)
{ 

	if(c=='*' && getLastChar()=='/') // start of block comment
	{
		killLastChar(); // remove last char from the buff
		curr=BlockCommentState::getState(curr);
		curr=curr->addChar(curr,'/');
		curr=curr->addChar(curr,'*');
		return curr;
	}
	else if(c=='/' && getLastChar()=='/') // start of line comment
	{
		killLastChar(); // remove last char from the buff
		curr=LineCommentState::getState(curr);
		curr=curr->addChar(curr,'/');
		curr=curr->addChar(curr,'/');
		return curr;
	}  
	else if(c=='\"') // start of string comment
	{
		curr=StringState::getState(curr);
		curr->putChar('\"');
		return curr;
	}
	else if(c=='\'') // start of string comment
	{
		curr=QuoteState::getState(curr);
		curr->putChar('\'');
		return curr;
	}
	else if((c>='0' && c<='9') && IsDelim(getLastChar())) // start of number
	{
		//cerr<<"add a number\n";

		curr=NumberState::getState(curr);
		curr=curr->addChar(curr,c);
		return curr;
	}
	else if(c=='\n' || c=='\r') {
		putChar(c);
		curr=NewLineState::getState(curr);
		return curr;  
	}
	else putChar(c);

	return this; 
}
CState *LineCommentState::addChar(CState *curr,char c)
{
	// end of line  - end of comment
	putChar(c);

	if(c=='\n' || c=='\r') {
		curr=NewLineState::getState(curr);
		return curr;
	}
	return (CState *)this;
}
CState *BlockCommentState::addChar(CState *curr,char c)
{
	// end of comment block
	putChar(c);

	if(c=='/' && get2ndLastChar()=='*') {
		curr=NormalState::getState(curr);
		return curr;
	}

	return (CState *)this;
}
CState *StringState::addChar(CState *curr,char c)
{
	// end of comment block


	if(c=='\"' && getLastChar()!=0) {
		// if we have \\"
		//if((getLastChar()!='\\') ||
		//   (getLastChar()=='\\' && get2ndLastChar()!='\\')) 
		//{
		putChar(c);

		curr=NormalState::getState(curr);
		return curr;
		//}
	}
	putChar(c);

	return (CState *)this;
}
CState *QuoteState::addChar(CState *curr,char c)
{
	if(c=='\'' && getLastChar()!=0 && 
			(getLastChar()!='\\' ||
			 (getLastChar()=='\\' && get2ndLastChar()=='\\'))) {
		putChar(c); 
		curr=NormalState::getState(curr);
		return curr;
	}
	putChar(c);

	return (CState *)this;
}
CState *NumberState::addChar(CState *curr,char c)
{
	if(!IsNumber(c)) 
	{
		curr=NormalState::getState(curr);
		curr->addChar(curr,c);
		return curr;
	}
	putChar(c);

	return (CState *)this;
}
CState *PreState::addChar(CState *curr,char c)
{
	// end of line  - pre-processsor stuff?
	putChar(c);

	if(c=='\n' || c=='\r') {
		curr=NewLineState::getState(curr);
		return curr;
	}
	return (CState *)this;
}
void NormalState::flushBuffer() {
	bool bold=false;
	char c;

	if(currpos>0) {
		int i,x;
		statebuff[currpos]='\0';

		for(i=0;i<currpos;i++) {
			c=statebuff[i];

			// handle bolding of delims that are not spaces
			// ie. operators
			if(IsDelim(c)==true && IsSpace(c)==false) {
				if(!bold) std::cout<<BOLDSTYLE;
				bold=true;
			}
			else if(!IsSpace(c)){
				if(bold) std::cout<<NORMALSTYLE;
				bold=false;  
			}

			if(c=='\n') {
				std::cout<<"\\par\n";    
			}
			else if(c=='\t') {
				std::cout<<"\\tab ";          
			}
			else if(c=='\\') {
				std::cout<<"\\\\";               
			}
			else if(c=='{' || c=='}') {
				std::cout<<"\\"<<c;               
			}
			else if((x=IsReservedWord(&statebuff[i]))>0 && 
					(i==0 || IsDelim(statebuff[i-1]))) 
			{
				// a reserved word found
				int n;

				if(!bold) std::cout<<BOLDSTYLE;
				bold=true;

				for(n=i;n<i+x;n++) {
					c=statebuff[n];
					std::cout<<c;

				}
				i+=(x-1);
			}
			else {
				std::cout<<c;
			}


		}
	}
	if(bold) std::cout<<NORMALSTYLE;

	currpos=0;
}
void CState::flushBuffer() {
	char c;

	if(currpos>0) {
		int i;

		for(i=0;i<currpos;i++) {
			c=statebuff[i];

			if(c=='\n') {
				std::cout<<"\\par\n";    
			}
			else if(c=='\t') {
				std::cout<<"\\tab ";          
			}
			else if(c=='\\') {
				std::cout<<"\\\\";               
			}
			else if(c=='{' || c=='}') {
				std::cout<<"\\"<<c;               
			}
			else {
				std::cout<<c;
			}
		}
	}
	currpos=0;
	return;
}
