/*
 * Copyright (C) 2007-2008 Xagasoft, All rights reserved.
 *
 * This file is part of the libbu++ library and is released under the
 * terms of the license contained in the file LICENSE.
 */

#ifndef BU_MINI_MACRO_H
#define BU_MINI_MACRO_H

#include "bu/hash.h"
#include "bu/fstring.h"

namespace Bu
{
	typedef Bu::Hash<Bu::FString, Bu::FString> StrHash;
	/**
	 * A processor for Libbu++ brand Mini Macros.  These are really simple, but
	 * still fairly flexible.  It's mainly text replacement, but with a few
	 * extras like filter functions and conditional text segments.  So far we
	 * don't support loops or anything, I'm not sure we ever will.
	 *
	 * Anatomy of a mini macro:
	 * - Every macro begins with a two character code, the first character is
	 *   always '{', the second character determines the operation to perform.
	 * - If the '{' is followed by a character that is not valid it is not
	 *   considered for expansion and the characters are copied to the output.
	 * - Every macro ends with a closing '}'
	 * - Macro types:
	 *   - '=': variable replacement.  The '=' is immediatley followed by the
	 *     name of the variable to replace, then any number of optional filter
	 *     segments.
	 *   - '?': conditional text.  The '?' is immediately followed by the
	 *     variable to test.  This works two ways, the variable can be alone, in
	 *     which case it's existance is tested, or it can be followed by a "="
	 *     and a string to compare to.  This is then followed by a text segment
	 *     that will be used if the test is true, and an optional text segment
	 *     to be used if the test is false.
	 *   - ':': command.  The ':' is immediately followed by a command string,
	 *     of which there's only one right now, but that's ok.  These are not
	 *     put into the output stream, but instead mark something for the
	 *     parser.  Currently supported:
	 *     - {:end}: end of parsing, stop here, also make note of how many input
	 *       characters were used.
	 * - Segments:
	 *   - Each segment is seperated by a colon.
	 *   - Filter segments give the name of the filter, followed by
	 *     parenthesies.  Parameters may be provided within the parenthesies.
	 *   - Text segments should always be quoted, but may contain any characters
	 *     within the quotes, backslash is used as per C/ANSI/ISO standard.
	 *     You can also quote any text using [' '] instead of quotes, which
	 *     allows for nested strings.  The [' token is only recognised within
	 *     a macro.
	 *
	 *@verbatim
	 {=name:tolower()}
	 {=name:ccsplit("_"):toupper()}
	 {?name:"name exists and is {=name}"}
	 {?name:"{=name}":"no name!"}
	 {?name="bob":"You're named bob!":"Who are you?  I only know bob..."}
	 @endverbatim
	 */
	class MiniMacro
	{
	public:
		MiniMacro();
		MiniMacro( const StrHash &sVarSrc );
		virtual ~MiniMacro();

		Bu::FString parse( const Bu::FString &sIn );
		void addVar( const Bu::FString &sName, const Bu::FString &sValue );
		bool hasVar( const Bu::FString &sName );
		const Bu::FString &getVar( const Bu::FString &sName );
		const StrHash &getVars();
		int getPosition();

	private:
		const char *sCur;
		Bu::FString parseRepl();
		Bu::FString parseCond();
		Bu::FString parseCmd();
		Bu::FString callFunc(
			const Bu::FString &sIn, const Bu::FString &sFunc );

		StrHash hVars;
		bool bContinue;
		int iLastPos;

	public:
		typedef Bu::List<Bu::FString> StrList;
		class Func
		{
		public:
			Func(){}
			virtual ~Func(){}
			virtual Bu::FString call(
				const Bu::FString &sIn, StrList &lsParam )=0;
		};

		class FuncToUpper : public Func
		{
		public:
			FuncToUpper(){}
			virtual ~FuncToUpper(){}
			virtual Bu::FString call(
				const Bu::FString &sIn, StrList & )
			{
				Bu::FString sOut( sIn );
				sOut.toUpper();
				return sOut;
			}
		};

		class FuncToLower : public Func
		{
		public:
			FuncToLower(){}
			virtual ~FuncToLower(){}
			virtual Bu::FString call(
				const Bu::FString &sIn, StrList & )
			{
				Bu::FString sOut( sIn );
				sOut.toLower();
				return sOut;
			}
		};
		
	private:
		typedef Bu::Hash<Bu::FString,class Func *> FuncHash;
		FuncHash hFuncs;
	};
};

#endif