#include <stdlib.h>
#include <qimage.h>
#include <qpointarray.h>

#include "qtpgplot.h"

QtPgplot *pgplotWindows[10];
int npts = 0;
int nVertices = 0;
int cursormode = 0;
int xmouseref, ymouseref, cursorx, cursory, rubberband = 0;
int rubberx1, rubberx2, rubbery1, rubbery2, rubbermode;
QPointArray polypoints;
QMutex mousemutex;
bool wantmouse = FALSE;
QSemaphore *pg_sem = NULL;
QPainter mousepainter;

#define MAXCOLOR	256
#define MAXCOLOR1	(MAXCOLOR-1)

// FIXME: do modes 3-4
void drawRubberBand(int fromx, int fromy, int tox, int toy, int mode) {
	fromy = -fromy + pgplotWindows[WINDOW_ID]->height();
	switch (mode) {
		case 1:
			mousepainter.drawLine(tox, toy, fromx, fromy);
			break;
		case 2:
			mousepainter.drawLine(tox, toy, tox, fromy);
			mousepainter.drawLine(fromx, toy, fromx, fromy);
			mousepainter.drawLine(tox, toy, fromx, toy);
			mousepainter.drawLine(tox, fromy, fromx, fromy);
			break;
		case 3:
			break;
		case 4:
			break;
		case 5:
			mousepainter.drawLine(0, toy, pgplotWindows[WINDOW_ID]->width(), toy);
			break;
		case 6:
			mousepainter.drawLine(tox, 0, tox, pgplotWindows[WINDOW_ID]->height());
			break;
		case 7:
			mousepainter.drawLine(0, toy, pgplotWindows[WINDOW_ID]->width(), toy);
			mousepainter.drawLine(tox, 0, tox, pgplotWindows[WINDOW_ID]->height());
			break;
		default: break;
	}
	if (mode > 0) {
		if (rubberband == 1) rubberband = 0;
		else rubberband = 1;
		rubberx1 = fromx;
		rubberx2 = tox;
		rubbery1 = fromy;
		rubbery2 = toy;
		rubbermode = mode;
	}
}

// FIXME: do modes 3-4
void deleteRubberBand() {
	if ((rubberx1 == -1) && (rubberx2 == -1) && (rubbery1 == -1) && (rubbery2 == -1)) return;
	switch (rubbermode) {
		case 1:
			mousepainter.drawLine(rubberx1, rubbery1, rubberx2, rubbery2);
			break;
		case 2:
			mousepainter.drawLine(rubberx1, rubbery1, rubberx1, rubbery2);
			mousepainter.drawLine(rubberx2, rubbery1, rubberx2, rubbery2);
			mousepainter.drawLine(rubberx1, rubbery1, rubberx2, rubbery1);
			mousepainter.drawLine(rubberx1, rubbery2, rubberx2, rubbery2);
			break;
		case 3:
			break;
		case 4:
			break;
		case 5:
			mousepainter.drawLine(0, rubbery2, pgplotWindows[WINDOW_ID]->width(), rubbery2);
			break;
		case 6:
			mousepainter.drawLine(rubberx2, 0, rubberx2, pgplotWindows[WINDOW_ID]->height());
			break;
		case 7:
			mousepainter.drawLine(0, rubbery2, pgplotWindows[WINDOW_ID]->width(), rubbery2);
			mousepainter.drawLine(rubberx2, 0, rubberx2, pgplotWindows[WINDOW_ID]->height());
			break;
		default: break;
	}
	rubberx1 = rubberx2 = rubbery1 = rubbery2 = -1;
}

void QtPgParse(void) {
	int i, n1, n2, n3, n4;
	QPainter p;

	switch (pg_cmd) {
		case 6:
			pg_width = pgplotWindows[WINDOW_ID]->width();
			pg_height = pgplotWindows[WINDOW_ID]->height();
			break;
		case 9:
			if (pgplotWindows[WINDOW_ID] == NULL) {
				pgplotWindows[WINDOW_ID] = new QtPgplot(NULL, 0, Qt::WType_TopLevel);
			}
			pgplotWindows[WINDOW_ID]->setCaption("QtPgplot Window");
			pgplotWindows[WINDOW_ID]->setDefaultColors();
			if (!pgplotWindows[WINDOW_ID]->isVisible()) pgplotWindows[WINDOW_ID]->show();
		break;
		case 11: {
			n1 = (int)(pg_rbuf[0] + 0.5);
			n2 = (int)(pg_rbuf[1] + 0.5);
			pgplotWindows[WINDOW_ID]->resize(n1, n2);
			QWMatrix m(1., 0., 0., -1., 0., (double)n2);
//			pgplotWindows[WINDOW_ID]->setDefaultColors();
			pgplotWindows[WINDOW_ID]->pixmap->fill(pgplotWindows[WINDOW_ID]->colors[0]);
			pgplotWindows[WINDOW_ID]->p.begin(pgplotWindows[WINDOW_ID]->pixmap);
			pgplotWindows[WINDOW_ID]->p.setWorldMatrix(m);
			pgplotWindows[WINDOW_ID]->p.setWorldXForm(TRUE);
			pgplotWindows[WINDOW_ID]->curcol = 1;
			pgplotWindows[WINDOW_ID]->p.setPen(pgplotWindows[WINDOW_ID]->colors[pgplotWindows[WINDOW_ID]->curcol]);
			pgplotWindows[WINDOW_ID]->p.setBrush(pgplotWindows[WINDOW_ID]->colors[pgplotWindows[WINDOW_ID]->curcol]);
				 }
			break;
		case 12:
			n1 = (int)(pg_rbuf[0] + 0.5);
			n2 = (int)(pg_rbuf[1] + 0.5);
			n3 = (int)(pg_rbuf[2] + 0.5);
			n4 = (int)(pg_rbuf[3] + 0.5);
			pgplotWindows[WINDOW_ID]->p.drawLine(n1, n2, n3, n4);
			break;
		case 13:
			n1 = (int)(pg_rbuf[0] + 0.5);
			n2 = (int)(pg_rbuf[1] + 0.5);
			pgplotWindows[WINDOW_ID]->p.drawPoint(n1, n2);
			break;
		case 14:
			pgplotWindows[WINDOW_ID]->p.end();
			pgplotWindows[WINDOW_ID]->setUpdatesEnabled(TRUE);
			pgplotWindows[WINDOW_ID]->repaint(FALSE);
			pgplotWindows[WINDOW_ID]->setUpdatesEnabled(FALSE);
			break;
		case 15:
			n1 = (int)(pg_rbuf[0] + 0.5);
			pgplotWindows[WINDOW_ID]->curcol = n1;
			pgplotWindows[WINDOW_ID]->p.setPen(pgplotWindows[WINDOW_ID]->colors[pgplotWindows[WINDOW_ID]->curcol]);
			pgplotWindows[WINDOW_ID]->p.setBrush(pgplotWindows[WINDOW_ID]->colors[pgplotWindows[WINDOW_ID]->curcol]);
			break;
		case 16:
			pgplotWindows[WINDOW_ID]->setUpdatesEnabled(TRUE);
			pgplotWindows[WINDOW_ID]->repaint(FALSE);
			pgplotWindows[WINDOW_ID]->setUpdatesEnabled(FALSE);
			break;
		case 17:
			pg_mousechar = ' ';
			cursormode = (int)(pg_rbuf[4] + 0.5);
			if (cursormode > 0) {
				pgplotWindows[WINDOW_ID]->setMouseTracking(TRUE);
				xmouseref = (int)(pg_rbuf[2] + 0.5);
				ymouseref = (int)(pg_rbuf[3] + 0.5);
				cursorx = (int)(pg_rbuf[0] + 0.5);
				cursory = (int)(pg_rbuf[1] + 0.5);
				mousepainter.begin(pgplotWindows[WINDOW_ID]);
				mousepainter.setRasterOp(Qt::XorROP);
				mousepainter.setPen(pgplotWindows[WINDOW_ID]->colors[pgplotWindows[WINDOW_ID]->curcol]);
//				drawRubberBand(xmouseref, ymouseref, cursorx, cursory, cursormode);
			}
			(*pg_sem)++;
			(*pg_sem)++;
			if (cursormode > 0) {
				pgplotWindows[WINDOW_ID]->setMouseTracking(FALSE);
				deleteRubberBand();
				mousepainter.end();
			}
			(*pg_sem)--;
			break;
		case 20:
			if (nVertices == 0) {
				nVertices = (int)(pg_rbuf[0] + 0.5);
				npts = 0;
				polypoints.resize(nVertices);
			} else {
				polypoints.setPoint(npts, (int)(pg_rbuf[0] + 0.5), (int)(pg_rbuf[1] + 0.5));
				npts++;
			}
			if (npts == nVertices) {
				pgplotWindows[WINDOW_ID]->p.drawPolygon(polypoints);
				polypoints.resize(0);
				npts = 0;
				nVertices = 0;
			}
			break;
		case 21:
			n1 = (int)(pg_rbuf[0] + 0.5);
			if ((n1 < 0) || (n1 >= MAXCOLOR)) break;

			n2 = (int)(pg_rbuf[1] * MAXCOLOR1 + 0.5);
			n3 = (int)(pg_rbuf[2] * MAXCOLOR1 + 0.5);
			n4 = (int)(pg_rbuf[3] * MAXCOLOR1 + 0.5);
			pgplotWindows[WINDOW_ID]->colors[n1]  = QColor(n2, n3, n4);
			if (n1 == 0) pgplotWindows[WINDOW_ID]->setBackgroundColor(QColor(n2, n3, n4));
			pgplotWindows[WINDOW_ID]->colortableChanged = TRUE;
			if (n1 == pgplotWindows[WINDOW_ID]->curcol) pgplotWindows[WINDOW_ID]->p.setPen(QColor(n2, n3, n4));
			break;
		case 24:
			n1 = (int)(pg_rbuf[0] + 0.5);
			n2 = (int)(pg_rbuf[1] + 0.5);
			n3 = (int)(pg_rbuf[2] + 0.5);
			n4 = (int)(pg_rbuf[3] + 0.5);
			pgplotWindows[WINDOW_ID]->p.drawRect(n1, n2, n3-n1, n4-n2);
			break;
		case 26:
			n1 = (int)(pg_rbuf[0] + 0.5);
			n2 = (int)(pg_rbuf[1] + 0.5);
			for (i = 2; i < pg_nbuf; i++) {
				pgplotWindows[WINDOW_ID]->p.setPen(pgplotWindows[WINDOW_ID]->colors[(unsigned char)(pg_rbuf[i] + 0.5)]);
				pgplotWindows[WINDOW_ID]->p.drawPoint(n1, n2);
				n1++;
			}
			break;
		case 29:
			n1 = (int)(pg_rbuf[0] + 0.5);
			if ((n1 < 0) || (n1 >= MAXCOLOR)) break;
			pg_rbuf[1] = pgplotWindows[WINDOW_ID]->colors[n1].red() / (float)MAXCOLOR1;
			pg_rbuf[2] = pgplotWindows[WINDOW_ID]->colors[n1].green() / (float)MAXCOLOR1;
			pg_rbuf[3] = pgplotWindows[WINDOW_ID]->colors[n1].blue() / (float)MAXCOLOR1;
			break;
		default:
			break;
	}
}

QtPgplot::QtPgplot(QWidget *parent, const char *name, WFlags f)
: QWidget(parent, name, f) {
	if (pg_sem == 0) pg_sem = new QSemaphore(1);
	setDefaultColors();
	pixmap = new QPixmap(640, 480);
	pixmap->fill(colors[0]);
	resize(640, 480);
	setUpdatesEnabled(FALSE);
	setBackgroundColor(colors[0]);
}

void QtPgplot::paintEvent(QPaintEvent *p) {
	if (pixmap != NULL) {
		if (colortableChanged) {
			QImage im;
			QPainter pa;
			int i;

			im = *pixmap;
			im.setNumColors(256);
			for (i = 0; i < 256; i++) {
				im.setColor(i, qRgb(colors[i].red(), colors[i].green(), colors[i].blue()));
			}
			pa.begin(this);
			pa.drawImage(0, 0, im);
			pa.end();
			*pixmap = im;
			colortableChanged = FALSE;
		} else {
			bitBlt(this, 0, 0, pixmap);
		}
	}
}

void QtPgplot::resizeEvent(QResizeEvent *r) {
	pixmap->resize(r->size().width(), r->size().height());
}

void QtPgplot::closeEvent(QCloseEvent *e) {
	hide();
}

void QtPgplot::mousePressEvent(QMouseEvent *m) {
	if ((pg_sem->total() == 1) && (this == pgplotWindows[WINDOW_ID])) {
		if (m->button() == LeftButton) pg_mousechar = 'A';
		else if (m->button() == MidButton) pg_mousechar = 'D';
		else if (m->button() == RightButton) pg_mousechar = 'X';
		pg_mousex = m->x();
		pg_mousey = -m->y() + pgplotWindows[WINDOW_ID]->height();
		(*pg_sem)--;
	}
}

void QtPgplot::mouseMoveEvent(QMouseEvent *m) {
	if (hasMouseTracking()) {
		deleteRubberBand();
		cursorx = m->x();
		cursory = m->y();
		drawRubberBand(xmouseref, ymouseref, cursorx, cursory, cursormode);
	}
}

void QtPgplot::keyPressEvent(QKeyEvent *k) {
	if ((pg_sem->total() == 1) && (this == pgplotWindows[WINDOW_ID])) {
		pg_mousechar = k->key();
		(*pg_sem)--;
	}
}

void QtPgplot::setDefaultColors(void) {
	int i;

	for (i = 0; i < 256; i++) colors[i] = QColor(i, i, i);
	colors[0]  = QColor(0, 0, 0);//0x00000000;  // current background color
	colors[1]  = QColor(255, 255, 255);//0x00ffffff;  // current foreground color
	colors[2]  = QColor(255, 0, 0);//0x000000ff;  // Red  
	colors[3]  = QColor(0, 255, 0);//0x0000ff00;  // Green
	colors[4]  = QColor(0, 0, 255);//0x00ff0000;  // Blue 
	colors[5]  = QColor(0, 255, 255);//0x00ffff00;  // Cyan (Green + Blue)
	colors[6]  = QColor(255, 0, 255);//0x00ff00ff;  // Magenta (Red + Blue)
	colors[7]  = QColor(255, 255, 0);//0x0000ffff;  // Yellow (Red + Green)
	colors[8]  = QColor(255, 127, 0);//0x00007fff;  // Orange (Red + Yellow)
	colors[9]  = QColor(127, 255, 0);//0x0000ff7f;  // Green + Yellow
	colors[10] = QColor(0, 255, 127);//0x007fff00;  // Green + Cyan
	colors[11] = QColor(0, 127, 255);//0x00ff7f00;  // Blue + Cyan
	colors[12] = QColor(127, 0, 255);//0x00ff007f;  // Blue + Magenta
	colors[13] = QColor(255, 0, 127);//0x007f00ff;  // Red + Magenta
	colors[14] = QColor(85, 85, 85);//0x00555555;  // Dark Gray
	colors[15] = QColor(170, 170, 170);//0x00aaaaaa;  // Light Gray
	colortableChanged = TRUE;
}
