/* "CodeWorker":	a scripting language for parsing and generating text.

Copyright (C) 1996-1997, 1999-2002 Cédric Lemaire

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library 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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

To contact the author: codeworker@free.fr
*/

#ifdef WIN32
#pragma warning (disable : 4786)
#endif

#include "DtaScriptVariable.h"
#include "ExprScriptVariable.h"
#include "ScpStream.h"
#include "CppCompilerEnvironment.h"
#include "GrfForeach.h"

namespace CodeWorker {
	GrfForeach::~GrfForeach() {
		delete _pIndexExpr;
		delete _pListExpr;
	}

	void GrfForeach::setSortedNoCase(bool bSortedNoCase) {
		if (bSortedNoCase) _bSorted = true;
		_bSortedNoCase = bSortedNoCase;
	}

	void GrfForeach::setReverse(bool bReverse) {
		_bReverse = bReverse;
	}

	SEQUENCE_INTERRUPTION_LIST GrfForeach::executeInternal(DtaScriptVariable& visibility) {
		SEQUENCE_INTERRUPTION_LIST result = NO_INTERRUPTION;
		DtaScriptVariable* pVariable = visibility.getExistingVariable(*_pListExpr);
		if (pVariable != 0L) {
			const std::list<DtaScriptVariable*>* pArray = pVariable->getArray();
			if ((pArray != 0L) && !pArray->empty()) {
				DtaScriptVariable stackForeach(&visibility, "##stack## foreach");
				DtaScriptIndex* pIndex = stackForeach.createIndex(_pIndexExpr->getName().c_str());
				pIndex->setForeach(this);
				if (_iCascading != NO_CASCADING) _sCascadedName = pVariable->getName();
				if (_bSorted) {
					if (_bSortedNoCase) {
						if (_bReverse) result = executeReverseSortedIndirectForeach(*pIndex, *pVariable, stackForeach);
						else result = executeSortedIndirectForeach(*pIndex, *pVariable, stackForeach);
					} else {
						if (_bSortOnValue) result = executeSortedIndirectForeach(*pIndex, *pVariable, stackForeach);
						else if (_bReverse) result = executeReverseSortedForeach(*pIndex, *pVariable, stackForeach);
						else result = executeSortedForeach(*pIndex, *pVariable, stackForeach);
					}
				} else {
					if (_bReverse) result = executeReverseForeach(*pIndex, *pVariable, stackForeach);
					else result = executeForeach(*pIndex, *pVariable, stackForeach);
				}
				if (result == BREAK_INTERRUPTION) result = NO_INTERRUPTION;
			}
		}
		return result;
	}

	SEQUENCE_INTERRUPTION_LIST GrfForeach::executeForeach(DtaScriptIndex& theIndex, DtaScriptVariable& theVariable, DtaScriptVariable& stackForeach) {
		SEQUENCE_INTERRUPTION_LIST result;
		const std::list<DtaScriptVariable*>* list = theVariable.getArray();
		if (list == 0L) return NO_INTERRUPTION;
		int iOldArraySize = _iArraySize;
		_iArraySize = list->size();
		int iOldPosition = _iCurrentPosition;
		_iCurrentPosition = 0;
		std::string sOldKey = _sKey;
		for (std::list<DtaScriptVariable*>::const_iterator i = list->begin(); i != list->end(); i++) {
			if (_iCascading == CASCADING_FIRST) {
				DtaScriptVariable* pNext = (*i)->getNode(_sCascadedName.c_str());
				if (pNext != 0L) {
					result = executeForeach(theIndex, *pNext, stackForeach);
					if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
				}
			}
			theIndex.setValue((*i));
			_sKey = (*i)->getName();
			result = GrfBlock::executeInternal(stackForeach);
			if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
			if (_iCascading == CASCADING_LAST) {
				DtaScriptVariable* pNext = (*i)->getNode(_sCascadedName.c_str());
				if (pNext != 0L) {
					result = executeForeach(theIndex, *pNext, stackForeach);
					if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
				}
			}
			_iCurrentPosition++;
		}
		_iCurrentPosition = iOldPosition;
		_iArraySize = iOldArraySize;
		_sKey = sOldKey;
		return NO_INTERRUPTION;
	}

	SEQUENCE_INTERRUPTION_LIST GrfForeach::executeReverseForeach(DtaScriptIndex& theIndex, DtaScriptVariable& theVariable, DtaScriptVariable& stackForeach) {
		SEQUENCE_INTERRUPTION_LIST result;
		const std::list<DtaScriptVariable*>* list = theVariable.getArray();
		if (list == 0L) return NO_INTERRUPTION;
		int iOldArraySize = _iArraySize;
		_iArraySize = list->size();
		int iOldPosition = _iCurrentPosition;
		_iCurrentPosition = 0;
		std::string sOldKey = _sKey;
		for (std::list<DtaScriptVariable*>::const_reverse_iterator i = list->rbegin(); i != list->rend(); i++) {
			if (_iCascading == CASCADING_FIRST) {
				DtaScriptVariable* pNext = (*i)->getNode(_sCascadedName.c_str());
				if (pNext != 0L) {
					result = executeForeach(theIndex, *pNext, stackForeach);
					if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
				}
			}
			theIndex.setValue((*i));
			_sKey = (*i)->getName();
			result = GrfBlock::executeInternal(stackForeach);
			if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
			if (_iCascading == CASCADING_LAST) {
				DtaScriptVariable* pNext = (*i)->getNode(_sCascadedName.c_str());
				if (pNext != 0L) {
					result = executeForeach(theIndex, *pNext, stackForeach);
					if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
				}
			}
			_iCurrentPosition++;
		}
		_iCurrentPosition = iOldPosition;
		_iArraySize = iOldArraySize;
		_sKey = sOldKey;
		return NO_INTERRUPTION;
	}

	SEQUENCE_INTERRUPTION_LIST GrfForeach::executeSortedForeach(DtaScriptIndex& theIndex, DtaScriptVariable& theVariable, DtaScriptVariable& stackForeach) {
		SEQUENCE_INTERRUPTION_LIST result;
		const std::map<std::string, DtaScriptVariable*>* mapOfElements = theVariable.getSortedArray();
		if (mapOfElements == 0L) return NO_INTERRUPTION;
		int iOldArraySize = _iArraySize;
		_iArraySize = mapOfElements->size();
		int iOldPosition = _iCurrentPosition;
		_iCurrentPosition = 0;
		std::string sOldKey = _sKey;
		for (std::map<std::string, DtaScriptVariable*>::const_iterator i = mapOfElements->begin(); i != mapOfElements->end(); i++) {
			if (_iCascading == CASCADING_FIRST) {
				DtaScriptVariable* pNext = i->second->getNode(_sCascadedName.c_str());
				if (pNext != 0L) {
					result = executeSortedForeach(theIndex, *pNext, stackForeach);
					if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
				}
			}
			theIndex.setValue(i->second);
			_sKey = i->second->getName();
			result = GrfBlock::executeInternal(stackForeach);
			if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
			if (_iCascading == CASCADING_LAST) {
				DtaScriptVariable* pNext = i->second->getNode(_sCascadedName.c_str());
				if (pNext != 0L) {
					result = executeSortedForeach(theIndex, *pNext, stackForeach);
					if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
				}
			}
			_iCurrentPosition++;
		}
		_sKey = sOldKey;
		_iCurrentPosition = iOldPosition;
		_iArraySize = iOldArraySize;
		return NO_INTERRUPTION;
	}

	SEQUENCE_INTERRUPTION_LIST GrfForeach::executeSortedIndirectForeach(DtaScriptIndex& theIndex, DtaScriptVariable& theVariable, DtaScriptVariable& stackForeach) {
		SEQUENCE_INTERRUPTION_LIST result;
		std::auto_ptr<std::vector<DtaScriptVariable*> > listOfElements;
		if (_bSortedNoCase) {
			if (_bSortOnValue) {
				listOfElements = std::auto_ptr<std::vector<DtaScriptVariable*> >(theVariable.getSortedNoCaseArrayOnValue());
			} else {
				listOfElements = std::auto_ptr<std::vector<DtaScriptVariable*> >(theVariable.getSortedNoCaseArray());
			}
		} else if (_bSortOnValue) {
			listOfElements = std::auto_ptr<std::vector<DtaScriptVariable*> >(theVariable.getSortedArrayOnValue());
		}
		int iOldArraySize = _iArraySize;
		_iArraySize = listOfElements->size();
		int iOldPosition = _iCurrentPosition;
		_iCurrentPosition = 0;
		std::string sOldKey = _sKey;
		for (std::vector<DtaScriptVariable*>::const_iterator i = listOfElements->begin(); i != listOfElements->end(); i++) {
			if (_iCascading == CASCADING_FIRST) {
				DtaScriptVariable* pNext = (*i)->getNode(_sCascadedName.c_str());
				if (pNext != 0L) {
					result = executeSortedIndirectForeach(theIndex, *pNext, stackForeach);
					if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
				}
			}
			theIndex.setValue(*i);
			_sKey = (*i)->getName();
			result = GrfBlock::executeInternal(stackForeach);
			if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
			if (_iCascading == CASCADING_LAST) {
				DtaScriptVariable* pNext = (*i)->getNode(_sCascadedName.c_str());
				if (pNext != 0L) {
					result = executeSortedIndirectForeach(theIndex, *pNext, stackForeach);
					if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
				}
			}
			_iCurrentPosition++;
		}
		_sKey = sOldKey;
		_iCurrentPosition = iOldPosition;
		_iArraySize = iOldArraySize;
		return NO_INTERRUPTION;
	}

	SEQUENCE_INTERRUPTION_LIST GrfForeach::executeReverseSortedForeach(DtaScriptIndex& theIndex, DtaScriptVariable& theVariable, DtaScriptVariable& stackForeach) {
		SEQUENCE_INTERRUPTION_LIST result;
		const std::map<std::string, DtaScriptVariable*>* mapOfElements = theVariable.getSortedArray();
		if (mapOfElements == 0L) return NO_INTERRUPTION;
		int iOldArraySize = _iArraySize;
		_iArraySize = mapOfElements->size();
		int iOldPosition = _iCurrentPosition;
		_iCurrentPosition = 0;
		std::string sOldKey = _sKey;
		for (std::map<std::string, DtaScriptVariable*>::const_reverse_iterator i = mapOfElements->rbegin(); i != mapOfElements->rend(); i++) {
			if (_iCascading == CASCADING_FIRST) {
				DtaScriptVariable* pNext = i->second->getNode(_sCascadedName.c_str());
				if (pNext != 0L) {
					result = executeSortedForeach(theIndex, *pNext, stackForeach);
					if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
				}
			}
			theIndex.setValue(i->second);
			_sKey = i->second->getName();
			result = GrfBlock::executeInternal(stackForeach);
			if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
			if (_iCascading == CASCADING_LAST) {
				DtaScriptVariable* pNext = i->second->getNode(_sCascadedName.c_str());
				if (pNext != 0L) {
					result = executeSortedForeach(theIndex, *pNext, stackForeach);
					if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
				}
			}
			_iCurrentPosition++;
		}
		_sKey = sOldKey;
		_iCurrentPosition = iOldPosition;
		_iArraySize = iOldArraySize;
		return NO_INTERRUPTION;
	}

	SEQUENCE_INTERRUPTION_LIST GrfForeach::executeReverseSortedIndirectForeach(DtaScriptIndex& theIndex, DtaScriptVariable& theVariable, DtaScriptVariable& stackForeach) {
		SEQUENCE_INTERRUPTION_LIST result;
		std::auto_ptr<std::vector<DtaScriptVariable*> > listOfElements;
		if (_bSortedNoCase) {
			if (_bSortOnValue) {
				listOfElements = std::auto_ptr<std::vector<DtaScriptVariable*> >(theVariable.getSortedNoCaseArrayOnValue());
			} else {
				listOfElements = std::auto_ptr<std::vector<DtaScriptVariable*> >(theVariable.getSortedNoCaseArray());
			}
		} else if (_bSortOnValue) {
			listOfElements = std::auto_ptr<std::vector<DtaScriptVariable*> >(theVariable.getSortedArrayOnValue());
		}
		int iOldArraySize = _iArraySize;
		_iArraySize = listOfElements->size();
		int iOldPosition = _iCurrentPosition;
		_iCurrentPosition = 0;
		std::string sOldKey = _sKey;
		for (std::vector<DtaScriptVariable*>::reverse_iterator i = listOfElements->rbegin(); i != listOfElements->rend(); i++) {
			if (_iCascading == CASCADING_FIRST) {
				DtaScriptVariable* pNext = (*i)->getNode(_sCascadedName.c_str());
				if (pNext != 0L) {
					result = executeReverseSortedIndirectForeach(theIndex, *pNext, stackForeach);
					if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
				}
			}
			theIndex.setValue(*i);
			_sKey = (*i)->getName();
			result = GrfBlock::executeInternal(stackForeach);
			if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
			if (_iCascading == CASCADING_LAST) {
				DtaScriptVariable* pNext = (*i)->getNode(_sCascadedName.c_str());
				if (pNext != 0L) {
					result = executeReverseSortedIndirectForeach(theIndex, *pNext, stackForeach);
					if ((result != CONTINUE_INTERRUPTION) && (result != NO_INTERRUPTION)) return result;
				}
			}
			_iCurrentPosition++;
		}
		_sKey = sOldKey;
		_iCurrentPosition = iOldPosition;
		_iArraySize = iOldArraySize;
		return NO_INTERRUPTION;
	}

	void GrfForeach::compileCpp(CppCompilerEnvironment& theCompilerEnvironment) const {
		std::string sIndex = _pIndexExpr->getName();
		std::string sIndex2Cpp = theCompilerEnvironment.convertToCppVariable(sIndex);
		theCompilerEnvironment.addVariable(sIndex);
		theCompilerEnvironment.pushForeach(const_cast<GrfForeach*>(this));
		if (_bSorted) {
			if (_bSortedNoCase) {
				if (theCompilerEnvironment.addVariable("_compilerMapSortedNoCaseForeach_" + sIndex)) {
					CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "std::vector<DtaScriptVariable*>::const_iterator _compilerIndexSortedNoCaseForeach_" << sIndex << ";";
					CW_BODY_STREAM.endl();
					CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "std::auto_ptr<std::vector<DtaScriptVariable*> > _compilerMapSortedNoCaseForeach_" << sIndex << "(";
					_pListExpr->compileCpp(theCompilerEnvironment);
					CW_BODY_STREAM << ".getSortedNoCaseArray());";
					CW_BODY_STREAM.endl();
				} else {
					CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "_compilerMapSortedNoCaseForeach_" << sIndex << " = ";
					_pListExpr->compileCpp(theCompilerEnvironment);
					CW_BODY_STREAM << ".getSortedNoCaseArray();";
					CW_BODY_STREAM.endl();
				}
				CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "for (_compilerIndexSortedNoCaseForeach_" << sIndex << " = _compilerMapSortedNoCaseForeach_" << sIndex << "->begin(); _compilerIndexSortedNoCaseForeach_" << sIndex << " != _compilerMapSortedNoCaseForeach_" << sIndex << "->end(); ++_compilerIndexSortedNoCaseForeach_" << sIndex << ") {";
				CW_BODY_STREAM.endl();
				theCompilerEnvironment.incrementIndentation();
				CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "CppParsingTree_var " << sIndex2Cpp << "(*_compilerIndexSortedNoCaseForeach_" << sIndex << ");";
				CW_BODY_STREAM.endl();
				CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "std::string _compilerKeyForeach_" << sIndex << "(" << sIndex2Cpp << ".getName());";
				CW_BODY_STREAM.endl();
			} else {
				if (theCompilerEnvironment.addVariable("_compilerMapSortedForeach_" + sIndex)) {
					CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "std::map<std::string, DtaScriptVariable*>::const_iterator _compilerIndexSortedForeach_" << sIndex << ";";
					CW_BODY_STREAM.endl();
					CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "const std::map<std::string, DtaScriptVariable*>* _compilerMapSortedForeach_" << sIndex << " = ";
					_pListExpr->compileCpp(theCompilerEnvironment);
					CW_BODY_STREAM << ".getSortedArray();";
					CW_BODY_STREAM.endl();
				} else {
					CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "_compilerMapSortedForeach_" << sIndex << " = ";
					_pListExpr->compileCpp(theCompilerEnvironment);
					CW_BODY_STREAM << ".getSortedArray()";
					CW_BODY_STREAM.endl();
				}
				CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "if (_compilerMapSortedForeach_" << sIndex << " != 0) for (_compilerIndexSortedForeach_" << sIndex << " = _compilerMapSortedForeach_" << sIndex << "->begin(); _compilerIndexSortedForeach_" << sIndex << " != _compilerMapSortedForeach_" << sIndex << "->end(); ++_compilerIndexSortedForeach_" << sIndex << ") {";
				CW_BODY_STREAM.endl();
				theCompilerEnvironment.incrementIndentation();
				CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "CppParsingTree_var " << sIndex2Cpp << "(_compilerIndexSortedForeach_" << sIndex << "->second);";
				CW_BODY_STREAM.endl();
				CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "std::string _compilerKeyForeach_" << sIndex << "(_compilerIndexSortedForeach_" << sIndex << "->first);";
				CW_BODY_STREAM.endl();
			}
		} else {
			if (theCompilerEnvironment.addVariable("_compilerMapForeach_" + sIndex)) {
				CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "std::list<DtaScriptVariable*>::const_iterator _compilerIndexForeach_" << sIndex << ";";
				CW_BODY_STREAM.endl();
				CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "const std::list<DtaScriptVariable*>* _compilerMapForeach_" << sIndex << " = ";
				_pListExpr->compileCpp(theCompilerEnvironment);
				CW_BODY_STREAM << ".getArray();";
				CW_BODY_STREAM.endl();
			} else {
				CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "_compilerMapForeach_" << sIndex << " = ";
				_pListExpr->compileCpp(theCompilerEnvironment);
				CW_BODY_STREAM << ".getArray();";
				CW_BODY_STREAM.endl();
			}
			CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "if (_compilerMapForeach_" << sIndex << " != 0) for (_compilerIndexForeach_" << sIndex << " = _compilerMapForeach_" << sIndex << "->begin(); _compilerIndexForeach_" << sIndex << " != _compilerMapForeach_" << sIndex << "->end(); ++_compilerIndexForeach_" << sIndex << ") {";
			CW_BODY_STREAM.endl();
			theCompilerEnvironment.incrementIndentation();
			CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "CppParsingTree_var " << sIndex2Cpp << "(*_compilerIndexForeach_" << sIndex << ");";
			CW_BODY_STREAM.endl();
			CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "std::string _compilerKeyForeach_" << sIndex << "(" << sIndex2Cpp << ".getName());";
			CW_BODY_STREAM.endl();
		}
		theCompilerEnvironment.bracketsToNextBlock(false);
		GrfBlock::compileCpp(theCompilerEnvironment);
		theCompilerEnvironment.decrementIndentation();
		CW_BODY_STREAM << theCompilerEnvironment.getIndentation() << "}";
		CW_BODY_STREAM.endl();
		theCompilerEnvironment.popForeach();
	}
}
