#include <iostream>

#include <qapplication.h>

#if QT_VERSION < 0x040000
#include <qwidget.h>
#include <qpixmap.h>
#include <qpen.h>
#include <qpainter.h>
#include <qtimer.h>
#include <qpoint.h>
#include <qptrlist.h>
#include <qeventloop.h>
#include <qimage.h>
#include <qprinter.h>
#include <qpaintdevicemetrics.h>
#include <qpicture.h>
#else
#include <QWidget>
#include <QPixmap>
#include <QImage>
#include <QPicture>
#include <QPen>
#include <QPainter>
#include <QFlags>
#include <QTimer>
#include <QLineF>
#include <QPointF>
#include <QLinkedList>
#include <QMouseEvent>
#include <QResizeEvent>
#include <QCoreApplication>
#include <QPrinter>
#if QT_VERSION >= 0x040300
#include <QSvgGenerator>
#endif
#endif

#include "plplot.h"
#include "plplotP.h"
#include "plstrm.h"
#include <cmath>

#include <sys/time.h>
#include <time.h>

#define PLOT_MODE_RASTER 0
#define PLOT_MODE_SVG 1
#define PLOT_MODE_PS 2

#define PLOT_FONT_FACTOR_RASTER 8./32768.
#define PLOT_FONT_FACTOR_SVG 8./32768.
#define PLOT_FONT_FACTOR_PS 5./32768.*1000

/* Set the Qt Widget to be plotted (with the 'qt' driver) */

void plsetqtdev(void *plotdev);

double PlWidth2QtWidth(double width);

int QtWidth2PlWidth(double width);

#if QT_VERSION < 0x040000

typedef enum ElementType_
{
    LINE,
    POLYLINE,
    POLYGON,
    SET_WIDTH,
    SET_COLOUR,
    SET_SMOOTH,
    TEXT
} ElementType;

struct LineStruct_
{
	PLFLT x1;
	PLFLT x2;
	PLFLT y1;
	PLFLT y2;
};

struct PolylineStruct_
{
	PLINT npts;
	PLFLT* x;
	PLFLT* y;
};

struct ColourStruct_
{
	PLINT R, G, B, A;
};

struct TextStruct_
{
	PLFLT x;
	PLFLT y;
	PLFLT clipxmin;
	PLFLT clipymin;
	PLFLT clipxmax;
	PLFLT clipymax;
	PLFLT rotation;
	PLFLT shear;
	PLFLT stride;
	PLFLT just;
	PLUNICODE* text;
	PLUNICODE fci;
	PLINT len;
	PLFLT chrht;
};

class BufferElement
{
    public:
        
    ElementType Element;
    
    union DataType
    {
        struct LineStruct_* LineStruct;
        
        struct PolylineStruct_* PolylineStruct;
        
        struct ColourStruct_* ColourStruct;
	
	struct TextStruct_* TextStruct;
        
        PLINT intParam;
        PLFLT fltParam;
    } Data;
};
#else
typedef enum ElementType_
{
	LINE,
 POLYLINE,
 POLYGON,
 RECTANGLE,
 SET_WIDTH,
 SET_COLOUR,
 SET_SMOOTH,
 TEXT
} ElementType;

struct ColourStruct_
{
	PLINT R, G, B, A;
};

struct TextStruct_
{
	PLFLT x;
	PLFLT y;
	PLFLT clipxmin;
	PLFLT clipymin;
	PLFLT clipxmax;
	PLFLT clipymax;
	PLFLT rotation;
	PLFLT shear;
	PLFLT stride;
	PLFLT just;
	PLUNICODE* text;
	PLUNICODE fci;
	PLINT len;
	PLFLT chrht;
};

class BufferElement
{
	public:
        
		ElementType Element;
    
		union DataType
		{
			QLineF* Line;
        
			QPolygonF* Polyline;
			
			QRectF * Rect;
        
			struct ColourStruct_* ColourStruct;
	
			struct TextStruct_* TextStruct;
        
			PLINT intParam;
			PLFLT fltParam;
		} Data;
};
#endif

class QtPLWidget : public QWidget
{
	Q_OBJECT

	public:
        
		QtPLWidget(int i_iWidth=500, int i_iHeight=500, double i_dSaveWidth=500., double i_dSaveHeight=500., QWidget * i_wiParent = 0);
        
		~QtPLWidget();
        
		void clearWidget();
		
		void drawLine(short x1, short y1, short x2, short y2);
		void drawPolyline(short * x, short * y, PLINT npts);
		void drawPolygon(short * x, short * y, PLINT npts);
		void setColor(int r, int g, int b, double alpha);
		void setWidth(PLFLT r);

		void drawText(PLStream* pls, EscText* txt);
		
		void renderText(QPainter* p, struct TextStruct_* s, double x_fact, double x_offset, double y_fact, double y_offset);
		
		//void setAntialiasing(bool);
		void setSmooth(bool);
		void setNewSaveSize(double i_dSaveWidth, double i_dSaveHeight);
        
		void savePixmap(QString& name, char * format, int width, int height, int compression);
		
#if QT_VERSION >= 0x040300
		void saveSVG(QString& name, int width, int height, int resolution=150);
#endif
		
		void print(QPrinter& printer);

        void plot(QPainter* p, double x_fact, double y_fact, double x_offset, double y_offset, bool useCosmeticPens=true);
        
		void captureMouse(double * xi, double* yi, double * xf, double * yf);
		void clearBuffer();
		
		double getVirtualWidth(){return m_dVirtualWidth;}
		double getVirtualHeight(){return m_dVirtualHeight;}
		
	signals:
		
		void resized(double ratio);
		
		void clicked(double x, double y, int pos_x, int pos_y); // to start interactive X/T interval adjustment
		
	public slots:
        
		void paintEvent( QPaintEvent * );
		void mouseMoveEvent(QMouseEvent* event);
		void mouseReleaseEvent(QMouseEvent * event);
		void mousePressEvent(QMouseEvent * event);
		void resizeEvent(QResizeEvent *);
        
	protected:
        
		void getPlotParameters(double & io_dXFact, double & io_dYFact, double & io_dXOffset, double & io_dYOffset, double& io_dRatio, double i_dWidth=-1., double i_dHeight=-1.);
		QFont getFont(PLUNICODE code);
		void drawTextInPicture(QPainter*, const QString&);
		QPicture getTextPicture(PLUNICODE fci, PLUNICODE* text, int len, double chrht, double y_fact);
		
#if QT_VERSION < 0x040000
		std::list<BufferElement> m_listBuffer;
#else
		QLinkedList<BufferElement> m_listBuffer;
#endif
        
		int m_iPreviousWidth, m_iPreviousHeight;
		int m_iOldSize;
		double m_dSaveWidth, m_dSaveHeight;
// 		double m_dLineWidthFactor;

		bool m_bAwaitingRedraw;
		QPen m_penCurrent;
#if QT_VERSION < 0x040000
		QBrush m_brushCurrent;
#endif
		QPainter * m_painterP;
		QPixmap * m_pixPixmap;
        
		PLFLT m_dVirtualWidth, m_dVirtualHeight;
		double m_dAspectRatio;
		
		struct
		{
			bool isTracking;
			int step; //1 for first part, 2 for second part
			double cursor_start_x, cursor_start_y;
			double cursor_end_x, cursor_end_y;
		} cursorParameters;
		
		double currentFontScale;
		double currentFontSize;
		bool overlined;
		bool underlined;
		double yOffset;
		double xOffset;
		
		int currentMode;
		
#if QT_VERSION >= 0x040000
		struct
		{
			int r;
			int g;
			int b;
			double alpha;
		} lastColour;
#endif
};

