/******************************** LICENSE ********************************

 Copyright 2007 European Centre for Medium-Range Weather Forecasts (ECMWF)

 Licensed 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.

 ******************************** LICENSE ********************************/

/*! \file TextVisitor.cc
    \brief Implementation of the Template class TextVisitor.
    
    Magics Team - ECMWF 2007
    
    Started: Tue 6-Mar-2007
    
    Changes:
    
*/



#include "TextVisitor.h"
#include "Text.h"
#include "Dimension.h"
#include "XmlReader.h"
#include "AnimationRules.h"
#include "Layer.h"
#include "MagicsFormat.h"

#include "MetaData.h"

using namespace magics;

/*!
 \brief Method to relate C++ special characters with XML ones
 
 \todo write one code to do so 
 
 \sa PostScriptDriver.cc
*/

map<string, string> TextVisitor::tags_;

string special(const string& sp)
{
	static map<string, string> specials;
	if ( specials.empty() )
	{
		// http://www.w3.org/TR/REC-html40/sgml/entities.html
		specials["#251"] = "&#169;"; //copyright
		specials["#260"] = "&#176;"; //degres
		specials["#265"] = "&#956;"; //mu
		specials["#274"] = "&#188;"; //vulgar fraction one quarter
		specials["#275"] = "&#189;"; //vulgar fraction one half
		specials["#276"] = "&#190;"; //vulgar fraction three quarters
		specials["#277"] = "&#191;"; //inverted question mark
		specials["#300"] = "&#192;"; //latin capital letter A with grave
		specials["#301"] = "&#193;"; //latin capital letter A with acute
		
		specials["#303"] = "&#195;"; //latin capital letter A with tilde
		specials["#304"] = "&#196;"; //latin capital letter A with  diaeresis
		specials["#305"] = "&#197;"; //latin capital letter A with ring above
		specials["#306"] = "&#198;"; //latin capital letter AE
		specials["#307"] = "&#199;"; //latin capital letter C with cedilla
		
		specials["#310"] = "&#200;"; //latin capital letter E with grave
		specials["#311"] = "&#201;"; //latin capital letter E with acute
		specials["#312"] = "&#202;"; //latin capital letter E with circumflex
		specials["#313"] = "&#203;"; //latin capital letter E with  diaeresis
		
		specials["#314"] = "&#204;"; //latin capital letter I with grave
		specials["#315"] = "&#205;"; //latin capital letter I with acute
		specials["#316"] = "&#206;"; //latin capital letter I with circumflex
		specials["#317"] = "&#207;"; //latin capital letter I with  diaeresis
		
		specials["#321"] = "&#209;"; //latin capital letter N with tilde
		
		specials["#322"] = "&#210;"; //latin capital letter O with grave
		specials["#323"] = "&#211;"; //latin capital letter O with acute
		specials["#325"] = "&#214;"; //latin capital letter O with  diaeresis
		specials["#330"] = "&#216;"; //latin capital letter O slash
		
		specials["#331"] = "&#217;"; //latin capital letter U with grave
		specials["#332"] = "&#218;"; //latin capital letter U with acute
		specials["#333"] = "&#219;"; //latin capital letter U with circumflex
		specials["#334"] = "&#220;"; //latin capital letter U with  diaeresis
		
		specials["#335"] = "&#221;"; //latin capital letter Y with acute
		
		specials["#336"] = "&#222;"; //latin capital letter THORN
		specials["#337"] = "&#223;"; //latin small letter sharp s = ess-zed
		
		specials["#340"] = "&#224;"; //latin small letter a with grave
		specials["#341"] = "&#225;"; //latin small letter a with cute
		specials["#342"] = "&#226;"; //latin small letter a with circumflex
		specials["#343"] = "&#227;"; //latin small letter a with tilde
		specials["#344"] = "&#228;"; //latin small letter a with  diaeresis
		specials["#345"] = "&#229;"; //latin small letter a with ring above
		specials["#346"] = "&#230;"; //latin small letter ae
		specials["#347"] = "&#231;"; //latin small letter c with cedilla
		
		specials["#350"] = "&#232;"; //latin small letter e with grave
		specials["#351"] = "&#233;"; //latin small letter e with cute
		specials["#352"] = "&#234;"; //latin small letter e with circumflex
		specials["#353"] = "&#235;"; //latin small letter e with ring above
		
		specials["#354"] = "&#236;"; //latin small letter i with grave
		specials["#355"] = "&#237;"; //latin small letter i with cute
		specials["#356"] = "&#238;"; //latin small letter i with circumflex
		specials["#357"] = "&#239;"; //latin small letter i with diaeresis
		
		specials["#361"] = "&#241;"; //latin small letter n with tilde
		
		specials["#362"] = "&#242;"; //latin small letter o with grave
		specials["#363"] = "&#243;"; //latin small letter o with cute
		specials["#364"] = "&#244;"; //latin small letter o with diaeresis
		specials["#365"] = "&#245;"; //latin small letter o with tilde
		
		specials["#370"] = "&#248;"; //latin small letter o slash
		
		specials["#371"] = "&#249;"; //latin small letter u with grave
		specials["#372"] = "&#250;"; //latin small letter u with cute
		specials["#373"] = "&#251;"; //latin small letter u with circumflex
		specials["#374"] = "&#252;"; //latin small letter u with diaeresis
		
		specials["#375"] = "&#253;"; //latin small letter y with acute
		
		specials["#376"] = "&#254;"; //latin small letter THORN
		specials["#377"] = "&#255;"; //latin small letter y with diaeresis		
	}

	const map<string, string>::iterator f = specials.find(sp);	
	return( f == specials.end() ) ? "&" + sp +";" : f->second;
}


class ConvertHelper 
{
public:
	ConvertHelper() { 
		if ( !list_ ) list_ = new vector<ConvertHelper* >;
		list_->push_back(this); 
	} 
	virtual ~ConvertHelper() {};
	virtual bool operator()(vector<string>::const_iterator&, vector<string>&, ostringstream&) const 
		{ return false; } 
	

	static void convert(vector<string>::const_iterator& token, vector<string>& tokens, ostringstream& out) {	
	    bool basic = true;
	    if ( token == tokens.end() ) return;
		for ( vector<ConvertHelper* >::const_iterator helper = list_->begin(); helper != list_->end(); ++helper) 
			    if ( (**helper)(token, tokens,  out) ) {
			    	basic = false;
			    	break;
			    }
			if ( basic ) out << *token;
	}
	
	static string convert(const string& in) {
		if ( in.empty() ) return in;
		// First interpret the > and < ...
		string casein;
		enum tc  { NOTHING, LOWER, UPPER };
		tc style = NOTHING;
	
		for ( string::const_iterator c = in.begin(); c != in.end(); ++c) {
			if ( *c == '>' ) {
				style = LOWER;
				continue;
			}
			if ( *c == '<' ) {
				style = UPPER;
				continue;
			}
			if (*c == '#') { 
				string x;
				x.push_back(*c);
				++c;
				x.push_back(*c);
				++c;
				x.push_back(*c);
				++c;
				x.push_back(*c);
			
				//MagLog::dev()<< "special character-> " << x << endl;
				
				casein = casein + special(x);
				
				continue;
			}
			switch (style) {
				case NOTHING: casein.push_back(*c); break;
				case UPPER: casein.push_back(toupper(*c)); break;
				case LOWER: casein.push_back(tolower(*c)); break;
			}
		}
		
		//MagLog::dev()<< in << "--->" << casein << endl;

		Tokenizer tool("\\");
		vector<string> tokens;
		tool(casein, tokens);

		if ( tokens.size() == 1 ) return casein;

		ostringstream out;

		for ( vector<string>::const_iterator token = tokens.begin(); token != tokens.end(); ++token)
		{	
			convert(token, tokens, out);
		}
		//MagLog::dev()<< "In-->" << in << endl;
		//MagLog::dev()<< "Out-->" << out.str() << endl;
		return out.str();
    }
    static vector<ConvertHelper* >* list_;
};

class HeightHelper :public ConvertHelper
{
public:
	HeightHelper() { }
	~HeightHelper() {};
	bool operator()(vector<string>::const_iterator& token, 
							vector<string>& tokens, ostringstream& out) const 
	{
		string hgr = token->substr(0,3);
		string hg = token->substr(0,2);
		if ( magCompare(hgr, "hgr") ) {
		
			return true;
		}
		if ( magCompare(hg, "hg") ) {
			float size;
			try {
				istringstream h(token->substr(2));
				h >> size;
				++token;
				out << "<font size='" << size << "'>";
				convert(token, tokens, out);
				out << "</font>";
				return true;
			}
			catch (...) {}
		}  
		return false;
		
	}
};

class ColourHelper : public ConvertHelper
{
public:
	ColourHelper() {}
	~ColourHelper() {}
	bool operator()(vector<string>::const_iterator& token, vector<string>& tokens, ostringstream& out) const 
	{
		string clr = token->substr(0,3);
		string cl = token->substr(0,2);
		if ( token->size() == 3 && magCompare(clr, "clr") ) {
			return true;
		}
		if ( magCompare(cl, "cl") ) {
			string colour = token->substr(2); 
		    string::size_type index = colour.find_last_not_of(" ");
            colour = (index == string::npos) ? "" :  colour.substr(0, index+1);
			++token;
			out << "<font colour = '" << colour << "'>";
			MagLog::debug() << "TEXT SETTING-->" << out.str() << endl;
			convert(token, tokens, out);
			out << "</font>";
			return true;
		}
		return false;
	}
};

class EasyHelper : public ConvertHelper
{
public:
	EasyHelper(const string& set, const string& reset, const string& html) : 
		set_(set), reset_(reset), html_(html) {}
	~EasyHelper() {}
	bool operator()(vector<string>::const_iterator& token, vector<string>& tokens, ostringstream& out) const 
	{
		
		
		if ( magCompare(token->substr(0, reset_.size()), reset_) ) {
			return true;
		}
		if ( magCompare(token->substr(0, set_.size()), set_) )  {
			++token;
			out << "<" << html_ << ">";
			convert(token, tokens, out);
			out << "</" << html_ << ">";
			return true;
		}
		return false;
	}
protected :
	string set_;
	string reset_;
	string html_;
};


class QualityHelper :public ConvertHelper
{
public:
	QualityHelper() { }
	~QualityHelper() {};
	bool operator()(vector<string>::const_iterator& token, vector<string>& tokens, ostringstream& out) const 
	{
		string ql = token->substr(0,3);
	
		if ( magCompare(ql, "qlr") || magCompare(ql, "fnr") ) {
			return true;
		}
		if ( magCompare(ql, "qlh") || magCompare(ql, "fn3") ) {
				++token;
				out << "<font name='sansserif' style='bold'>";
				convert(token, tokens, out);
				out << "</font>";
				return true;
		}  
		if ( magCompare(ql, "qlm") || magCompare(ql, "fn2") ) {
				++token;
				out << "<font name = 'sansserif' style='normal'>";
				convert(token, tokens, out);
				out << "</font>";
				return true;
		}  
		if ( magCompare(ql, "qll") || magCompare(ql, "fn1") ) {
				++token;
				out << "<font name='serif' style='normal'>";
				convert(token, tokens, out);
				out << "</font>";
				return true;
		}  
		return false;
	}
};


static HeightHelper height;
static QualityHelper quality;
static ColourHelper colour;
static EasyHelper underline("ul", "ulr", "u");
static EasyHelper superscript("sp", "spr", "sup");
static EasyHelper subscript("sb", "sbr", "sub");

vector<ConvertHelper* >* ConvertHelper::list_ = 0;



TextVisitor::TextVisitor() :positional_(true)
{

	static int i = 0;
	ostringstream n;
	n << "TextVisitor" << i;
	name(n.str());
	
	layout_ = new Layout();
	layout_->name(n.str());
	i++;
	current_ = layout_;

	if (tags_.empty()) {
		tags_["base_date"] = "grib_info  key='start-date' format='%Y-%m-%d %H:%M:00'";
		tags_["valid_date"] = "grib_info  key='end-date' format='%A %d %B %Y at %H UTC'";
	}
}



TextVisitor::~TextVisitor() 
{
}


void TextVisitor::addToTags(const string& line, const string& entry) 
{
	add(line, new TextEntry(entry)); 
}
/*!
 Class information are given to the output-stream.
*/		
void TextVisitor::print(ostream& out)  const
{
	out << "TextVisitor[";
	LayoutVisitor::print(out);
	out << "]";
}

void TextVisitor::getReady()
{	
}


void TextVisitor::visit(MetaDataVisitor& meta){
	meta.add("magics.title", "\"" + label_ + "\"");

}


void  TextVisitor::visit() {
	
	decode();
	clear();
	
	vector<double>::iterator ratio = height_ratios_.begin();
	currentTexts_.clear();

	for ( vector<string>::iterator line = lines_.begin(); line != lines_.end(); ++line) {
		double font_size = font_size_;
		if ( ratio !=  height_ratios_.end() ) {
			font_size *= *ratio;
			++ratio;
		}
		MagFont font(font_);
		font.style(font_style_);
		font.size(font_size);
		font.colour(font_colour_->name());

		TagConverter converter(*this);
		converter.font(font);
		if ( converter.staticTag(*line) ) {
			Text* text = new Text();
			
			converter.decode(*line, text);	
			currentTexts_[*line].push_back(text);
		}
	}

	 
}

void TextVisitor::visit(BasicSceneObject& parent)
{
	
	// Setup the information the automatic title if necessary!
	decode();
	
	
	TagHandler::reset();
	bool automatic = false;
	clear();
		parent.visit(*this);
		vector<string>::iterator line = lines_.begin(); 
		map<string, vector<string> > lines;
		vector<string> lineslist; ;
		
		while ( line != lines_.end() )
		{
			lines.insert(make_pair(*line, vector<string>()));
			
	 		if ( *line != "<magics_title/>" ) {
	 			lines[*line].push_back(*line);
	 			(*this)[*line].push_back(new TextEntry(*line));
	 		}
	 		else {			
	 			automatic = true;
	 			map<string, vector<TextEntry* >  >::iterator key = find(*line);
	 			if ( key != end() ) {
	 				for (vector<TextEntry* >::iterator entry = key->second.begin(); entry !=  key->second.end(); ++entry) 
	 					lines[*line].push_back((*entry)->entry_);
	 				}
	 			
	 		}	
	 		++line;
		}
	if ( !automatic && !texts_.empty() ) {
		// Nothing to do the text has already been created!
		return;
	}



	
	vector<string>::reverse_iterator lend = lines_.rend();
	vector<double>::reverse_iterator ratios = height_ratios_.rbegin();
	currentTexts_.clear();
	
	for (vector<string>::reverse_iterator line = lines_.rbegin(); line != lend; ++line)
	{
		double ratio =  ( ratios ==  height_ratios_.rend() ) ?  1 : *ratios;

		if ( ratios != height_ratios_.rend() ) 	++ratios;
		MagFont font(font_);
		font.style(font_style_);
		font.size(font_size_*ratio);
		font.colour(font_colour_->name());
		TagConverter converter(*this);
		converter.font(font);
		if ( converter.staticTag(*line) )
			continue;
		currentTexts_[*line] = vector<Text*>();
		map<string,vector<TextEntry* >  >::iterator key = find(*line);
		if ( key != end() ) {

			for (vector<TextEntry*  >::reverse_iterator entry = key->second.rbegin(); entry !=  key->second.rend(); ++entry) {
			 		    	 Text* text = new Text();
			 		    	 converter.decode((*entry)->entry_, text);	
			 		    	 if (text->noText()) {
			 		    		 delete text;
			 		    		 continue;
			 		    	 }
			 		    	if ( currentTexts_.find(*line) == currentTexts_.end() )
			 		    					currentTexts_[*line] = vector<Text*>();
			 		    	 currentTexts_[*line].push_back(text);
			}
		}
		
		
	}
	
}
void TextVisitor::start()
{
	
	texts_.clear();
}
void TextVisitor::finish(BasicGraphicsObjectContainer& parent) 
{
// Here we adjustt the position of the text...
	newLayout();
	parent.push_back(current_);
	
	current_->blankIt();
	
	
	double height = (font_size_ /current_->absoluteHeight())*100;
	double last = (0.15/current_->absoluteHeight())*100; //in %
	double gap = height*1.2;
   



	for (vector<Text*>::iterator text = texts_.begin(); text != texts_.end(); ++text)
	{

		// Calculate the position depending on the jsutification.
		double x;
		if (justification_ == MLEFT) x = .1; // 0.1% 
		else if (justification_ == MRIGHT) x = 98.; //98 %
		else x = 50.; 

		(*text)->setJustification(justification_);
		(*text)->setVerticalAlign(MBOTTOM);

		gap = (*text)->noText() ?  height*1.2 : (((*text)->getFontMax()/current_->absoluteHeight())*100)*1.2;
		(*text)->push_back(PaperPoint(x, last)); // approximate position to be improved 
		last += gap;
		current_->push_back(*text);
		
	}
	current_->frameIt();
	
}

void TextVisitor::update(vector<Text*>& texts) 
{
	
        
			
		for (vector<Text*>::iterator text = texts.begin(); text != texts.end(); ++text)
		{
			texts_.push_back(*text);
		}
		
}


struct TextHelper : public XmlNodeVisitor, public vector<string>
{
	TextHelper() {}
	~TextHelper() {}

	vector<string>::iterator begin()
	{
		 push_back(current_.str());
		 return vector<string>::begin();
	}

	bool  empty()
	{
		return 	current_.str().empty();	
	}

	void visit(const XmlNode& node)
	{
			if ( magCompare(node.name(), "font") )
				current_ << node << endl;
			if ( magCompare(node.name(), "b") )
				current_ << node << endl;
			if ( magCompare(node.name(), "magics_title") )
				current_ << node << endl;
			if ( magCompare(node.name(), "grib_info") )
							current_ << node << endl;
			if ( magCompare(node.name(), "data") )
				current_ << node << endl;
			
			if (magCompare(node.name(), "br")) {
				push_back(current_.str());		
				current_.seekp(ios_base::beg);
				for (unsigned int n = 0; n < back().size(); n++)
					current_ << " ";
				current_.seekp(ios_base::beg);
			}
	}
	ostringstream current_; 
};

void XmlTextVisitor::set(const XmlNode& node) 
{ 
	XmlNode view = node;
	view.name("view");
	XmlBasicNodeAttributes::set(view); 
	TextVisitor::set(node);
	TextHelper helper;
	node.visit(helper);

	if ( helper.empty() ) {
        vector<string> lines;
    
                lines.push_back(line1_);
		        lines.push_back(line2_);
		        lines.push_back(line3_);
                lines.push_back(line4_);
                lines.push_back(line5_);
                lines.push_back(line6_);
                lines.push_back(line7_);
                lines.push_back(line8_);
                lines.push_back(line9_);
                lines.push_back(line10_);
        for (int i = 0; i <count_; i++)
            lines_.push_back(lines[i]);
               
    }
	

	for (TextHelper::const_iterator line = helper.begin(); line != helper.end(); ++line) {
		lines_.push_back(*line);
	}
}

void XmlTextVisitor::getReady()
{

	assert (BasicSceneObject::parent_);

	Dimension bottom(bottom_, BasicSceneObject::parent_->absoluteWidth(), 0);
	Dimension left(left_, BasicSceneObject::parent_->absoluteHeight(), 0);
	Dimension width(XmlBasicNodeAttributes::width_, BasicSceneObject::parent_->absoluteWidth(), 100);
	Dimension height(XmlBasicNodeAttributes::height_, BasicSceneObject::parent_->absoluteHeight(), 100);

	Dimension mb(margin_bottom_, height.absolute(), 0);	
	Dimension ml(margin_left_, width.absolute(), 5);
	Dimension mr(margin_right_, width.absolute(), 5);
	Dimension mt(margin_top_, height.absolute(), 0);

	// We have to calculate the new x, y, width, height
	const double xl = ml.percent()*width.absolute()*0.01;
	const double xr = mr.percent()*width.absolute()*0.01;
	const double yb = mb.percent()*height.absolute()*0.01;
	const double yt = mt.percent()*height.absolute()*0.01;
	
	const double text_x =  left.percent()  + xl *100./BasicSceneObject::parent_->absoluteWidth();
	const double text_y =  bottom.percent()+ yb *100./BasicSceneObject::parent_->absoluteHeight();;
		
	double text_width  = width.percent();
	double text_height = height.percent();

	if ( ( text_height + text_y ) > 100 ) 
		text_height = 100 - text_y ;
	if ( ( text_width + text_x ) > 100 ) 
		text_width = 100 - text_x ;
	
	this->x(text_x);
	this->y(text_y);
	this->width (text_width  - xr *100/BasicSceneObject::parent_->absoluteWidth());
	this->height(text_height - yt *100/BasicSceneObject::parent_->absoluteHeight());
	
	// adjust the font size!...	
	Dimension text(font_dimension_,height.absolute(), 10);
	font_size_ = text.absolute();

	layout_->display( XmlBasicNodeAttributes::display_); 
	layout_->frame(TextVisitorAttributes::blanking_, TextVisitorAttributes::border_, *TextVisitorAttributes::border_colour_, M_SOLID, 1);

	//BasicSceneObject::displayType_ = XmlBasicNodeAttributes::display_;
//	MagLog::dev()<< "XmlTextVisitor::getReady()" << endl;
//	MagLog::dev()<< XmlBasicNodeAttributes::left_ << "-->" << BasicSceneObject::x_ << "(" << parent_->absoluteWidth() << ")" << endl;
//	MagLog::dev()<< XmlBasicNodeAttributes::bottom_ << "-->" << BasicSceneObject::y_ << "(" << parent_->absoluteHeight() << ")" << endl;
//	MagLog::dev()<< XmlBasicNodeAttributes::width_ << "-->" << BasicSceneObject::width_ << "(" << parent_->absoluteWidth() << ")" << endl;
//	MagLog::dev()<< XmlBasicNodeAttributes::height_ << "-->" << BasicSceneObject::height_ << "(" << parent_->absoluteHeight() << ")" << endl;
} 

FortranTextVisitor::FortranTextVisitor()
{
}  

void XmlTextVisitor::decode()
{
	
	

	
}


void FortranTextVisitor::decode()
{
	if (lines_.empty()) {

    stringarray lines;
    vector<double> ratios;

    interpret(line1_, lines);
    ratios.push_back(height_ratio_1_);   
    interpret(line2_, lines);
    ratios.push_back(height_ratio_2_);     
    interpret(line3_, lines);
    ratios.push_back(height_ratio_3_);    
    interpret(line4_, lines);
    ratios.push_back(height_ratio_4_);     
    interpret(line5_, lines);
    ratios.push_back(height_ratio_5_);     
    interpret(line6_, lines);
    ratios.push_back(height_ratio_6_);
    interpret(line7_, lines);
    ratios.push_back(height_ratio_7_);    
    interpret(line8_, lines);
    ratios.push_back(height_ratio_8_);
    interpret(line9_, lines);
    ratios.push_back(height_ratio_9_);
    interpret(line10_, lines);
    ratios.push_back(height_ratio_10_);

    if (first_ > 10) {
    	MagLog::warning() << "Invalid value for text_first_line[" << first_ << "] : reset to 1 " << "\n";
        first_ = 1;
    }
    
    if ( count_ > 10) {
    	MagLog::warning() << "Invalid value for text_count_line[" << first_ << "]  reset to  " << count_ << "\n";
        count_ = 10 - first_ + 1;
    } 
    
    int last = first_ + count_;
    if ( last > 10) {
        last  = 10;
        count_ = 10 - first_ + 1;
        MagLog::warning() << "Invalid value for text_first_line[" << first_ << "] and  text_count_line[" << count_ << "]  reset"<< "\n";
    } 
    
    for (int i = 0; i < count_; i++) {
    	if (lines[first_+i-1].empty())
    		lines_.push_back("<magics_title/>"); // Ask for an automatic title...
    	else 
        	lines_.push_back(lines[first_+i-1]);
        height_ratios_.push_back(ratios[first_+i-1]);
    }
	}
	
}

void FortranTextVisitor::interpret(string& line, stringarray& lines)
{
	typedef const string& (FortranTextVisitor::*GetStringFunction)() const;
	typedef int (FortranTextVisitor::*GetIntFunction)() const;
	typedef double (FortranTextVisitor::*GetFloatFunction)() const;

	static map<string, GetStringFunction> getString;
	static map<string, GetIntFunction> getInt;
	static map<string, GetFloatFunction> getFloat;
	
    if ( getString.empty() ) {
		getString["text_character_1"] = &TextVisitor::getCharacter1;
		getString["text_character_2"] = &TextVisitor::getCharacter2;
		getString["text_character_3"] = &TextVisitor::getCharacter3;
		getString["text_character_4"] = &TextVisitor::getCharacter4;
		getString["text_character_5"] = &TextVisitor::getCharacter5;
		getString["text_character_6"] = &TextVisitor::getCharacter6;
		getString["text_character_7"] = &TextVisitor::getCharacter7;
		getString["text_character_8"] = &TextVisitor::getCharacter8;
		getString["text_character_9"] = &TextVisitor::getCharacter9;
		getString["text_character_10"] = &TextVisitor::getCharacter10;

    }
     if ( getFloat.empty() ) {	
		getFloat["text_real_1"] = &TextVisitor::getReal1;
		getFloat["text_real_2"] = &TextVisitor::getReal2;
		getFloat["text_real_3"] = &TextVisitor::getReal3;
		getFloat["text_real_4"] = &TextVisitor::getReal4;
		getFloat["text_real_5"] = &TextVisitor::getReal5;
		getFloat["text_real_6"] = &TextVisitor::getReal6;
		getFloat["text_real_7"] = &TextVisitor::getReal7;
		getFloat["text_real_8"] = &TextVisitor::getReal8;
		getFloat["text_real_9"] = &TextVisitor::getReal9;
		getFloat["text_real_10"] = &TextVisitor::getReal10;

    }
    if ( getInt.empty() ) {
		getInt["text_integer_1"] = &TextVisitor::getInteger1;
		getInt["text_integer_2"] = &TextVisitor::getInteger2;
		getInt["text_integer_3"] = &TextVisitor::getInteger3;
		getInt["text_integer_4"] = &TextVisitor::getInteger4;
		getInt["text_integer_5"] = &TextVisitor::getInteger5;
		getInt["text_integer_6"] = &TextVisitor::getInteger6;
		getInt["text_integer_7"] = &TextVisitor::getInteger7;
		getInt["text_integer_8"] = &TextVisitor::getInteger8;
		getInt["text_integer_9"] = &TextVisitor::getInteger9;
		getInt["text_integer_10"] = &TextVisitor::getInteger10;

    }
    
    string key, format;
    int where;
    int howmany;

    extract(line, key, format, where, howmany);

    if ( key.empty() ) {
    	// No special key found! nothing to do
    	lines.push_back(html_ ? line : ConvertHelper::convert(line));
    	return;
    }
    // Try to find the key in the string keys
    map<string, GetStringFunction>::const_iterator text = getString.find(key);
    if ( text != getString.end() ) {
    	line.replace(where, howmany, (this->*text->second)());
    }


    //  Try to find the key in the integer keys and apply the Format!
    map<string, GetIntFunction>::const_iterator integer = getInt.find(key);
    if ( integer != getInt.end() ) {
    	// Apply format
    	ostringstream value;
    	value << MagicsFormat(format, (this->*integer->second)());
    	line.replace(where, howmany, value.str());
    }

    //  Try to find the key in the real keys and apply the Format!
    //  Try to find the key in the integer keys and apply the Format!
	map<string, GetFloatFunction>::const_iterator real = getFloat.find(key);
	if ( real != getFloat.end() ) {
		// Apply format
		ostringstream value;
		value << MagicsFormat(format, (this->*real->second)());
		line.replace(where, howmany, value.str());
	}



	if (html_) {
		for (map<string, string>::iterator tag = tags_.begin(); tag != tags_.end(); ++tag) {
			size_t pos = line.find(tag->first);
			if ( pos != string::npos ) 
				line.replace(pos, tag->first.length(), tag->second);

		}
	}
    lines.push_back(html_ ? line : ConvertHelper::convert(line));
}


FortranAutomaticTextVisitor::FortranAutomaticTextVisitor()
{
	positional_ = false;
}

void FortranAutomaticTextVisitor::getReady()
{
	decode();
	MagLog::dev() << "FortranAutomaticTextVisitor::getReady()" << endl;
	
	Dimension text(font_dimension_, 1, 10);
	font_size_ = text.absolute();
	layout_->frame(blanking_, border_, *border_colour_, border_line_style_, border_thickness_);
	
}

FortranPositionalTextVisitor::FortranPositionalTextVisitor()
{
}

void FortranPositionalTextVisitor::getReady()
{
	decode();
	positional_ = true;

	layout_->x(adjustDimension(TextVisitorAttributes::x_, 7.5, BasicPositionalObject::absoluteWidth()));
	layout_->y( adjustDimension(TextVisitorAttributes::y_, 5.,BasicPositionalObject::absoluteHeight()));
	layout_->width(adjustDimension(TextVisitorAttributes::width_, 50., BasicPositionalObject::absoluteWidth()));
	layout_->height(adjustDimension(TextVisitorAttributes::height_, 20., BasicPositionalObject::absoluteHeight()));
	Dimension text(font_dimension_, absoluteHeight(), 10);
		font_size_ = text.absolute();
	layout_->frame(TextVisitorAttributes::blanking_, TextVisitorAttributes::border_, *border_colour_, border_line_style_, border_thickness_);
}


XmlTextVisitor::XmlTextVisitor() 
{
	//displayType_ = BLOCK;
	//border_ = false;
	//blanking_= false;
	//lines_.push_back("<magics_title/>");
}

void TextVisitor::titles(vector<string>& titles)
{	
	//prepare();	
	for ( vector<string>::const_iterator line = lines_.begin(); line != lines_.end(); ++line)
		titles.push_back(*line);
}
void TextVisitor::add(const string& line, TextEntry* entry)
{  
	map<string, vector<TextEntry* >  >::iterator key = find(line);
	if ( key == end() ) {
		map<string, vector<TextEntry* >  >::insert(make_pair(line, vector<TextEntry* >()) );
		key = find(line);
	}
	key->second.push_back(entry);
}

void TextVisitor::extract(const string& line, string& key, string& format, int& where, int& howmany)
{
	int count = 0;

	format = "(automatic)";
	where = 0;
    howmany = 0;

    string helper;
    char escape = *(getParameter_escape().begin());
    bool special = false;

    string::const_iterator x = line.begin();
    while ( x != line.end() ) {
    	if (*x == escape)  {
            if ( special ) {
                      howmany = helper.length() + 2;
                      string::iterator k = helper.begin();
                      // Try to extract the format!
                      while ( k != helper.end()) {
                      if ( *k == '(' )  {
                          format.clear();
                          while ( k != key.end()) {
                              format.push_back(*k);
                              if ( *k == ')' )
                                  break;
                              ++k;
                          }
                      }
                      else
                          key.push_back(*k);
                      ++k;
                      }
                  }
                  else {
                      where = count;
                  }
                  special = !special;
          }
          else {
                  if ( special)
                      helper.push_back(*x);
              }
          ++x;
          count++;
      }

    key = lowerCase(key);


}
