/**************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *************************************************************/

#include <precomp.h>
#include "preproc.hxx"

// NOT FULLY DEFINED SERVICES
#include <cosv/tpl/tpltools.hxx>
#include "all_toks.hxx"
#include "defdescr.hxx"
#include <tools/tkpchars.hxx>
#include "c_rcode.hxx"

namespace cpp
{

PreProcessor::F_TOKENPROC PreProcessor::aTokProcs[PreProcessor::state_MAX] =
	{
		&PreProcessor::On_plain,
		&PreProcessor::On_expect_macro_bracket_left,
		&PreProcessor::On_expect_macro_param
	};

PreProcessor::PreProcessor()
	:	pCppExplorer(0),
		pSourceText(0),
		pCurValidDefines(0),
		// aTokens,
		eState(plain),
		pCurMacro(0),
		dpCurMacroName(0),
		// aCurMacroParams,
		aCurParamText(60000),
		nBracketInParameterCounter(0)
		// aBlockedMacroNames
{
}

PreProcessor::~PreProcessor()
{
}

void
PreProcessor::AssignPartners( CodeExplorer &      o_rCodeExplorer,
							  CharacterSource &   o_rCharSource,
							  const MacroMap &    i_rCurValidDefines )
{
	pCppExplorer = &o_rCodeExplorer;
	pSourceText = &o_rCharSource;
	pCurValidDefines = &i_rCurValidDefines;
}

void
PreProcessor::Process_Token( cpp::Token & let_drToken )
{
	csv_assert(pCppExplorer != 0); // Implies pSourceText and pCurValidDefines.

	(this->*aTokProcs[eState])(let_drToken);
}

void
PreProcessor::On_plain( cpp::Token & let_drToken )
{
	if ( let_drToken.TypeId() == Tid_Identifier )
	{
		if (CheckForDefine(let_drToken))
			return;
	}

	pCppExplorer->Process_Token(let_drToken);
}

void
PreProcessor::On_expect_macro_bracket_left( cpp::Token & let_drToken )
{
	if ( let_drToken.TypeId() == Tid_Bracket_Left )
	{
		aCurParamText.seekp(0);
		eState = expect_macro_param;
	}
	else
	{
		pCppExplorer->Process_Token(*dpCurMacroName);
		dpCurMacroName = 0;
		pCppExplorer->Process_Token(let_drToken);
		eState = plain;
	}
}

void
PreProcessor::On_expect_macro_param( cpp::Token & let_drToken )
{
	if ( let_drToken.TypeId() == Tid_Bracket_Left )
		nBracketInParameterCounter++;
	else if ( let_drToken.TypeId() == Tid_Bracket_Right )
	{
		if ( nBracketInParameterCounter > 0 )
			nBracketInParameterCounter--;
		else
		{
			if ( NOT csv::no_str(aCurParamText.c_str()) )
			{
				aCurMacroParams.push_back( String(aCurParamText.c_str()) );
			}
			csv_assert( aCurMacroParams.size() == pCurMacro->ParamCount() );

			InterpretMacro();
			eState = plain;
			return;
		}
	}
	else if ( let_drToken.TypeId() == Tid_Comma AND nBracketInParameterCounter == 0 )
	{
		aCurMacroParams.push_back( String (aCurParamText.c_str()) );
		aCurParamText.seekp(0);
		return;
	}

	// KORR_FUTURE:
	// If in future whitespace is parsed also, that should match exactly and the
	// safety spaces, " ", here should be removed.
	aCurParamText << let_drToken.Text() << " ";
}

bool
PreProcessor::CheckForDefine( cpp::Token & let_drToken )
{
	String sTokenText(let_drToken.Text());
 	pCurMacro = csv::value_from_map( *pCurValidDefines, sTokenText );
	if (pCurMacro == 0 )
		return false;
	for ( StringVector::const_iterator it = aBlockedMacroNames.begin();
		  it != aBlockedMacroNames.end();
		  ++it )
	{
		if ( strcmp( (*it).c_str(), let_drToken.Text() ) == 0 )
			return false;
	}

	if ( pCurMacro->DefineType() == DefineDescription::type_define )
	{
		delete &let_drToken;

		aCurParamText.seekp(0);
		pCurMacro->GetDefineText(aCurParamText);

		if ( aCurParamText.tellp() > 1 )
			pSourceText->InsertTextAtCurPos(aCurParamText.c_str());
	}
	else // ( pCurMacro->DefineType() == DefineDescription::type_macro )
	{
		dpCurMacroName = &let_drToken;
		eState = expect_macro_bracket_left;
		csv::erase_container( aCurMacroParams );
		aCurParamText.seekp(0);
		nBracketInParameterCounter = 0;
	} // endif

	return true;
}

void
PreProcessor::UnblockMacro( const char * i_sMacroName )
{
	for ( StringVector::iterator it = aBlockedMacroNames.begin();
		  it != aBlockedMacroNames.end();
		  ++it )
	{
		if ( strcmp( (*it), i_sMacroName ) == 0 )
		{
			aBlockedMacroNames.erase(it);
			break;
		}
	} // end for
}

void
PreProcessor::InterpretMacro()
{
	aCurParamText.seekp(0);
	pCurMacro->GetMacroText(aCurParamText, aCurMacroParams);

	if ( NOT csv::no_str(aCurParamText.c_str()) )
	{
		aCurParamText.seekp(-1, csv::cur);
		aCurParamText << " #unblock-" << dpCurMacroName->Text() << " ";

		pSourceText->InsertTextAtCurPos(aCurParamText.c_str());
		String sCurMacroName(dpCurMacroName->Text());
		aBlockedMacroNames.insert( aBlockedMacroNames.begin(), sCurMacroName );
	}

	delete dpCurMacroName;
	dpCurMacroName = 0;
	pCurMacro = 0;
	csv::erase_container(aCurMacroParams);
	aCurParamText.seekp(0);
}


} // end namespace cpp

/* vim: set noet sw=4 ts=4: */
