/*
 * (C) Copyright 1996-2016 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

/*! \file XmlMagics.cc
    \brief Implementation of the Template class XmlMagics.
    \author Meteorological Visualisation Section, ECMWF

    Started: Apr-2007

*/


#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"

#endif

#include "MetgramGraph.h"
#include "EpsGraph.h"

using namespace magics;

XmlMagics::XmlMagics() : root_(0),
	gribloop_(0),
	geographical_(true),
	driversToSet_(true)
{
	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_["tephigram"] = &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_["capebox"] = &XmlMagics::capebox;
	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)
{

	MetgramGraph* metgraph = new MetgramGraph();
	metgraph->set(node);
	top()->visdef(metgraph);

}


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::capebox(const XmlNode& node)
{
	CapeBox* cape = new CapeBox();
	cape->set(node);
	top()->visdef(cape);
}
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
}
