#include <QMessageBox>
#include <QPainter>
#include <QPainterPath>
#include <QMouseEvent>
#include <QScrollBar>

#include "QFitsMainWindow.h"
#include "QFitsMainView.h"
#include "QFitsWidgetContour.h"
#include "QFitsWidget2D.h"
#include "QFitsWedge.h"
#include "QFitsSingleBuffer.h"
#include "QFitsViewingTools.h"
#include "QFitsToolBar.h"
#include "fits.h"
#include "qtdpuser.h"

extern "C" {
#include "resources/action_stop.xpm"
#include "resources/icon_accept.xpm"
}

void Contour(Fits &d, Fits &z, QPainter &p);
void dpPGCONX(Fits *d, Fits &z, QPainter &p);

//void QFitsWidgetContour::setMin(double m) {
//    getMyBuffer()->setMin(m);
//    contour->update();
//}
//
//void QFitsWidgetContour::setMax(double m) {
//    getMyBuffer()->setMax(m);
//    contour->update();
//}

EscLineEdit::EscLineEdit(QWidget *parent)
    : QLineEdit(parent) {
    QPalette p;
    p.setColor(backgroundRole(), QColor(200,200,200));
    setPalette(p);
    setContentsMargins(0, 0, 40, 0);
    okButton = new QToolButton(this);
    okButton->setIcon(QPixmap(icon_accept));
    okButton->setGeometry(width() - 40, 0, 20, 20);
    okButton->setToolTip("Accept values");
    okButton->setCursor(Qt::ArrowCursor);
    connect(okButton, SIGNAL(clicked()), this, SLOT(okButtonClicked()));

    cancelButton = new QToolButton(this);
    cancelButton->setIcon(QPixmap(action_stop));
    cancelButton->setGeometry(width() - 20, 0, 20, 20);
    cancelButton->setToolTip("Cancel values");
    cancelButton->setCursor(Qt::ArrowCursor);
    connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancelButtonClicked()));
}

void EscLineEdit::keyPressEvent(QKeyEvent *k) {
    if (k->key() == Qt::Key_Escape) {
        emit escapePressed();
        QPalette p;
        p.setColor(backgroundRole(), QColor(200,200,200));
        setPalette(p);
    } else {
        QPalette p;
        p.setColor(backgroundRole(), QColor(255,255,255));
        setPalette(p);
        QLineEdit::keyPressEvent(k);
    }
}

void EscLineEdit::resizeEvent(QResizeEvent *r) {
    okButton->setGeometry(width() - 2*height(), 0, height(), height());
    cancelButton->setGeometry(width() - height(), 0, height(), height());
}

void EscLineEdit::okButtonClicked() {
    QPalette p;
    p.setColor(backgroundRole(), QColor(255,255,255));
    setPalette(p);
    emit returnPressed();
}

void EscLineEdit::cancelButtonClicked() {
    QPalette p;
    p.setColor(backgroundRole(), QColor(200,200,200));
    setPalette(p);
    emit escapePressed();
}

dpContour::dpContour(QWidget *parent, QFitsWidgetContour *parent2)
    : QWidget(parent) {
    myParent = parent2;
    setMouseTracking(true);
    nlevels = 10;
    levels = new Fits();
    applyZoom();
}

void dpContour::paintEvent(QPaintEvent *e) {
//    QReadLocker locker(&buffersLock);

    if (myParent->manualLevels) {
// TODO QT6
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
				QStringList l = myParent->levels->text().split(",", QString::SkipEmptyParts);
#else
        QStringList l = myParent->levels->text().split(",", Qt::SkipEmptyParts);
#endif /* QT_VERSION */
        levels->create(l.size(), 1, R8);
        for (int i = 0; i < l.size(); i++) {
            levels->r8data[i] = l[i].toDouble();
        }
    } else {
        levels->create(nlevels-1, 1, R8);
        switch (myParent->getMyBuffer()->getImageScalingMethod()) {
            case 0: // linear
                for (int i = 0; i < nlevels-1; i++) {
                    levels->r8data[i] = (i+1)/(double)nlevels;
                }
                break;
            case 1: // logarithmic
                for (int i = 0; i < nlevels-1; i++) {
                    levels->r8data[i] = pow(10., (i+1)/2.-(double)nlevels/2.);
                }
                break;
            case 2: // square root
                for (int i = 0; i < nlevels-1; i++) {
                    levels->r8data[i] = pow(2., (i+1)/2.-(double)nlevels/2.);
                }
                break;
            default:
                break;
        }

        levels->norm();
        levels->mul(myParent->getMyBuffer()->getImageMaxValue() - myParent->getMyBuffer()->getImageMinValue());
        levels->add(myParent->getMyBuffer()->getImageMinValue());

        QString text;
        for (int i = 0; i < levels->Naxis(1); i++) {
            text.append(QString::number(levels->ValueAt(i)));
            if (i != levels->Naxis(1) - 1) {
                text.append(",");
            }
        }
        myParent->levels->setText(text);
    }

    QPainter p;
    p.begin(this);
    p.setRenderHint(QPainter::Antialiasing);
    p.drawRect(0, 0, width(), height());

    T.reset();
    T.translate(0, height());
    double zoomFactor = myParent->getMyBuffer()->getZoomFactor();
    T.scale(zoomFactor, -zoomFactor);

    int rot = myParent->getMyBuffer()->getRotation(),
        dx  = myParent->getMyBuffer()->Naxis(1),
        dy  = myParent->getMyBuffer()->Naxis(2);
    switch (rot) {
    case 90:
        T.rotate(90);
        T.translate(0, -dy);
        break;
    case 180:
        T.rotate(180);
        T.translate(-dx, -dy);
        break;
    case 270:
        T.rotate(270);
        T.translate(-dx, 0);
        break;
    default:
        break;
    }

    if (myParent->getMyBuffer()->getFlipX()) {
        if (rot == 0 || rot == 180) {
            T.translate(dx, 0);
            T.scale(-1, 1);
        } else {
            T.translate(0, dy);
            T.scale(1, -1);
        }
    }
    if (myParent->getMyBuffer()->getFlipY()) {
        if (rot == 0 || rot == 180) {
            T.translate(0, dy);
            T.scale(1, -1);
        } else {
            T.translate(dx, 0);
            T.scale(-1, 1);
        }
    }
    T.translate(-0.5, -0.5);
//    Contour(cdata, levels, p);
//    dpPGCONX(contourParent->getMyBuffer()->getFitsData(), levels, p);
    paintContour(p, T);
//    dpPGCONX(contourParent->getMyBuffer()->getFitsData(), *levels, p);
    trans = p.combinedTransform().inverted();
    p.end();

    QRect r = trans.mapRect(visibleRegion().boundingRect());
//    myParent->getMyBuffer()->setXcenter(r.center().x());
//    myParent->getMyBuffer()->setYcenter(r.center().y());
    myParent->getMyBuffer()->setWidthVisible(r.width());
    myParent->getMyBuffer()->setHeightVisible(r.height());

    if (myParent->scroller->horizontalScrollBar()->isVisible()) {
        double sx = myParent->scroller->horizontalScrollBar()->value() + myParent->width() / 2;
        myParent->getMyBuffer()->setXcenter(sx / zoomFactor);
    }
    if (myParent->scroller->verticalScrollBar()->isVisible()) {
        double sy = myParent->scroller->verticalScrollBar()->value() + myParent->scroller->height() / 2;
        myParent->getMyBuffer()->setYcenter(myParent->getMyBuffer()->Naxis(2) - sy / zoomFactor);
    }

    fitsMainWindow->updateTotalVisibleRect();

    myParent->getMyBuffer()->notifyCombiner();
}

void dpContour::mouseMoveEvent(QMouseEvent *m) {
    if (!hasFocus()) {
        setFocus();
    }

    QFitsSingleBuffer *sb = myParent->getMyBuffer();
    fitsMainWindow->setActualSB(sb);
    if (m->buttons() == Qt::LeftButton) {
        QPoint mypos = myParent->mapToParent(mapToParent(m->pos()));
        nlevels = (int)((double)(myParent->parentWidget()->height()-mypos.y()) / (double)myParent->parentWidget()->height() * 30.);
        if (nlevels < 2) {
            nlevels = 2;
        }
        update();
        QFitsWidget2D *fw2D = sb->getQFitsWidget2D();
        if (fw2D != NULL) {
            sb->setBrightnessContrast((int)((float)mypos.x() / (float)myParent->parentWidget()->width() * 1000.),
                                      (int)((float)mypos.y() / (float)myParent->parentWidget()->height() * 1000.));
        }
    } else {
        QPoint  p = trans.map(m->pos());
        int     x = p.x(),
                y = p.y();
        if (!(buffersLock.tryLockForRead())) {
            return;
        }

        if ((sb == NULL) ||
            (sb->getDpData()->type != typeFits) ||
            (sb->getDpData()->fvalue == NULL))
        {
            // do nothing
        } else {
            fitsMainWindow->updateTools(x, y, x, y);
        }
        buffersLock.unlock();
    }
}

void dpContour::mousePressEvent(QMouseEvent *m) {
    if (m->button() == Qt::LeftButton) {
        fitsMainWindow->main_view->setCurrentBufferFromWidget(myParent->getMyBuffer());
    }
}

void dpContour::keyPressEvent( QKeyEvent *e ) {
    QPoint pos = cursor().pos();
    QCursor mycursor = cursor();

    if (cursor().shape() != Qt::WaitCursor) {
        if (!(e->modifiers() & Qt::ControlModifier) &&
            !(e->modifiers() & Qt::AltModifier) &&
            !(e->modifiers() & Qt::ShiftModifier))
        {
            switch (e->key()) {
            case Qt::Key_Up:
                pos -= QPoint(0, 1);
                mycursor.setPos(pos);
                break;
            case Qt::Key_Down:
                pos += QPoint(0, 1);
                mycursor.setPos(pos);
                break;
            case Qt::Key_Left:
                pos -= QPoint(1, 0);
                mycursor.setPos(pos);
                break;
            case Qt::Key_Right:
                pos += QPoint(1, 0);
                mycursor.setPos(pos);
                break;
            default:
                break;
            }
        }
    }
}

void dpContour::enterEvent(QEvent *e) {
    setFocus();
    myParent->enterBuffer();
}

void dpContour::leaveEvent (QEvent *e) {
    myParent->leaveBuffer();
}

void dpContour::paintContour(QPainter &p, QTransform &T) {
    QFitsSingleBuffer *sb = myParent->getMyBuffer();
    if ((sb != NULL) &&
        (sb->getDpData()->type == typeFits) &&
        (sb->getDpData()->fvalue != NULL))
    {
        p.setWorldTransform(T);
        QReadLocker locker(&buffersLock);
        dpPGCONX(sb->getDpData()->fvalue, *levels, p);
    }
}

void dpContour::applyZoom() {
    QFitsSingleBuffer *sb = myParent->getMyBuffer();

    if (sb != NULL) {
        double zoomFactor = sb->getZoomFactor();
        int imagewidth = sb->Naxis(1) * zoomFactor,
            imageheight = sb->Naxis(2) * zoomFactor;

        if (sb->getRotation() == 90 || sb->getRotation() == 270) {
            int tmp = imagewidth;
            imagewidth = imageheight;
            imageheight = tmp;
        }
        if (imagewidth != width() || imageheight != height()) {
            double xcenter = sb->getXcenter();
            double ycenter = sb->getYcenter();
            resize(imagewidth, imageheight);
            myParent->scroller->horizontalScrollBar()->setSliderPosition(xcenter * zoomFactor - myParent->scroller->horizontalScrollBar()->width() / 2);
            myParent->scroller->verticalScrollBar()->setSliderPosition((sb->Naxis(2) - ycenter) * zoomFactor- myParent->scroller->verticalScrollBar()->height() / 2);
        } else {
            update();
        }
    }
}

QFitsWidgetContour::QFitsWidgetContour(QFitsBaseBuffer *bb) :
                                                         QFitsBaseWidget(bb)
{
    manualLevels = false;
    levels = new EscLineEdit(this);
    levels->setGeometry(0, 0, width(), 20);
    scroller = new QScrollArea(this);
    contour = new dpContour(scroller, this);
    scroller->setAlignment(Qt::AlignCenter);
    scroller->setWidget(contour);
    scroller->setGeometry(0, 20, width(), height() - 20);
    levels->show();
    scroller->show();
    contour->show();

    connect(levels, SIGNAL(returnPressed()), this, SLOT(newManualLevels()));
    connect(levels, SIGNAL(escapePressed()), this, SLOT(discardManualLevels()));
}

/*!
  Release allocated resources
*/

QFitsWidgetContour::~QFitsWidgetContour()
{
}

void QFitsWidgetContour::orientationChanged() {
    contour->applyZoom();
}

void QFitsWidgetContour::setImageCenter(double xcenter, double ycenter) {
    setScrollerImageCenter(xcenter,  myBuffer->Naxis(2) - ycenter);
}

void QFitsWidgetContour::setZoom(double zoomFactor) {
    scroller->hide();
    getMyBuffer()->setZoomFactor(zoomFactor);
    contour->applyZoom();
    scroller->show();
}

void QFitsWidgetContour::resizeEvent(QResizeEvent *e) {
    levels->setGeometry(0, 0, width(), 20);
    scroller->setGeometry(0, 20, width(), height() - 20 - fitsMainWindow->main_view->getWedge()->height());
    contour->update();
//    delete im;
//    im = new QImage(e->size().width(), e->size().height(), QImage::Format_RGB32);
}

void QFitsWidgetContour::mouseMoveEvent(QMouseEvent *m) {
    QMouseEvent e(m->type(), contour->mapFrom(this, m->pos()), m->button(), m->buttons(), m->modifiers());
    contour->extMouseMoved(&e);
}

void QFitsWidgetContour::setScrollerImageCenter(double x, double y) {
    double nx = x * myBuffer->getZoomFactor(),
           ny = y * myBuffer->getZoomFactor();

    scroller->horizontalScrollBar()->setSliderPosition(nx - width() / 2. + 0.5);
    scroller->verticalScrollBar()->setSliderPosition(ny - scroller->height() / 2. + 0.5);

    myBuffer->setXcenter(x);
    myBuffer->setYcenter(myBuffer->Naxis(2) - y);

    update();
}

void QFitsWidgetContour::newManualLevels() {
    manualLevels = true;
    contour->update();
}

void QFitsWidgetContour::discardManualLevels() {
    manualLevels = false;
    QPalette p;
    p.setColor(levels->backgroundRole(), QColor(200,200,200));
    levels->setPalette(p);
    contour->update();
}

void QFitsWidgetContour::updateScaling() {
    QFitsSingleBuffer *sb = getMyBuffer();
    if ((sb != NULL) &&
        (sb->getDpData()->type == typeFits) &&
        (sb->getDpData()->fvalue != NULL))
    {
        QReadLocker locker(&buffersLock);
        myBuffer->calculateMinMax(sb->getDpData()->fvalue);
        discardManualLevels();
    }
}

void QFitsWidgetContour::setScaling(int scaling) {
    discardManualLevels();
}

/*
   Derivation from the fortran version of CONREC by Paul Bourke
   d               ! matrix of data to contour
   ilb,iub,jlb,jub ! index bounds of data matrix
   x               ! data matrix column coordinates
   y               ! data matrix row coordinates
   nc              ! number of contour levels
   z               ! contour levels in increasing order
*/
//void Contour(double **d,int ilb,int iub,int jlb,int jub,
//   double *x,double *y,int nc,double *z,
//   void (*ConrecLine)(double,double,double,double,double))
void Contour(Fits &d, Fits &z, QPainter &p)
{
#define xsect(p1,p2) (h[p2]*xh[p1]-h[p1]*xh[p2])/(h[p2]-h[p1])
#define ysect(p1,p2) (h[p2]*yh[p1]-h[p1]*yh[p2])/(h[p2]-h[p1])

    int ilb = 1, jlb = 1;
    int iub = d.Naxis(1)-1, jub = d.Naxis(2)-1;
    int nc = z.Nelements();
   int m1,m2,m3,case_value;
   double dmin,dmax,x1=0,x2=0,y1=0,y2=0;
   int i,j,k,m;
   double h[5];
   int sh[5];
   double xh[5],yh[5];
   int im[4] = {0,1,1,0},jm[4]={0,0,1,1};
   int castab[3][3][3] = {
     { {0,0,8},{0,2,5},{7,6,9} },
     { {0,3,4},{1,10,1},{4,3,0} },
     { {9,6,7},{5,2,0},{8,0,0} }
   };
   double temp1,temp2;
   double *x, *y;

   x = (double *)malloc(iub * sizeof(double));
   for (i = 0; i < iub; i++) x[i] = (double)i;
   y = (double *)malloc(jub * sizeof(double));
   for (i = 0; i < jub; i++) y[i] = (double)i;

   for (j=(jub-1);j>=jlb;j--) {
      for (i=ilb;i<=iub-1;i++) {
         temp1 = MIN(d[d.C_I(i,j)],d[d.C_I(i,j+1)]);
         temp2 = MIN(d[d.C_I(i+1,j)],d[d.C_I(i+1,j+1)]);
         dmin  = MIN(temp1,temp2);
         temp1 = MAX(d[d.C_I(i,j)],d[d.C_I(i,j+1)]);
         temp2 = MAX(d[d.C_I(i+1,j)],d[d.C_I(i+1,j+1)]);
         dmax  = MAX(temp1,temp2);
         if (dmax < z[0] || dmin > z[nc-1])
            continue;
         for (k=0;k<nc;k++) {
            if (z[k] < dmin || z[k] > dmax)
               continue;
            for (m=4;m>=0;m--) {
               if (m > 0) {
                  h[m]  = d[d.C_I(i+im[m-1],j+jm[m-1])]-z[k];
                  xh[m] = x[i+im[m-1]];
                  yh[m] = y[j+jm[m-1]];
               } else {
                  h[0]  = 0.25 * (h[1]+h[2]+h[3]+h[4]);
                  xh[0] = 0.50 * (x[i]+x[i+1]);
                  yh[0] = 0.50 * (y[j]+y[j+1]);
               }
               if (h[m] > 0.0)
                  sh[m] = 1;
               else if (h[m] < 0.0)
                  sh[m] = -1;
               else
                  sh[m] = 0;
            }

            /*
               Note: at this stage the relative heights of the corners and the
               centre are in the h array, and the corresponding coordinates are
               in the xh and yh arrays. The centre of the box is indexed by 0
               and the 4 corners by 1 to 4 as shown below.
               Each triangle is then indexed by the parameter m, and the 3
               vertices of each triangle are indexed by parameters m1,m2,and m3.
               It is assumed that the centre of the box is always vertex 2
               though this isimportant only when all 3 vertices lie exactly on
               the same contour level, in which case only the side of the box
               is drawn.
                  vertex 4 +-------------------+ vertex 3
                           | \               / |
                           |   \    m-3    /   |
                           |     \       /     |
                           |       \   /       |
                           |  m=2    X   m=2   |       the centre is vertex 0
                           |       /   \       |
                           |     /       \     |
                           |   /    m=1    \   |
                           | /               \ |
                  vertex 1 +-------------------+ vertex 2
            */
            /* Scan each triangle in the box */
            for (m=1;m<=4;m++) {
               m1 = m;
               m2 = 0;
               if (m != 4)
                  m3 = m + 1;
               else
                  m3 = 1;
               if ((case_value = castab[sh[m1]+1][sh[m2]+1][sh[m3]+1]) == 0)
                  continue;
               switch (case_value) {
               case 1: /* Line between vertices 1 and 2 */
                   x1 = xh[m1];
                   y1 = yh[m1];
                   x2 = xh[m2];
                   y2 = yh[m2];
                   break;
               case 2: /* Line between vertices 2 and 3 */
                   x1 = xh[m2];
                   y1 = yh[m2];
                   x2 = xh[m3];
                   y2 = yh[m3];
                   break;
               case 3: /* Line between vertices 3 and 1 */
                   x1 = xh[m3];
                   y1 = yh[m3];
                   x2 = xh[m1];
                   y2 = yh[m1];
                   break;
               case 4: /* Line between vertex 1 and side 2-3 */
                   x1 = xh[m1];
                   y1 = yh[m1];
                   x2 = xsect(m2,m3);
                   y2 = ysect(m2,m3);
                   break;
               case 5: /* Line between vertex 2 and side 3-1 */
                   x1 = xh[m2];
                   y1 = yh[m2];
                   x2 = xsect(m3,m1);
                   y2 = ysect(m3,m1);
                   break;
               case 6: /* Line between vertex 3 and side 1-2 */
                   x1 = xh[m3];
                   y1 = yh[m3];
                   x2 = xsect(m3,m2);
                   y2 = ysect(m3,m2);
                   break;
               case 7: /* Line between sides 1-2 and 2-3 */
                   x1 = xsect(m1,m2);
                   y1 = ysect(m1,m2);
                   x2 = xsect(m2,m3);
                   y2 = ysect(m2,m3);
                   break;
               case 8: /* Line between sides 2-3 and 3-1 */
                   x1 = xsect(m2,m3);
                   y1 = ysect(m2,m3);
                   x2 = xsect(m3,m1);
                   y2 = ysect(m3,m1);
                   break;
               case 9: /* Line between sides 3-1 and 1-2 */
                   x1 = xsect(m3,m1);
                   y1 = ysect(m3,m1);
                   x2 = xsect(m1,m2);
                   y2 = ysect(m1,m2);
                   break;
               case 10:
                   x1 = y1 = x2 = y2 = -1.;
                   break;
               default:
                   break;
               }

               /* Finally draw the line */
//               ConrecLine(x1,y1,x2,y2,z[k]);
//               if (x1 > ilb+1 && y1 > jlb+1 && x2 < iub-1  && y2 < jub-1 && x2 > ilb+1 && y2 > jlb+1 && x1 < iub-1  && y1 < jub-1)
                if (sqrt(fabs(x2-x1)*fabs(x2-x1)+fabs(y2-y1)*fabs(y2-y1)) < 5)
                   p.drawLine(x1, y1, x2, y2);
            } /* m */
         } /* k - contour */
      } /* i */
   } /* j */

   free(x);
   free(y);
}

#define MAXEMX 100
#define MAXEMY 100
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4

bool FLAGS[MAXEMX+1][MAXEMY+1][3];

QPainterPath dpCN01(Fits &Z, int MX, int MY, int IA, int IB, int JA, int JB, double Z0, int IS, int JS, int SDIR) {
    int DIR, I, J, II, JJ;
    double X, Y, STARTX, STARTY;
    QPainterPath plot;

    I = IS;
    J = JS;
    DIR = SDIR;
    II = 1+I-IA;
    JJ = 1+J-JA;
    if (DIR == UP || DIR == DOWN) {
        X = (double)I + (Z0-Z.ValueAt(Z.F_I(I,J)))/(Z.ValueAt(Z.F_I(I+1,J))-Z.ValueAt(Z.F_I(I,J)));
        Y = (double)J;
    } else {
        X = (double)I;
        Y = (double)J + (Z0-Z.ValueAt(Z.F_I(I,J)))/(Z.ValueAt(Z.F_I(I,J+1))-Z.ValueAt(Z.F_I(I,J)));
    }

    plot.moveTo(X, Y);
    STARTX = X;
    STARTY = Y;

    l100:
    II = 1 + I - IA;
    JJ = 1 + J - JA;

    switch (DIR) {
    case UP:
        FLAGS[II][JJ][1] = false;
        if (J == JB) {
            return plot;
        } else if (FLAGS[II][JJ][2]) {
            DIR = LEFT;
            goto l200;
        } else if (FLAGS[II+1][JJ][2]) {
            DIR = RIGHT;
            I = I+1;
            goto l200;
        } else if (FLAGS[II][JJ+1][1]) {
            J = J+1;
            goto l250;
        } else {
            goto l300;
        }
        break;
    case DOWN:
        FLAGS[II][JJ][1] = false;
        if (J == JA) {
            return plot;
        } else if (FLAGS[II+1][JJ-1][2]) {
            DIR = RIGHT;
            I = I+1;
            J = J-1;
            goto l200;
        } else if (FLAGS[II][JJ-1][2]) {
            DIR = LEFT;
            J = J-1;
            goto l200;
        } else if (FLAGS[II][JJ-1][1]) {
            J = J-1;
            goto l250;
        } else {
            goto l300;
        }
        break;
    case LEFT:
        FLAGS[II][JJ][2] = false;
        if (I == IA) {
            return plot;
        } else if (FLAGS[II-1][JJ][1]) {
            DIR = DOWN;
            I = I-1;
            goto l250;
        } else if (FLAGS[II-1][JJ+1][1]) {
            DIR = UP;
            I = I-1;
            J = J+1;
            goto l250;
        } else if (FLAGS[II-1][JJ][2]) {
            I = I-1;
            goto l200;
        } else {
            goto l300;
        }
        break;
    case RIGHT:
        FLAGS[II][JJ][2] = false;
        if (I == IB) {
            return plot;
        } else if (FLAGS[II][JJ+1][1]) {
            DIR = UP;
            J = J+1;
            goto l250;
        } else if (FLAGS[II][JJ][1]) {
            DIR = DOWN;
            goto l250;
        } else if (FLAGS[II+1][JJ][2]) {
            I = I+1;
            goto l200;
        } else {
            goto l300;
        }
        break;
    default:
        break;
    }

    l200:
    X = (double)I;
    Y = (double)J + (Z0-Z.ValueAt(Z.F_I(I,J)))/(Z.ValueAt(Z.F_I(I,J+1))-Z.ValueAt(Z.F_I(I,J)));
    plot.lineTo(X,Y);
    goto l100;

    l250:
    X = (double)I + (Z0-Z.ValueAt(Z.F_I(I,J)))/(Z.ValueAt(Z.F_I(I+1,J))-Z.ValueAt(Z.F_I(I,J)));
    Y = (double)J;
    plot.lineTo(X,Y);
    goto l100;

    l300:
    plot.lineTo(STARTX, STARTY);
    return plot;
}

bool dpRANGE(double P, double P1, double P2) {
    return ((P > MIN(P1,P2)) && (P <= MAX(P1,P2)) && (P1 != P2));
}

void dpPGCNSC(Fits &Z, int MX, int MY, int IA, int IB, int JA, int JB, double Z0, QPainter &p) {
//    bool FLAGS[MAXEMX+1][MAXEMY+1][2+1];
    int I, J, II, JJ, DIR;
    double Z1, Z2, Z3, P, P1, P2;
    QPainterPath path;

    if ((IB-IA+1) > MAXEMX || (JB-JA+1) > MAXEMY) {
        dp_debug("PGCNSC - array index range exceeds built-in limit of 100");
        return;
    }
    for (I = IA; I <= IB; I++) {
        II = I - IA + 1;
        for (J = JA; J <= JB; J++) {
            JJ = J-JA+1;
            Z1 = Z.ValueAt(Z.F_I(I,J));
            FLAGS[II][JJ][1] = false;
            FLAGS[II][JJ][2] = false;
            if (I < IB) {
                Z2 = Z.ValueAt(Z.F_I(I+1,J));
                if (dpRANGE(Z0,Z1,Z2)) FLAGS[II][JJ][1] = true;
            }
            if (J < JB) {
                Z3 = Z.ValueAt(Z.F_I(I,J+1));
                if (dpRANGE(Z0,Z1,Z3)) FLAGS[II][JJ][2] = true;
            }
        }
    }
// Bottom edge.
    J = JA;
    JJ = J-JA+1;
    for (I = IA; I <= IB-1; I++) {
        II = I-IA+1;
        if (FLAGS[II][JJ][1] && (Z.ValueAt(Z.F_I(I,J)) > Z.ValueAt(Z.F_I(I+1,J)))) {
            path = dpCN01(Z, MX, MY, IA, IB, JA, JB, Z0, I, J, UP);
            p.drawPath(path);
        }
    }
// Right edge.
    I = IB;
    II = I-IA+1;
    for (J=JA; J <= JB-1; J++) {
        JJ = J-JA+1;
        if (FLAGS[II][JJ][2] && (Z.ValueAt(Z.F_I(I,J)) > Z.ValueAt(Z.F_I(I,J+1)))) {
            path = dpCN01(Z, MX, MY, IA, IB, JA, JB, Z0, I, J, LEFT);
            p.drawPath(path);
        }
    }
// Top edge.
    J = JB;
    JJ = J-JA+1;
    for (I=IB-1; I >= IA; I--) {
        II = I-IA+1;
        if (FLAGS[II][JJ][1] && (Z.ValueAt(Z.F_I(I+1,J)) > Z.ValueAt(Z.F_I(I,J)))) {
            path = dpCN01(Z, MX, MY, IA, IB, JA, JB, Z0, I, J, DOWN);
            p.drawPath(path);
        }
    }
// Left edge.
    I = IA;
    II = I-IA+1;
    for (J=JB-1; J >= JA; J--) {
        JJ = J-JA+1;
        if (FLAGS[II][JJ][2] && (Z.ValueAt(Z.F_I(I,J+1)) > Z.ValueAt(Z.F_I(I,J)))) {
            path = dpCN01(Z, MX, MY, IA, IB, JA, JB, Z0, I, J, RIGHT);
            p.drawPath(path);
        }
    }
//
    for (I=IA+1; I <= IB-1; I++) {
        II = I-IA+1;
        for (J=JA+1; J <= JB-1; J++) {
            JJ = J-JA+1;
            if (FLAGS[II][JJ][1]) {
                DIR = UP;
                if (Z.ValueAt(Z.F_I(I+1,J)) > Z.ValueAt(Z.F_I(I,J))) DIR = DOWN;
                path = dpCN01(Z, MX, MY, IA, IB, JA, JB, Z0, I, J, DIR);
                p.drawPath(path);
            }
        }
    }
}

void dpPGCONX(Fits *A, Fits &C, QPainter &p) {
    int IDIM = A->Naxis(1);
    int JDIM = A->Naxis(2);
    int I1 = 1, I2 = IDIM, J1 = 1, J2 = JDIM;
    int NC = C.Nelements();

    bool STYLE = true;

    int I;
    int NNX,NNY, KX,KY, KI,KJ, IA,IB, JA,JB, LS, PX, PY;
    QPen pen = p.pen();
    QPen pensolid, pendashed;
    pensolid.setStyle(Qt::SolidLine);
    pensolid.setColor(pen.color());
//    pensolid.setWidth(pen.width());
    pensolid.setWidth(0);
    pendashed.setStyle(Qt::DashLine);
    pendashed.setColor(pen.color());
//    pendashed.setWidth(pen.width());
    pendashed.setWidth(0);

    NNX = I2-I1+1;
    NNY = J2-J1+1;
    KX = MAX(1,(NNX+MAXEMX-2)/(MAXEMX-1));
    KY = MAX(1,(NNY+MAXEMY-2)/(MAXEMY-1));
    PX = (NNX+KX-1)/KX;
    PY = (NNY+KY-1)/KY;
    for (KI = 1; KI <= KX; KI++) {
        IA = I1 + (KI-1)*PX;
        IB = MIN(I2, IA + PX);
        for (KJ = 1; KJ <= KY; KJ++) {
            JA = J1 + (KJ-1)*PY;
            JB = MIN(J2, JA + PY);
//             Draw the contours in one panel.
            if (STYLE) p.setPen(pensolid);
            for (I = 1; I <= labs(NC); I++) {
                if (STYLE && (C.ValueAt(C.F_I(I)) < 0.0)) p.setPen(pendashed);
                dpPGCNSC(*A,IDIM,JDIM,IA,IB,JA,JB,C.ValueAt(C.F_I(I)), p);
                if (STYLE) p.setPen(pensolid);
            }
        }
    }
}
