/******************************** 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 XmlMagics.cc
    \brief Implementation of the Template class XmlMagics.
    \author Meteorological Visualisation Section, ECMWF

    Started: Apr-2007

*/


#include "magics_ecmwf_log.h"
#include "XmlMagics.h"
#include "XmlReader.h"
#include "MagException.h"
#include "Timer.h"

#include "RootSceneNode.h"
#include "SceneNode.h"
#include "ViewNode.h"
#include "Coastlines.h"
#include "BinaryObject.h"
#include "TephiGrid.h"
#include "TaylorGrid.h"
#ifdef HAVE_GRIB
#include "GribDecoder.h"
#endif
#include "TableDecoder.h"
#include "VisualAction.h"
#include "XYList.h"
#include "EpsXmlInput.h"
#include "WrepJSon.h"
#include "GeoJSon.h"
#include "SymbolPlotting.h"
#include "Contour.h"
#include "Wind.h"
//#include "Histogram.h"

#include "LegendVisitor.h"
#include "TextVisitor.h"

#include "UserPoint.h"
#include "UserPoint.h"
#include "Axis.h"
#include "MatrixTestDecoder.h"

#include "SimplePolylineInput.h"
#include "SimplePolylineVisualiser.h"

#include "MetaData.h"


#ifdef HAVE_SPOT
#include "ClassicMtgDecoder.h"
#include "EpsgramDecoder.h"
#include "MetgramGraph.h"
#endif

#include "EpsGraph.h"

using namespace magics;

XmlMagics::XmlMagics() : root_(0),
	gribloop_(0),
	geographical_(true),
	driversToSet_(true)
{
	writeMagLog("magml");

	actions_["magics"] = &XmlMagics::magics;
	actions_["page"] = &XmlMagics::page;

	actions_["mgb"] = &XmlMagics::binary;

	actions_["text"] = &XmlMagics::text;
	actions_["map"] = &XmlMagics::map;
	actions_["matrix"] = &XmlMagics::matrix;
	actions_["cartesian"] = &XmlMagics::cartesian;
	actions_["taylor"] = &XmlMagics::cartesian;

	actions_["cylindrical"] = &XmlMagics::geographical;
	actions_["polar_stereographic"] = &XmlMagics::geographical;

	actions_["horizontal_axis"] = &XmlMagics::horizontalAxis;
	actions_["vertical_axis"] = &XmlMagics::verticalAxis;
	actions_["coastlines"] = &XmlMagics::coastlines;
	actions_["taylorgrid"] = &XmlMagics::taylor;
	actions_["tephigrid"] = &XmlMagics::tephigrid;
	actions_["thermo"] = &XmlMagics::cartesian;
	actions_["grib"] = &XmlMagics::grib;
	actions_["netcdf"] = &XmlMagics::netcdf;
	actions_["table"] = &XmlMagics::table;

	actions_["odb"] = &XmlMagics::odb;
	actions_["xyinput"] = &XmlMagics::xyinput;
	actions_["input"] = &XmlMagics::input;

	actions_["wind"] = &XmlMagics::wind;
	actions_["image"] = &XmlMagics::image;
	actions_["symbol"] = &XmlMagics::symbol;
	actions_["contour"] = &XmlMagics::contour;
	actions_["histogram"] = &XmlMagics::histogram;
	actions_["legend"] = &XmlMagics::legend;
	actions_["meta"] = &XmlMagics::metadata;

	actions_["drivers"] = &XmlMagics::driver;

	actions_["plot"] = &XmlMagics::layer;
	actions_["geopoints"] = &XmlMagics::geopoints;
	actions_["cdfgraph"] = &XmlMagics::cdfgraph;
	actions_["efigraph"] = &XmlMagics::efigraph;
	actions_["efigram"] = &XmlMagics::efigram;

	actions_["metgraph"] = &XmlMagics::metgraph;
	actions_["metgram"] = &XmlMagics::metgram;

	actions_["gribloop"] = &XmlMagics::gribloop;

	actions_["epsxml"] = &XmlMagics::epsxml;
	actions_["wrepjson"] = &XmlMagics::wrepjson;
	actions_["geojson"] = &XmlMagics::geojson;

	actions_["epsbufr"] = &XmlMagics::epsbufr;
	actions_["epsgraph"] = &XmlMagics::epsgraph;
	actions_["epsgram"] = &XmlMagics::epsgram;
	actions_["epswind"] = &XmlMagics::epswind;
	actions_["epscloud"] = &XmlMagics::epscloud;
	actions_["epsbar"] = &XmlMagics::epsbar;
	actions_["epswave"] = &XmlMagics::epswave;
	actions_["epsshade"] = &XmlMagics::epsshading;
	actions_["epsplume"] = &XmlMagics::epsplume;
	actions_["epsdirection"] = &XmlMagics::epsdirection;

	actions_["mapgen"] = &XmlMagics::mapgen;
	actions_["graph"] = &XmlMagics::graph;

	actions_["polyline_input"] = &XmlMagics::polyinput;
	actions_["polyline"] = &XmlMagics::polyline;

	actions_["layer"] = &XmlMagics::split;
	actions_["thread"] = &XmlMagics::thread;
	actions_["import"] = &XmlMagics::import;
}


XmlMagics::~XmlMagics()
{
}

/*!
 Class information are given to the output-stream.
*/
void XmlMagics::print(ostream& out)  const
{
	out << "XmlMagics[";
	out << "]";
}

void XmlMagics::execute(XmlTree& tree)
{
	{

	try {
		tree.visit(*this);
	}
	catch (MagicsException& e) {
		MagLog::debug() << e.what() << endl;
	}

	if ( driversToSet_ )
		output_.set(drivers_);

	ASSERT(root_);

	root_->execute();

	if ( root_->scalingFactor() != 1)
		drivers_.setOutputWidth(root_->scalingFactor());


	drivers_.setDriversWidth(root_->absoluteWidth());
	drivers_.setDriversHeight(root_->absoluteHeight());

	{
		Timer timer("drivers", "rendering of the graphical tree");
		drivers_.openDrivers();
		drivers_.dispatch(root_->root());

		drivers_.closeDrivers();
	}
	}
	MetaDataVisitor::collect();
	//root_->metadata(meta);
	/* later
		std::map<string, string>::const_iterator filename = meta.find("filename");
		if ( filename != meta.end() && !filename->second.empty()) {
			ofstream file (filename->second.c_str());

			for (std::map<string, string>::const_iterator d = meta.begin(); d != meta.end(); ++d) {
				if ( d != filename )
	                file << d->first << " = " << d->second << "; " << endl;
	        }
		}
	*/
	output_.clear();
	//ParameterManager::release();
	delete root_;
	root_ = 0;
}

void XmlMagics::execute(const string& file, std::map<string, string>& )
{

	XmlReader parser(true);
	XmlTree tree;
	ParameterManager::set(string("layout"), string("magml"));
	try {
		parser.interpret(file, &tree);
		execute(tree);
	}
	catch (MagicsException& e) {
		MagLog::debug() << e.what() << endl;
	}
}

void XmlMagics::display(const string& file)
{
	XmlReader parser(true);
	XmlTree tree;

	try {
		parser.interpret(file, &tree);
		tree.visit(*this);
	}
	catch (MagicsException& e) {
			MagLog::debug() << e.what() << endl;
	}

	if ( driversToSet_ )
		output_.set(drivers_);

	ASSERT(root_);
	root_->getReady();
	drivers_.setDriversWidth(root_->absoluteWidth());
	drivers_.setDriversHeight(root_->absoluteHeight());

	root_->execute();
	drivers_.openDrivers();
	drivers_.dispatch(root_->root());

	drivers_.closeDrivers();
}

void XmlMagics::visit(const XmlNode& node)
{
	std::map<string, Action>::iterator action = actions_.find(node.name());

	if ( action == actions_.end() )
	{
		MagLog::dev() << " MagML - ignoring tag: " << node.name()<< endl;
		return;
	}
	(this->*action->second)(node);
}

void XmlMagics::magics(const XmlNode& node)
{
	ParameterManager::set(string("layout"), string("magml"));
	const float version_ = 3.0;
	MagLog::debug() << " You are using the " << fixed << setprecision(1) << version_ << " version of the magml interpreter\n";
	string version = node.getAttribute("version");
	if ( version.empty() )
	{
		MagLog::error() << " No version defined in your magml file\n"
		             << " Compatibilty issue: check your magml file and add a version number" << endl;
		return;
	}
	float v;
	std::stringstream sv(version);
	sv >> v;
	if ( v < version_ )
	{
		MagLog::error() << " The version defined in the file is " << fixed << setprecision(1) << v
		             << "\n Compatibilty issue: check your magml file and update the version number" << endl;
		return;
	}
	string method = node.getAttribute("method") ;

	if  ( method == "wrep" )
		root_ = new  WrepRootSceneNode();
	else if  ( method == "legacy" )
		root_ =  new  LegacyRootSceneNode();
	else
		root_ =  new  XmlRootSceneNode();

	push(root_);
	root_->set(node);
	root_->getReady();
	node.visit(*this);
}



void XmlMagics::driver(const XmlNode& node)
{
	for ( XmlNode::ElementIterator driver = node.firstElement(); driver != node.lastElement(); ++driver)
	{
		output_.set(**driver, drivers_);
	}
	driversToSet_ = false;
}


void XmlMagics::split(const XmlNode& node)
{
/*
	LayerNode* layer = new LayerNode();
	layer->set(node);
	top()->push_back(layer);
	push(layer);
	node.visit(*this);
	pop();
*/
	node.visit(*this);
}


void XmlMagics::thread(const XmlNode& )
{
/* later
	ThreadNode* thread = new ThreadNode();
	thread->set(node);
	top()->push_back(thread);
	push(thread);
	node.visit(*this);
	pop();
*/
}

void XmlMagics::metadata(const XmlNode& node)
{
	MetaDataVisitor* meta = new MetaDataVisitor();
	meta->set(node);
	top()->push_back(meta);
}



void XmlMagics::page(const XmlNode& node)
{
	XmlSceneNode* page = new XmlSceneNode();

	page->set(node);
	top()->insert(page);
	push(page);
	node.visit(*this);
	pop();
}

void XmlMagics::legend(const XmlNode& node)
{
	LegendVisitor* legend = new XmlLegendVisitor();
	legend->set(node);

	top()->legend(legend);
	node.visit(*this);
}

void XmlMagics::text(const XmlNode& node)
{
	XmlTextVisitor* text = new XmlTextVisitor();
	text->set(node);
	top()->text(text);

	node.visit(*this);
}

void XmlMagics::map(const XmlNode& node)
{
	XmlViewNode* view = new XmlViewNode();
	view->set(node);
	BasicSceneObject* parent = top()->insert(view);
	if (parent != top() ) {
		pop();
		push(parent);
	}

	push(view);
	node.visit(*this);
	pop();
}

void XmlMagics::binary(const XmlNode& node)
{
	BinaryObject* binary = new BinaryObject();
	binary->set(node);
	top()->push_back(binary);
}

void XmlMagics::coastlines(const XmlNode& node)
{
	Coastlines* coast = new Coastlines();
	coast->set(node);
	top()->push_back(coast);
}

void XmlMagics::taylor(const XmlNode& node)
{
	TaylorGrid* grid = new TaylorGrid();
	grid->set(node);
	top()->push_back(grid);
}
void XmlMagics::tephigrid(const XmlNode& node)
{
	TephiGrid* grid = new TephiGrid();
	grid->set(node);
	top()->push_back(grid);
}
void XmlMagics::horizontalAxis(const XmlNode& node)
{
	HorizontalAxis* axis = new HorizontalAxis();
	axis->set(node);
	top()->push_back(axis);
}

void XmlMagics::verticalAxis(const XmlNode& node)
{
	VerticalAxis* axis = new VerticalAxis();
	axis->set(node);
	top()->push_back(axis);
}

void XmlMagics::cartesian(const XmlNode&)
{
	geographical_ = false;
}

void XmlMagics::geographical(const XmlNode&)
{
	geographical_ = true;
}

void XmlMagics::layer(const XmlNode& node)
{
	if ( geographical_ )
	{
		VisualAction* action = new VisualAction();
		action->set(node);
		top()->push_back(action);
		push(action);
	}
	else {
		VisualAction* action = new VisualAction();
		action->set(node);
		top()->push_back(action);
		push(action);
	}

	node.visit(*this);
	pop();
}

#ifdef HAVE_GRIB
GribDecoder* grib_handler;

void XmlMagics::gribloop(const XmlNode& node)
{
	if ( gribloop_) delete gribloop_;
	gribloop_ = new GribLoop();
	gribloop_->set(node);

	actions_["grib"] = &XmlMagics::gribinloop;
	actions_["layer"] = &XmlMagics::splitinloop;

	while ( gribloop_->hasMore() ) {
		node.visit(*this);
	}
}


void XmlMagics::splitinloop(const XmlNode& )
{
	/* later!
	ASSERT(gribloop_);

	LayerNode* layer = new LayerNode();
	layer->set(node);
	gribloop_->set(*layer);
	top()->push_back(layer);
	push(layer);
	this->layer(node);
	pop();
	*/
}

void XmlMagics::gribinloop(const XmlNode&)
{
	ASSERT(gribloop_);
	top()->data(gribloop_->current());
}

void XmlMagics::grib(const XmlNode& node)
{
	GribDecoder* grib = new GribDecoder();
	grib->set(node);

	top()->data(grib);

}
#else
void XmlMagics::gribloop(const XmlNode&)
{
	MagLog::warning() <<" Attempt to call GribLoop but GRIB support is disabled!"<< endl;
}

void XmlMagics::gribinloop(const XmlNode&)
{
	MagLog::warning() <<" Attempt to call GribInLoop but GRIB support is disabled!"<< endl;
}

void XmlMagics::grib(const XmlNode&)
{
	MagLog::warning() <<" Attempt to call Grib decoding but GRIB support is disabled!"<< endl;
}
#endif


#include "GeoPointsDecoder.h"
void XmlMagics::geopoints(const XmlNode& node)
{
	GeoPointsDecoder* geo = new GeoPointsDecoder();
	geo->set(node);
	top()->data(geo);
}

#include "MapGenDecoder.h"
void XmlMagics::mapgen(const XmlNode& node)
{
	if (geographical_) {
		MapGenDecoder* mapgen = new MapGenDecoder();
		mapgen->set(node);
		top()->data(mapgen);
	}
	else {
		MapGenDecoder* mapgen = new MapGenDecoder();
		mapgen->set(node);
		top()->data(mapgen);
	}
}

void XmlMagics::xyinput(const XmlNode& node)
{
	if (geographical_) {
			XYList* list = new XYList();
			list->set(node);
			top()->data(list);
	}
	else {
			XYList* list = new XYList();
			list->set(node);
			top()->data(list);
	}
}

#include "InputData.h"
void XmlMagics::input(const XmlNode& node)
{
	InputData* data = new InputData();
	data->set(node);
	top()->data(data);

}

#include "GraphPlotting.h"
void XmlMagics::graph(const XmlNode& node)
{
	GraphPlotting* graph =  new GraphPlotting();
	graph->set(node);
	top()->visdef(graph);
}

void XmlMagics::histogram(const XmlNode& node)
{
	/*
	Histogram* histogram =  new Histogram();
	histogram->set(node);
	top()->visdef(histogram);
	*/
}


/***************************************************************************

    O D B / O D A

***************************************************************************/

#ifdef HAVE_ODB
#include "OdaDecoder.h"
#endif

void XmlMagics::odb(const XmlNode& node)
{
#ifdef HAVE_ODB
	OdaGeoDecoder* odb = new OdaGeoDecoder();
	odb->set(node);
	top()->data(odb);
#else
	MagLog::warning() << "ODB support is NOT enabled!\n";
#endif

}

/* ********************************************************************* */


void XmlMagics::efigram(const XmlNode& node)
{
#ifdef HAVE_SPOT
	EfigramDecoder* efi = new EfigramDecoder();
	efi->set(node);
	top()->data(efi);
#endif
}

void XmlMagics::cdfgraph(const XmlNode& node)
{
	CdfGraph* cdf = new CdfGraph();
	cdf->set(node);
	top()->visdef(cdf);
}

void XmlMagics::efigraph(const XmlNode& node)
{
	EfiGraph* efi = new EfiGraph();
	efi->set(node);
	top()->visdef(efi);
}


void XmlMagics::epsplume(const XmlNode& node)
{
	EpsPlume* plume = new EpsPlume();
	plume->set(node);
	top()->visdef(plume);
}

void XmlMagics::epsdirection(const XmlNode& node)
{
	EpsDirection* direction = new EpsDirection();
	direction->set(node);
	top()->visdef(direction);
}

void XmlMagics::metgram(const XmlNode& node)
{
#ifdef HAVE_SPOT
	ClassicMtgDecoder* metgram = new ClassicMtgDecoder();
	metgram->set(node);
	top()->data(metgram);
#endif
}

void XmlMagics::metgraph(const XmlNode& node)
{
#ifdef HAVE_SPOT
	MetgramGraph* metgraph = new MetgramGraph();
	metgraph->set(node);
	top()->visdef(metgraph);
#endif
}


void XmlMagics::epsgram(const XmlNode& node)
{
#ifdef HAVE_SPOT
	EpsgramDecoder* eps = new EpsgramDecoder();
	eps->set(node);
	top()->data(eps);
#endif
}

void XmlMagics::epsxml(const XmlNode& node)
{
	EpsXmlInput* eps = new EpsXmlInput();
	eps->set(node);
	top()->data(eps);
}
void XmlMagics::wrepjson(const XmlNode& node)
{

	WrepJSon* wrep = new WrepJSon();
	wrep->set(node);
	top()->data(wrep);
}
void XmlMagics::geojson(const XmlNode& node)
{

	GeoJSon* geo = new GeoJSon();
	geo->set(node);
	top()->data(geo);
}

#ifdef HAVE_BUFR
#include "EpsBufr.h"
#endif
void XmlMagics::epsbufr(const XmlNode& node)
{
#ifdef HAVE_BUFR
	EpsBufr* eps = new EpsBufr();
	eps->set(node);
	top()->data(eps);
#endif
}

void XmlMagics::epsgraph(const XmlNode& node)
{
	EpsGraph* epsgraph = new EpsGraph();
	epsgraph->set(node);
	top()->visdef(epsgraph);
}

void XmlMagics::epswave(const XmlNode& node)
{
	EpsWave* eps = new EpsWave();
	eps->set(node);
	top()->visdef(eps);
}

void XmlMagics::epswind(const XmlNode& node)
{
	EpsWind* epswind = new EpsWind();
	epswind->set(node);
	top()->visdef(epswind);
}

void XmlMagics::epscloud(const XmlNode& node)
{
	EpsCloud* epscloud = new EpsCloud();
	epscloud->set(node);
	top()->visdef(epscloud);
}
void XmlMagics::epsbar(const XmlNode& node)
{
	EpsBar* epsbar = new EpsBar();
	epsbar->set(node);
	top()->visdef(epsbar);
}
void XmlMagics::epsshading(const XmlNode& node)
{
	EpsShade* eps = new EpsShade();
	eps->set(node);
	top()->visdef(eps);
}

#ifdef HAVE_NETCDF
#include "NetcdfDecoder.h"
#endif
void XmlMagics::netcdf(const XmlNode& node)
{
#ifdef HAVE_NETCDF
	if ( geographical_ ) {
		NetcdfDecoder* netcdf = new NetcdfDecoder();
		netcdf->set(node);
		top()->data(netcdf);
	}
	else {
		NetcdfDecoder* netcdf = new NetcdfDecoder();
		netcdf->set(node);
		top()->data(netcdf);
	}
#endif
}

void XmlMagics::table(const XmlNode& node)
{
	TableDecoder* table = new TableDecoder();
	table->set(node);
	top()->data(table);
}

#include "InputMatrix.h"
void XmlMagics::matrix(const XmlNode& node)
{
		//MatrixTestDecoder* matrix = new MatrixTestDecoder();
		//matrix->set(node);
		if ( geographical_ ) {
			InputMatrix* data =  new InputMatrix();
			data->set(node);
			top()->data(data);
		}
		else {
			InputMatrix* data =  new InputMatrix();
			data->set(node);
			top()->data(data);
		}
}

void XmlMagics::polyinput(const XmlNode& node)
{
	SimplePolylineInput* input = new SimplePolylineInput();
	input->set(node);
	top()->data(input);
}

void XmlMagics::polyline(const XmlNode& node)
{
	SimplePolylineVisualiser* poly =  new SimplePolylineVisualiser();
	poly->set(node);
	top()->visdef(poly);
}


void  XmlMagics::symbol(const XmlNode& node)
{
	if ( geographical_ ) {
		SymbolPlotting* symbol =  new SymbolPlotting();
		symbol->set(node);
		top()->visdef(symbol);
	}
	else {
		SymbolPlotting* symbol =  new SymbolPlotting();
		symbol->set(node);
		top()->visdef(symbol);
	}
}


void XmlMagics::contour(const XmlNode& node)
{
	if ( geographical_ ) {
		Contour* contour =  new Contour();
		contour->set(node);
		top()->visdef(contour);
	}
	else {
		Contour* contour =  new Contour();
		contour->set(node);
		top()->visdef(contour);
	}
}


void XmlMagics::image(const XmlNode& node)
{
	MagLog::warning() <<" Image has been deprecated! Please use contour."<< endl;
}

#include "ImportObjectHandler.h"
void XmlMagics::import(const XmlNode& node)
{
	ImportObjectHandler* object = new ImportObjectHandler();
	object->set(node);
	top()->push_back(object);

}
void XmlMagics::wind(const XmlNode& node)
{
	if ( geographical_ ) {
		Wind* wind =  new Wind();
		wind->set(node);
		top()->visdef(wind);
	}
	else {
		MagLog::warning() << " wind not yet implemented for cartesian system" << endl;
	}
#ifdef HAVE_GRIB
	if ( gribloop_ ) gribloop_->next();
#endif
}
