/******************************** 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 ViewNode.cc
    \brief Implementation of the Template class ViewNode.
     
    Magics Team - ECMWF 2007
    
    Started: Tue 6-Mar-2007
    
    Changes:

*/

#include "ViewNode.h"
#include "Transformation.h"
#include "Dimension.h"
#include "Layout.h"
#include "Layer.h"

#include "SceneVisitor.h"
#include "LegendVisitor.h"
#include "TextVisitor.h"
#include "MetaData.h"

#include "HistoVisitor.h"

#include "AnimationRules.h"

using namespace magics;


ViewNode::ViewNode() : transformation_(0), animation_("basic"), vaxis_(2.), haxis_(1.),  rules_(0), legend_(0)
{
	static int i = 0;
	ostringstream n;
	n << "ViewNode" << i;
	name_ = n.str();
	i++;
	layout_ = new Layout();
	layout_->name(name_);
} 


ViewNode::~ViewNode() 
{
}

void ViewNode::text(TextVisitor* text)
{
	texts_.push_back(text);
}

void ViewNode::legend(LegendVisitor* legend)
{
	legend_ = legend;
}

void ViewNode::visit(MetaDataVisitor& metadata)
{
	double top = 0;
	double left = 0;
	double width = 200;
	double height = 100;

	drawing_->layout().getDriverInfo(left, top, width, height);


	ViewNode::transformation_->visit(metadata, left, top, width, height);

	BasicSceneObject::visit(metadata);
	for (vector<TextVisitor*>::iterator text = texts_.begin(); text != texts_.end(); ++text ) 
			(*text)->visit(metadata);
}

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

void ViewNode::getReady()
{
	MagLog::dev() << " ViewNode::getReady() \n";
	BasicSceneObject::getReady();
}

void ViewNode::visit(PreviewVisitor& preview) 
{
	preview.minX(ViewNode::transformation_->getMinPCX());
	preview.maxX(ViewNode::transformation_->getMaxPCX());
	preview.minY(ViewNode::transformation_->getMinPCY());
	preview.maxY(ViewNode::transformation_->getMaxPCY());

	dispatch(preview);
}

void ViewNode::visit(HistoVisitor& histo) 
{
	dispatch(histo);
}





void ViewNode::prepareLayout(SceneLayer& tree)
{

	updateLayout();
	LayoutHelper helper;
	components_.clear();
	drawing_= new DrawingVisitor();

	const double width  = 100-drawing_left_-drawing_right_;
	const double height = 100-drawing_top_-drawing_bottom_;
	const double vaxis  = 100/absoluteWidth() *vaxis_;


	drawing_->transformation(ViewNode::transformation_);
		drawing_->y(drawing_bottom_);
		drawing_->x(drawing_left_);
		drawing_->height(height);
		drawing_->width(width);
		drawing_->id(id_);
		drawing_->widthResolution(widthResolution()*width/100);
		drawing_->heightResolution(heightResolution()*width/100);
		drawing_->zoomable(true);
		drawing_->zoomLevels(zoomLevels_);
		drawing_->zoomCurrentLevel(zoomCurrentLevel_);
		drawing_->frame(*layout_);
		drawing_->frameIt();
	components_.push_back(drawing_);
	helper.add(drawing_);

	// Then the axis!
	leftAxis_ = new LeftAxisVisitor(*drawing_);
		leftAxis_->width(vaxis);
		leftAxis_->frameIt();
	components_.push_back(leftAxis_);
	helper.attachLeft(leftAxis_);

	rightAxis_ = new RightAxisVisitor(*drawing_);
		rightAxis_->width(vaxis);
		rightAxis_->frameIt();
	helper.attachRight(rightAxis_);
	components_.push_back(rightAxis_);

	double topaxis  = (ViewNode::transformation_->needTopAxis() ) ? haxis_ : 0.25;
	topaxis= 100/absoluteHeight()*topaxis;
	double bottomaxis= 100/absoluteHeight()*haxis_;

	topAxis_ = new TopAxisVisitor(*drawing_);
	topAxis_->height(topaxis);
	topAxis_->frameIt();
	helper.attachTop(topAxis_);
	components_.push_back(topAxis_);
	helper.add(topAxis_);



	bottomAxis_ = new BottomAxisVisitor(*drawing_);
		bottomAxis_->height(bottomaxis);
		bottomAxis_->frameIt();
	components_.push_back(bottomAxis_);
	helper.attachBottom(bottomAxis_);
	helper.add(leftAxis_);
	helper.add(rightAxis_);
		
	if ( legend_)
	{
		tree.legend(legend_);
		if ( !legend_->positional())
		{
			legend_->height(5);
			helper.attachTop(legend_);
			legend_->x(drawing_left_);
			legend_->width(100-drawing_left_-drawing_right_);
			helper.add(legend_);
			legend_->getReady();
		}
		else
		{
			((BasicSceneObject*)legend_)->parent((BasicSceneObject*)this);
			legend_->getReady();
		}
		components_.push_back(legend_);
	}

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

		if ( !(*text)->positional() )
		{
			(*text)->height(15);
			helper.attachNoConstraintTop((*text));
			(*text)->x(drawing_left_);
			(*text)->width(100-drawing_left_-drawing_right_);
			(*text)->getReady();
		}
		else {
			((BasicSceneObject*)(*text))->parent((BasicSceneObject*)this);
			(*text)->getReady();
		}
	}
}



void ViewNode::copy(const ViewNode& other)
{
	transformation_ = other.transformation_;
	drawing_bottom_ = other.drawing_bottom_;
	drawing_top_ = other.drawing_top_;
	drawing_left_ = other.drawing_left_;
	drawing_right_ = other.drawing_right_;
	layout_->frame(*other.layout_);
}



void ViewNode::visit(SceneLayer& tree)
{
	MagLog::dev() << " ViewNode::visit(GraphicsList&) \n" << endl;
	ViewNode::transformation_->init();
	BasicSceneObject::visit(*ViewNode::transformation_); // to set up the automatic style for mapping! 

	// drawing_->layout().blankIt();
	// First the info about the animation overlay! 
	// Basic instantiation of the Animation rules...
	
	if (!rules_) {
		if ( magCompare(animation_, "none")  )
			rules_ =  new NoOverlayAnimationRules();
		else
			rules_ = new AsIsAnimationRules();

		BasicSceneObject::visit(*rules_);
	}

	tree.rules(rules_);
	
	//Here we have the steps! 	
	prepareLayout(tree);	
	if ( items_.empty() )
	{
		push_back(new EmptySceneObject() );
	}
	for ( vector<BasicSceneObject*>::iterator item = items_.begin(); item != items_.end(); ++item)  {
		(*item)->visit(tree, components_);
	}
	if ( drawing_->layoutPtr() )
	{
		drawing_->layout().frameIt();
	}

	if( mode() == interactif )
	{
		PreviewVisitor* preview = new PreviewVisitor();
		tree.push_back(preview);

		preview->transformation(ViewNode::transformation_);	
		preview->height(100-drawing_top_-drawing_bottom_);
		preview->width(100-drawing_left_-drawing_right_);
		preview->visit(*this);		
	}
	if( mode() == basic )
	{
		// We do not need the data animore we clean!
		for ( vector<BasicSceneObject*>::iterator item = items_.begin(); item != items_.end(); ++item)  {
			(*item)->release();

		}
	}
}



XmlViewNode::XmlViewNode()
{
	//displayType_ = INLINE;
}

void XmlViewNode::updateLayout()
{
//	double width    = 100 - drawing_right_ - drawing_left_;
//	double height   = 100 - drawing_top_ - drawing_bottom_;
	
	drawing_bottom_ = ((drawing_bottom_* layout_->height())/100) + layout_->y();
	drawing_left_   = ((drawing_left_* layout_->width())/100 ) + layout_->x();
	double right = 100 - layout_->width() - layout_->x();
	double top = 100 - layout_->height() - layout_->y();
	drawing_right_  = ((drawing_right_* layout_->width())/100) + right ;
	drawing_top_    = ((drawing_top_* layout_->height())/100 ) + top;
	
	layout_->x(0);
	layout_->y(0);
	layout_->width(100);
	layout_->height(100);
}


XmlViewNode::~XmlViewNode()
{
}


void XmlViewNode::getReady()
{
	assert (parent_);
	ViewNode::transformation_ = XmlViewNodeAttributes::transformation_.get();	

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

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

	drawing_bottom_ =mb.percent();
	drawing_left_ = ml.percent();
	
	double wab =  width.absolute()  - mr.absolute() - ml.absolute();
	double hab =  height.absolute()  - mt.absolute() - mb.absolute();

	double waa = wab;
	double haa = hab;
//	MagLog::dev() << "befor aspect ratio -->[" << waa << ", " << haa << "]" << endl;
	MagLog::dev() << "after aspect ratio -->[" << waa << ", " << haa << "]" << endl; 

	// could be usefull if we want to add the option compressed layout! 
	//double w1 = (waa + mr.absolute() + ml.absolute()) / absoluteWidth() *100;
	//double h1 = (haa +  mt.absolute() + mb.absolute()) / absoluteHeight() *100;
	
	double w2 = 100;
	double h2 =100;
	
	if ( fitted_  == "expand" )
	{
		ViewNode::transformation_->fill(waa, haa);
		absoluteRootHeight(haa);
		absoluteRootWidth(waa); 
	}
	if ( fitted_  == "crop" )
	{
		ViewNode::transformation_->aspectRatio(waa, haa);
		absoluteRootWidth(waa); 
		absoluteRootHeight(haa);
	}
	else
	{
		ViewNode::transformation_->aspectRatio(waa, haa);
		w2 = waa / width.absolute() *100;
		h2 = haa / height.absolute() *100;
	}

	drawing_top_ = 100 - drawing_bottom_ - h2;	
	drawing_right_ = 100 - drawing_left_- w2;
	

	layout_->x(left.percent());
	layout_->y(bottom.percent());
	layout_->width(width.percent());
	layout_->height(height.percent());
	
	layout_->display(display_);	
	layout_->frame(true, border_, *border_colour_, border_style_, border_thickness_);

	BasicSceneObject::getReady();
}


void XmlViewNode::print(ostream&) const
{
}


FortranViewNode::FortranViewNode()
{
}


FortranViewNode::~FortranViewNode()
{
}


#define undef(x) x ==-1

class AdjustHelper
{
public:
	AdjustHelper(double top, double bottom, double height, double parent) : 
		top_(top), bottom_(bottom), height_(height), parent_(parent) {}
	~AdjustHelper() {}

	void operator()(double& top, double& bottom, double& height)
	{
		if ( undef(top))
		{
			if ( undef(bottom) )
			{
				if ( undef(height) ) {
					height = height_;
					bottom = bottom_;
					top = top_;
				}
				else { // we adjust to the top!
					top = top_;
					height = height / parent_ *100;
					bottom = 100-top-height;
				}
			}
			else { // bottom is defined
				if ( undef(height) ) {
					bottom = bottom / parent_ *100;
					top = top_;
					height=100-bottom-top;
				}
				else { // we adjust to the top!
					bottom = bottom / parent_ *100;
					height=height / parent_ *100;
					top=100 -height-bottom;
				}			
			} 
		}
		else { // Top is defined 
			if ( undef(bottom) )
			{
				if ( undef(height) ) {
					top = top / parent_*100;
					bottom = bottom_;
					height = 100-top-bottom;
				}
				else {
					top = top / parent_ *100;
					height = height / parent_ *100;
					bottom = 100 -top-height;
				}
			}
			else { // bottom is defined
				if ( undef(height) ) {
					 top = top / parent_ *100;
					 bottom = bottom / parent_ *100;
					 height=100-bottom-top;
				}
				else {
					top = top / parent_ *100;
					bottom = bottom / parent_ *100;
					height=height / parent_ *100;
				}
			}
		}
	}

protected:
	double top_;
	double bottom_;
	double height_;
	double parent_;
};

void FortranViewNode::getReady()
{
	assert (parent_);
	
	ViewNode::transformation_ = FortranViewNodeAttributes::transformation_.get();
//	MagLog::dev()<< *ViewNode::transformation_ << endl;
	double left =  FortranViewNodeAttributes::left_;
	double right =  FortranViewNodeAttributes::right_;
	double top =  FortranViewNodeAttributes::top_;
	double bottom =  FortranViewNodeAttributes::bottom_;
	double width = FortranViewNodeAttributes::width_;	
	double height = FortranViewNodeAttributes::height_;
	animation_ = overlay_;
	
	AdjustHelper vertical(20, 5, 75,  parent_->absoluteHeight());
	AdjustHelper horizontal(5, 5, 90,  parent_->absoluteWidth());
	
	vertical(top, bottom, height);
	horizontal(left, right, width);
	
	double abswidth = width * absoluteWidth()  / 100;
	double absheight =  height * absoluteHeight()  / 100;
	double absx = left * absoluteWidth()  / 100;
	double absy =  bottom * absoluteHeight()  / 100;
	
	MagLog::dev() << "[" << abswidth << ", " << absheight << "]" << endl;
	MagLog::dev() << "[" << width << ", " << height << "]" << endl;

	ViewNode::transformation_->aspectRatio(abswidth, absheight);
	MagLog::dev() << "after aspect ratio -->[" << abswidth << ", " << absheight << "]" << endl; 
	MagLog::dev() << "[" << abswidth << ", " << absheight << "]" << endl;
	MagLog::dev() << "[" << absoluteWidth() << ", " << absoluteWidth() << "]" << endl;

	width = abswidth / absoluteWidth() *100;
	height = absheight / absoluteHeight() *100;

	MagLog::dev() << "[" << width << ", " << height << "]" << endl;
	// This is for alignement.. need further test!
	double x = magCompare(horizontal_, "left") ?  left : 100 - left - width;
	double y = magCompare(vertical_, "bottom") ?  bottom : 100  - top - height;

	drawing_top_ = 100 - y - height;
	drawing_bottom_= y;
	drawing_right_ = 100 - x - width;
	drawing_left_ = x;


	vaxis_ = vertical_axis_with_;
	haxis_ = horizontal_axis_height_;

	
	
	ParameterManager::set("subpage_x_length_internal", abswidth);
	ParameterManager::set("subpage_y_length_internal", absheight);
	ParameterManager::set("subpage_x_position_internal", absx);
	ParameterManager::set("subpage_y_position_internal", absy);

	layout_->frame(true, frame_, *frame_colour_, frame_line_style_, frame_thickness_);

	BasicSceneObject::getReady();
}

void FortranViewNode::print(ostream& out) const
{
	out << "FortranViewNode[";
	BasicSceneObject::print(out);
	FortranViewNodeAttributes::print(out);
	out << "]";
}

BasicSceneNode* FortranViewNode::clone()
{
	FortranViewNode* node = new FortranViewNode();
	//node->copy(*this);
	return node;
}
