#include "flow_basic_operators.h"


RawStrTag::SizeAttribute::SizeAttribute(const RawStrTag& outer)
: TagAttribute(true, false, false),
  outer(outer)
{ }
        
void
RawStrTag::SizeAttribute::writeSingle(ostream& os) const { os << outer.size(); }

const string&
RawStrTag::SizeAttribute::name() const { return ATTRIBUT_NAME; }

RawStrTag::ValueAttribute::ValueAttribute(const RawStrTag& outer)
: TagAttribute(true, false, false),
  outer(outer)
{ }

void
RawStrTag::ValueAttribute::writeSingle(ostream& os) const { os << outer; }

const string&
RawStrTag::ValueAttribute::name() const { return ATTRIBUT_NAME; }


RawStrTag::RawStrTag(const string& name)
: HtmlField(),
  name(name),
  conform_string(),
  sizeAttribute(*this),
  valueAttribute(*this)
{
    defineHeadAttribute(sizeAttribute);
    defineHeadAttribute(valueAttribute);
}
    
RawStrTag::RawStrTag(const string& name, const string& str)
: HtmlField(),
  name(name),
  conform_string(str),
  sizeAttribute(*this),
  valueAttribute(*this)
{
    defineHeadAttribute(sizeAttribute);
    defineHeadAttribute(valueAttribute);
}

RawStrTag::RawStrTag(const string& name, const char*const str)
: HtmlField(),
  name(name),
  conform_string(str),
  sizeAttribute(*this),
  valueAttribute(*this)
{
    defineHeadAttribute(sizeAttribute);
    defineHeadAttribute(valueAttribute);
}

const string&
RawStrTag::tagName() const { return name; }

void
RawStrTag::write(ostream& os) const { startTag.write(os); }



Anchor::Anchor(const AnchorType startValue)
: TagAttribute(true, false, false),
  value(startValue)
{ }

Anchor::operator AnchorType() const { return value; }

const string&
Anchor::name() const { return ATTRIBUT_NAME; }

void
Anchor::writeSingle(ostream& os) const { os << value; }




AnchorCounter::AnchorCounter(const AnchorType startValue)
: Anchor(startValue)
{ }
	
void
AnchorCounter::reset(const AnchorType resetValue) { value = resetValue; }

void
AnchorCounter::add(const AnchorType increment) { value += increment; }

void
AnchorCounter::computeFlowItem(const ichar& data) { if (!is_space(data.c)) ++value; }




TextFlowToStream::TextFlowToStream(ostream& os)
: osPtr(&os)
{ }

void
TextFlowToStream::setOutputStream(ostream& os) { osPtr = &os; }

void
TextFlowToStream::computeFlowItem(const ichar& data) { (*osPtr) << data; }

void
TextFlowToStream::computeFlowItem(const string& data)  { (*osPtr) << data; }
	



StringToCharFlow::StringToCharFlow(CharFlowListener& out)
: out(out)
{ }

void
StringToCharFlow::computeFlowItem(const string& data)
{
	for (string::const_iterator i = data.begin(); i != data.end(); ++i)
		out <<= (*i);
}




CharFlowString::CharFlowString()
: conform_string()
{ }

void
CharFlowString::computeFlowItem(const ichar& data) { push_back(data.c); }

void
CharFlowString::loadString(const string& str)
{
	for (string::const_iterator chr = str.begin(); chr != str.end(); ++chr)
		computeFlowItem(*chr);
}




CharFlowBuffer::CharFlowBuffer()
: RawStrTag(string())
{ }
	
void
CharFlowBuffer::computeFlowItem(const ichar& data) { push_back(data); }




PatternTracker::PatternTracker(const string& pattern, CharFlowListener& nextAgent)
: found(false),
  pipe(pattern.size(), nextAgent),
  localStr(pattern),  // duplication object
  pattern(localStr)   // reference la copie locale
//  nextWaitedChar(pattern.begin())
{
	nextWaitedChar = this->pattern.begin();
}

PatternTracker::PatternTracker(const string*const pattern, CharFlowListener& nextAgent)
: found(false),
  pipe(pattern->size(), nextAgent),
  localStr(),		   // pas de duplication
  pattern(*pattern)	 // reference l'instance externe
// nextWaitedChar(this->pattern.begin())
{
	assert(pattern);  // mais c'est deja trop tard.
	nextWaitedChar = this->pattern.begin();
}
	 
void
PatternTracker::clear()
{
	pipe.clear();
	found = false;
	nextWaitedChar = pattern.begin();
}
	
void 
PatternTracker::flush()
{
	pipe.flush();
	found = false;
	nextWaitedChar = pattern.begin();
}

bool
PatternTracker::full() { return pipe.full(); }
	
bool
PatternTracker::empty() { return pipe.empty(); }
	
ShortNatural
PatternTracker::size() const { return pipe.size(); }
	
ShortNatural
PatternTracker::capacity() const { return pattern.size(); }

void
PatternTracker::setNextAgent(CharFlowListener& nextAgent) { pipe.setNextAgent(nextAgent); }

void
PatternTracker::loadString(const string& str) { pipe.load(str.begin(), str.end()); }

string::size_type
PatternTracker::loadAndCheckString(const string& str) 
{ 
	for (string::size_type i = 0; i < str.length(); ++i) {
		computeFlowItem(str[i]);
		if (found) return i;  // then abort statement.
	}
	return str.npos;
}


bool
PatternTracker::patternFound() const { return found; }

const string&
PatternTracker::getPattern() const { return pattern; }
	
void
PatternTracker::computeFlowItem(const ichar& data)
{
	pipe <<= data;
	if (pipe.size())
	{
		if (data == (*nextWaitedChar))
		{
			found = ((++nextWaitedChar) == pattern.end());
			if (found) nextWaitedChar = pattern.begin();
		} else {
			nextWaitedChar = pattern.begin();
			found = false;
		}
	}
}





Filter::Filter(CharFlowListener& nextAgent)
: nextAgent(&nextAgent)
{ }

Filter::~Filter()
{ }

void
Filter::setNextAgent(CharFlowListener& nextAgent) { this->nextAgent = &nextAgent; }

void
Filter::applyFilter() const { }





AnchoredFilter::AnchoredFilter(CharFlowListener& nextAgent, const string& tagName, ostream& anchoredStream)
: Filter(nextAgent),
  HtmlField(),
  id(++counter),
  anchoredStreamPtr(&anchoredStream),
  definitionTag(),
  operatorType(DEFINED_TYPE, tagName, true, false),
  name(tagName),
  anchor()
{
	defineHeadAttribute(id);
	defineHeadAttribute(anchor);
	definitionTag.defineHeadAttribute(operatorType);
	definitionTag.defineHeadAttribute(id);
}

AnchoredFilter::FilterId::FilterId(const ShortNatural id)
: ShortNaturalAttribute(ATTRIBUT_NAME, id, true, false)
{ } 

void
AnchoredFilter::setAnchoredStream(ostream& anchoredStream) { anchoredStreamPtr = &anchoredStream; }
	
void
AnchoredFilter::writeDefinition(ostream& os)
{
	definitionTag.startTag.write(os);
	writeDefinitionField(os);
	definitionTag.stopTag.write(os);
}

const string&
AnchoredFilter::tagName() const { return name; }

void
AnchoredFilter::write(ostream& os) const { startTag.write(os); }





BasicQuietFilter::BasicQuietFilter(const string& startMark, const string& stopMark,
					               const bool keep, const bool exceptMarks, CharFlowListener& nextAgent)
: Filter(nextAgent),
  startMark(startMark,(keep ? nullCharFlowListener : nextAgent)),
  stopMark(stopMark,(keep ? nextAgent : nullCharFlowListener)),
  keep(keep),
  exceptMarks(exceptMarks),
  state((startMark.empty() ? InMarks : OutMarks))
{ }
	
void
BasicQuietFilter::setNextAgent(CharFlowListener& nextAgent) 
{
	Filter::setNextAgent(nextAgent);
	if (keep) stopMark.setNextAgent(nextAgent);
	else startMark.setNextAgent(nextAgent);
}

void
BasicQuietFilter::flush()
{ 
	stopMark.flush();
	startMark.flush();
	state = (startMark.capacity() == 0 ? InMarks : OutMarks);
}

const BasicQuietFilter::State&
BasicQuietFilter::getState() { return state; }

void
BasicQuietFilter::computeFlowItem(const ichar& data)
{
	switch (state) {
		case OutMarks	:	startMark <<= data;
							if (startMark.patternFound()) {
								state = InMarks;
								if (!keep)
									if (exceptMarks) startMark.flush();
									else startMark.clear();
								else
									if (!exceptMarks) {
										startMark.setNextAgent(stopMark);
										startMark.flush();
										startMark.setNextAgent(nullCharFlowAgent);
									}
							}
							break;

		case InMarks	:	stopMark <<= data;
							if (stopMark.patternFound()) {
								state = OutMarks;
								if (keep)
									if (exceptMarks) stopMark.clear();
									else stopMark.flush();
								else
									if (exceptMarks) {
										stopMark.setNextAgent(startMark);
										stopMark.flush();
										stopMark.setNextAgent(nullCharFlowAgent);
									}
							}
							break;

		default			:	throw domain_error(string("BasicQuietFilter: invalid internal state"));
							break;
	}
}





ExtractAnchoredFilter::ExtractAnchoredFilter(const string& start, 
                                             const string& stop,
                                             const string& subst,
                                             const AnchorType watchdog,
                                             CharFlowListener& nextAgent,
                                             ostream& anchoredStream)
: AnchoredFilter(nextAgent, TAG_NAME, anchoredStream),
  buffer(),
  startStrTag(SUBTAG_START_MARK, start),
  stopStrTag(SUBTAG_STOP_MARK, stop),
//	startMark(&startStrTag, nextAgent),
//	stopMark(&stopStrTag, buffer),
  substStrTag(SUBTAG_SUBST_MARK, subst),
  distanceMax(watchdog),
  state(OutMarks)
{
	// Il faut s'assurer que 'startTag' et 'stopTag' sont construit avant d'invoquer le constructeur
	// des 'PatternTracker', donc cette construction doit etre differee dans le corps de la methode.
	startMark = new PatternTracker(&startStrTag, nextAgent);
	stopMark = new PatternTracker(&stopStrTag, buffer);
	
	defineHeadAttribute(buffer.sizeAttribute);
	defineHeadAttribute(buffer.valueAttribute);
}

ExtractAnchoredFilter::~ExtractAnchoredFilter()
{
	delete startMark;
	delete stopMark;
}
	
void
ExtractAnchoredFilter::setNextAgent(CharFlowListener& nextAgent)
{
	AnchoredFilter::setNextAgent(nextAgent);
	startMark->setNextAgent(nextAgent);
}
	
void
ExtractAnchoredFilter::applyFilter() const 
{
	(*anchoredStreamPtr) << '\n';	    // make a little formatting, and
	startTag.write(*anchoredStreamPtr); // dump data (via SgmlTag ancestor) to anchored stream
}

void 
ExtractAnchoredFilter::writeDefinitionField(ostream& os) const 
{
	startStrTag.startTag.write(os);
	stopStrTag.startTag.write(os);
	substStrTag.startTag.write(os);
}

void
ExtractAnchoredFilter::flush() 
{
	switch (state) {
		case InMarks	:	stopMark->flush();
							for (CharFlowBuffer::const_iterator chr = buffer.begin();
								 chr != buffer.end(); ++chr)
								(*startMark) <<= (*chr);
							buffer.clear();
							cerr << "Warning: stop mark (" << stopStrTag << ") not found!\n";
							break;
		default			:	break;
	}
	startMark->flush();
	state = OutMarks;
	extractedCounter.reset;
}
		
void
ExtractAnchoredFilter::computeFlowItem(const ichar& data) 
{
	switch (state) {
		case OutMarks	:	anchor <<= data;
							(*startMark) <<= data;
							if (startMark->patternFound()) {
								state = InMarks;
								extractedCounter.reset();
							}
							break;

		case InMarks	:	extractedCounter <<= data;
							(*stopMark) <<= data;
							if (stopMark->patternFound()) {
								state = OutMarks;
								applyFilter();
								buffer.clear();
								stopMark->clear();
								startMark->clear();
								startMark->loadString(substStrTag);
								anchor.add(extractedCounter);
							} else {
								if ((distanceMax) && (extractedCounter > distanceMax)) { 
									state = OutMarks;
									stopMark->flush();
									CharFlowBuffer oldBuffer(buffer);
									buffer.clear();
									for (CharFlowBuffer::const_iterator chr = oldBuffer.begin();
										 chr != oldBuffer.end(); ++chr)
										computeFlowItem(*chr);
									oldBuffer.clear();
								}
							}
							break;

		  default		:	THROW(domain_error(string("ExtractAnchoredFilter: invalid internal state")));
							break;
	}
}




TranslateAnchoredFilter::MergeCounter::MergeCounter(const ShortNatural value)
: ShortNaturalAttribute(ATTRIBUT_NAME, value, true, true)
{ }

bool
TranslateAnchoredFilter::MergeCounter::isDefault() const { return (attribute == 0); }

void
TranslateAnchoredFilter::MergeCounter::reset() { attribute = 0; }

void
TranslateAnchoredFilter::MergeCounter::add(const ShortNatural value) { attribute += value; }



TranslateAnchoredFilter::TranslateAnchoredFilter(const string original,
                                                 const string translated,
                                                 const string mergeWith,
                                                 CharFlowListener& nextAgent,
                                                 ostream& anchoredStream)
: AnchoredFilter(nextAgent, TAG_NAME, anchoredStream),
  originalStrTag(SUBTAG_ORIG_MARK, original),
  translatedStrTag(SUBTAG_SUBST_MARK, translated),
  mergedStrTag(SUBTAG_MERGE_MARK, mergeWith),
//	originalSequence(&originalStrTag, nextAgent),
//	mergedSequence(&mergedStrTag, nextAgent),
  mergeContiguous(!mergeWith.empty()),
  mergeCounter(0),
  swappedCounter(0),
  state(WaitMark)
{
	originalSequence = new PatternTracker(&originalStrTag, nextAgent);
	mergedSequence = new PatternTracker(&mergedStrTag, nextAgent);
	defineHeadAttribute(mergeCounter);
}

TranslateAnchoredFilter::~TranslateAnchoredFilter()
{
	delete originalSequence;
	delete mergedSequence;
}
	
void
TranslateAnchoredFilter::setNextAgent(CharFlowListener& nextAgent)
{
	AnchoredFilter::setNextAgent(nextAgent);
	originalSequence->setNextAgent(nextAgent);
	mergedSequence->setNextAgent(nextAgent);
}
	
void
TranslateAnchoredFilter::applyFilter() const 
{
	(*anchoredStreamPtr) << '\n';
	startTag.write(*anchoredStreamPtr);
}

void
TranslateAnchoredFilter::writeDefinitionField(ostream& os) const 
{
	originalStrTag.startTag.write(os);
	mergedStrTag.startTag.write(os);
	translatedStrTag.startTag.write(os);
}

void 
TranslateAnchoredFilter::flush()
{
	if (state == MergeMarks) {
		state = WaitMark;
		applyFilter();
		mergeCounter.reset();
		anchor.add(swappedCounter);
		swappedCounter.reset;
		originalSequence->loadString(translatedStrTag);
		CharFlowBuffer buffer;
		mergedSequence->setNextAgent(buffer);
		mergedSequence->flush();
		mergedSequence->setNextAgent(*nextAgent);
		for (CharFlowBuffer::const_iterator chr = buffer.begin(); chr != buffer.end(); ++chr)
			computeFlowItem(*chr);
		buffer.clear();
	}
	originalSequence->flush();
}

void 
TranslateAnchoredFilter::computeFlowItem(const ichar& data)
{
	switch (state) {
		case	MergeMarks	:	swappedCounter <<= data;
								(*mergedSequence) <<= data;
								if (mergedSequence->patternFound()) {
									assert(mergedSequence->full());
									mergeCounter.add(1);
									mergedSequence->clear();
								} else {
									if (mergedSequence->full()) {
										state = WaitMark;
										applyFilter();
										mergeCounter.reset();
										anchor.add(swappedCounter);
										swappedCounter.reset();
										originalSequence->loadString(translatedStrTag);
										CharFlowBuffer buffer;
										mergedSequence->setNextAgent(buffer);
										mergedSequence->flush();
										mergedSequence->setNextAgent(*nextAgent);
										for (CharFlowBuffer::const_iterator chr = buffer.begin();
											 chr != buffer.end(); ++chr)
											computeFlowItem(*chr);
										buffer.clear();
									}
								}
								break;

		case	WaitMark	:	anchor <<= data;
								(*originalSequence) <<= data;
								if (originalSequence->patternFound()) {
									originalSequence->clear();
									mergeCounter.reset();
									if (! mergeContiguous) {
										applyFilter();
										originalSequence->loadString(translatedStrTag);
									}
									else state = MergeMarks;
								}
								break;

		default				:	throw domain_error(string("TranslateAnchoredFilter: invalid internal state"));
								break;
	}
}




QuietTranslateFilter::QuietTranslateFilter(const string original,
                                           const string translated,
                                           const string mergeWith,
                                           CharFlowListener& nextAgent)
: Filter(nextAgent),
  translated(translated),
  originalSequence(original, nextAgent),
  mergedSequence(mergeWith, nextAgent),
  mergeContiguous(!mergeWith.empty()),
  state(WaitMark)
{ }
	
void
QuietTranslateFilter::setNextAgent(CharFlowListener& nextAgent)
{
	Filter::setNextAgent(nextAgent);
	originalSequence.setNextAgent(nextAgent);
	mergedSequence.setNextAgent(nextAgent);
}
	
void 
QuietTranslateFilter::flush()
{
	if (state == MergeMarks) {
		state = WaitMark;
		originalSequence.loadString(translated);
		CharFlowBuffer buffer;
		mergedSequence.setNextAgent(buffer);
		mergedSequence.flush();
		mergedSequence.setNextAgent(*nextAgent);
		for (CharFlowBuffer::const_iterator chr = buffer.begin(); chr != buffer.end(); ++chr)
			computeFlowItem(*chr);
		buffer.clear();
	}
	originalSequence.flush();
}

void
QuietTranslateFilter::computeFlowItem(const ichar& data)
{
	switch (state) {
		case	MergeMarks	:	mergedSequence <<= data;
								if (mergedSequence.patternFound()) {
									assert(mergedSequence.full());
									mergedSequence.clear();
								} else {
									if (mergedSequence.full()) {
										state = WaitMark;
										originalSequence.loadString(translated);
										CharFlowBuffer buffer;
										mergedSequence.setNextAgent(buffer);
										mergedSequence.flush();
										mergedSequence.setNextAgent(*nextAgent);
										for (CharFlowBuffer::const_iterator chr = buffer.begin();
											 chr != buffer.end(); ++chr)
											computeFlowItem(*chr);
										buffer.clear();
									}
								}
								break;

		case	WaitMark	:	originalSequence <<= data;
								if (originalSequence.patternFound()) {
									originalSequence.clear();
									if (! mergeContiguous) {
										originalSequence.loadString(translated);
									}
									else state = MergeMarks;
								}
								break;

		default				:	throw domain_error(string("QuietTranslateFilter: invalid internal state"));
								break;
	}
}





QuietSpecificExtractFilter::QuietSpecificExtractFilter(const string& start,
                                                       const string& stop,
                                                       const string& subst,
                                                       const string& except,
                                                       CharFlowListener& nextAgent)
: Filter(nextAgent),
  buffer(),
  startMark(start, nextAgent),
  stopMark(stop, buffer),
  exceptMark(except, stopMark),
  subst(subst),
  state(OutMarks)
{ }

void
QuietSpecificExtractFilter::setNextAgent(CharFlowListener& nextAgent)
{
	Filter::setNextAgent(nextAgent);
	startMark.setNextAgent(nextAgent);
}

void
QuietSpecificExtractFilter::flush() 
{
	switch (state) {
		case Perhaps	:   exceptMark.flush();
							
		case InMarks	:	stopMark.flush();
							for (CharFlowBuffer::const_iterator chr = buffer.begin();
								 chr != buffer.end(); ++chr)
								startMark <<= (*chr);
							buffer.clear();
							cerr << "Warning: stop mark (" << stopMark.getPattern() << ") not found!\n";
							break;
		default			:	break;
	}
	startMark.flush();
	state = OutMarks;
}

void
QuietSpecificExtractFilter::computeFlowItem(const ichar& data) 
{
	switch (state) {
		case OutMarks	:	startMark <<= data;
							if (startMark.patternFound()) {
								state = Perhaps;
							}
							break;
							
		case Perhaps	:   exceptMark <<= data;
							if (exceptMark.full()) {
								if (exceptMark.patternFound()) {
									exceptMark.setNextAgent(*nextAgent);
									startMark.flush();
									exceptMark.flush();
									exceptMark.setNextAgent(stopMark);
									state = OutMarks;
								} else {
									state = InMarks;
									exceptMark.flush();
								}
							}
							break;

		case InMarks	:	stopMark <<= data;
							if (stopMark.patternFound()) {
								state = OutMarks;
								buffer.clear();
								stopMark.clear();
								startMark.clear();
								exceptMark.clear();
								startMark.loadString(subst);
							} 
							break;

		  default		:	throw domain_error(string("ExtractAnchoredFilter: invalid internal state"));
							  break;
	}
}

