/******************************** 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 Layout.h
    \brief Definition of the Template class Layout.
    
    Magics Team - ECMWF 2004
    
    Started: Fri 30-Jan-2004
    
    Changes:
    
*/

#ifndef Layout_H
#define Layout_H

#include "magics.h"
#include "BasicGraphicsObject.h"

#include "Colour.h"


namespace magics {


class PaperPoint;
class BasicSceneObject;
class AnimationRules;
class AnimationStep;
class LayoutVisitor;


class DriverInfo {
public:
	DriverInfo(double x, double y, double width, double height): 
		x_(x), y_(y), width_(width), height_(height) {}
	~DriverInfo() {}
	DriverInfo() {}

	void print(ostream& s) const
	{
		s << "<driver_info";
		s << " x = \'" << x_ << "\'";
		s << " y = \'" << y_ <<  "\'";
		s << " width = \'" << width_ << "\'";
		s << " height = \'" << height_ << "\'";
		s << "/>";
	}

	double x_;
	double y_;
	double width_;
	double height_;
	
	friend ostream& operator<<(ostream& s,const DriverInfo& p)
		{ p.print(s); return s; }
};

class Layout;
class LayoutVisitor;

class LayoutFrame 
{
public:
	LayoutFrame();
	~LayoutFrame();
	void blank(Layout&);
	void frame(Layout&);
protected:
	int thickness_;
	LineStyle style_;
	Colour colour_;
	bool blanking_;
	bool visible_;
	friend class Layout;
};


class StartPage : public BasicGraphicsObject
{
public:
	StartPage() {}
	virtual ~StartPage() {}
	void redisplay(const BaseDriver& driver) const;
};


class EndPage : public BasicGraphicsObject
{
public:
	EndPage() {}
	virtual ~EndPage() {}
	void redisplay(const BaseDriver& driver) const;
};


class Layout : public BasicGraphicsObjectContainer
{
public:
	Layout();
	virtual ~Layout();

	virtual Layout*  clone() const;
	virtual Layout*  newLayout() const { return new Layout(); }
	
	bool reproject(BasicGraphicsObjectContainer&) const;	
	void redisplay(const BaseDriver& driver) const;
	void redisplay(AnimationStep&, const BaseDriver& driver); 

	double x()    const { return x_ ; } // percentage of the parent
	double y()    const { return y_ ; };  // percentage of the parent
	double width()	 const { return width_ ; };  // percentage of the parent 
	double height() const { return height_ ; };  // percentage of the parent 
	
	void x(double x ) { x_ = x; }
	void y(double y ) { y_ = y; }
	void absoluteWidth(double);
	void absoluteHeight(double);
	void width(double width ) { width_ = width; }
	void height(double height ) { height_ = height; }
	
	
	void display(DisplayType display) { display_ = display; }
	DisplayType display() const { return display_; }

/* Methods needed to calculate the percentage when unit in cm!*/
	double absoluteX()      		const; //absolute position from the root
	double absoluteY()      		const; //absolute position from the root
	double absoluteWidth()  const; //absolute position from the root
	double absoluteHeight()  const; //absolute position from the root
	
	bool buildTree(const Layout&, unsigned int, const BaseDriver&) const;
	void release(); // Release the graphical objects!
	
	virtual Layout* execute(AnimationStep& step,  const Layout* visitor);

	
	double minX()  const { return xmin_; }
	double maxX()  const { return xmax_; }
	double minY()  const { return ymin_; }
	double maxY()  const { return ymax_; }
	void minX(double xmin)  { xmin_ = xmin; }
	void maxX(double xmax) {  xmax_ = xmax; }
	void minY(double ymin)  { ymin_ = ymin; }
	void maxY(double ymax) { ymax_ = ymax; }
	
	
	void setCoordinates(double xmin, double xmax, double ymin, double ymax) {
		xmin_ = xmin; xmax_ = xmax; ymin_ = ymin; ymax_ = ymax; 
	}
	
	void pushDriverInfo(double x, double y, double width, double height) const 
	{
		driverInfos_.push_back(DriverInfo(x, y, width, height));
	} 
	
	void getDriverInfo(double& x, double& y, double& width, double& height)
	{
		if ( !driverInfos_.empty() ) {
			x = driverInfos_.front().x_;
			y = driverInfos_.front().y_;
			width = driverInfos_.front().width_;
			height = driverInfos_.front().height_;
		}
		else {
			if (parent_)
				parent_->getDriverInfo(x, y, width, height);
		}
	}

	void frame(const Layout& other)
	{
		frame_.blanking_ = other.frame_.blanking_;
		frame_.visible_ = other.frame_.visible_;
		frame_.colour_ = other.frame_.colour_;
		frame_.style_ = other.frame_.style_;
		frame_.thickness_ = other.frame_.thickness_;
	}
	
	void frame(bool blank, bool visible, const Colour& colour, LineStyle style, int thickness) {
		frame_.blanking_ = blank;
		frame_.visible_ = visible;
		frame_.colour_ = colour;
		frame_.style_ = style;
		frame_.thickness_ = thickness;
	}
	
	void zoomable(bool zoom = true) { zoomable_ = zoom; }
	bool isZoomable() const { return zoomable_; }
	
	void setNavigable() { navigable_ = true; }
	bool isNavigable() const { return navigable_; }

	const string& id() const { return id_; }
	void id(const string& id) { id_ = id; }
    int zoomLevels() const { return zoomLevels_; }
    void zoomLevels(int levels)  { zoomLevels_ = levels; }
    int zoomCurrentLevel() const { return zoomCurrentLevel_; }
    void zoomCurrentLevel(int level)  { zoomCurrentLevel_ = level; }
 
    void animationRules(AnimationRules* rules) { animationRules_ = rules; }
    AnimationRules* animationRules() const { return animationRules_; }
    
    const Transformation& transformation() const { 
    	assert(transformation_); 
    	return *transformation_; 
    }
    virtual bool childOfRoot() const { assert(parent_); return parent_->root(); }
    
    void transformation(Transformation* );
    void transformation(Layout& layout) const
    { layout.transformation(transformation_);  }
  
    void frameIt() { frame_.frame(*this); }
    void blankIt() { frame_.blank(*this); }
    void resolve(bool resolve) { resolve_  = resolve; }
    bool resolve() const { return resolve_; }
    virtual void resize(double, double) {}
protected:
	//! Method to print string about this class on to a stream of type ostream (virtual).
	virtual void print(ostream&) const; 

	BasicSceneObject* owner_;
	AnimationRules*   animationRules_; // will be deleted in the destructor!
	Transformation*   transformation_; // Just a reference! 

	//Dimension of the layout in percentage of the parent! 
	double width_; 
	double height_;
	double x_;
	double y_;
	DisplayType display_;
	
	// coordiantes system used in this layout!
	double xmin_;
	double xmax_;
	double ymin_; 
	double ymax_;
	
	bool zoomable_;
	bool navigable_;
	// Zoom information : only used if mode is ZOOMABLE
	int zoomLevels_;
	int zoomCurrentLevel_;
	string id_;
	
	bool resizable_;
	bool resolve_;
	
	LayoutFrame frame_;
		
	static vector<DriverInfo> driverInfos_;
	friend class LayoutVisitor;
	friend class TopAxisVisitor;
	friend class BottomAxisVisitor;
	friend class LeftAxisVisitor;
	friend class RightAxisVisitor;
	
private:
	//! Copy constructor - No copy allowed
	Layout(const Layout&);
	//! Overloaded << operator to copy - No copy allowed
	Layout& operator=(const Layout&);
// 
};


class PreviewLayout : public Layout 
{
public:
	PreviewLayout();
	virtual ~PreviewLayout();
	void redisplay(const BaseDriver& driver) const;
	
};
class LegendLayout : public Layout
{
public:
	LegendLayout();
	virtual ~LegendLayout();
	void redisplay(const BaseDriver& driver) const;

};
class HistoLayout : public Layout 
{
public:
	HistoLayout();
	virtual ~HistoLayout();
	void redisplay(const BaseDriver& driver) const;
};

class SceneLayout : public Layout
{
public:
	SceneLayout();
	virtual ~SceneLayout();
	void redisplay(const BaseDriver& driver) const;
	
};



class MagnifierLayout : public Layout 
{
public:
	MagnifierLayout();
	virtual ~MagnifierLayout();
	void redisplay(const BaseDriver& driver) const;
	virtual void redisplay(const BaseDriver&, vector<PaperPoint>&,float,float) const {}
};

class RootLayout : public Layout
{
public:
	RootLayout(double width, double height);
	virtual ~RootLayout();
	double absoluteWidth() const { return absoluteWidth_; }
	double absoluteHeight() const { return absoluteHeight_; }
	virtual bool childOfRoot() { return false; }
	void redisplay(const BaseDriver& driver) const;

	 virtual void resize(double width, double height) { absoluteWidth_ = width; absoluteHeight_ = height; }

protected:
	double absoluteWidth_;
	double absoluteHeight_; 

};
class BasicLayout : public Layout
{
public:
	BasicLayout();
	virtual ~BasicLayout();
	bool buildTree(const Layout&, unsigned int, const BaseDriver&) const;
};
class LayoutVisitor;


class LayoutHelper
{
public:
	LayoutHelper();
	~LayoutHelper();
	void add(LayoutVisitor*);
	void attachTop(LayoutVisitor*);
	void attachNoConstraintTop(LayoutVisitor*);
	void attachLeft(LayoutVisitor*);
	void attachRight(LayoutVisitor*);
	void attachBottom(LayoutVisitor*);

protected :
	virtual void print(ostream&) const; 
	double xmin_;
	double xmax_;
	double ymin_;
	double ymax_;
	friend ostream& operator<<(ostream& s,const LayoutHelper& p)
		{ p.print(s); return s; }
};

} // namespace magics
#endif
