/**********************************************************************************************
    Copyright (C) 2009 Oliver Eichler oliver.eichler@gmx.de

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

**********************************************************************************************/

#include "CMapRaster.h"

#include <QtGui>
#include <gdal_priv.h>
#include <ogr_spatialref.h>

CMapRaster::CMapRaster(const QString& filename, QObject * parent)
: IMap(parent)
, zoomfactor(1.0)
, xscale(0)
, yscale(0)
, x(0)
, y(0)
, xsize(0)
, ysize(0)
, xref1(0)
, yref1(0)
, xref2(0)
, yref2(0)
, dataset(0)
, xCenter(0)
, yCenter(0)
{
    dataset = (GDALDataset*)GDALOpen(filename.toLocal8Bit(),GA_ReadOnly);
    if(dataset == 0) {
        //         QMessageBox::warning(0, tr("Error..."), tr("Failed to load file: %1").arg(filename));
        return;
    }

    char str[1024];
    strncpy(str,dataset->GetProjectionRef(),sizeof(str));
    char * ptr = str;
    OGRSpatialReference oSRS;
    oSRS.importFromWkt(&ptr);
    oSRS.exportToProj4(&ptr);

    if(strcmp("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ", ptr)) {
        //         QMessageBox::warning(0, tr("Error..."), tr("Failed to load file: %1\nBad projection: '%2'").arg(filename).arg(ptr));
        free(ptr);
        delete dataset;
        dataset = 0;
        return;
    }

    if(ptr) free(ptr);

    GDALRasterBand * pBand;
    pBand = dataset->GetRasterBand(1);
    if(pBand == 0) {
        //         QMessageBox::warning(0, tr("Error..."), tr("Failed to load file: %1\nNo data.").arg(filename));
        delete dataset;
        dataset = 0;
        return;
    }

    if(pBand->GetColorInterpretation() ==  GCI_PaletteIndex ) {
        GDALColorTable * pct = pBand->GetColorTable();
        for(int i=0; i < pct->GetColorEntryCount(); ++i) {
            const GDALColorEntry& e = *pct->GetColorEntry(i);
            colortable << qRgba(e.c1, e.c2, e.c3, e.c4);
        }
    }
    else {
        //         QMessageBox::warning(0, tr("Error..."), tr("Failed to load file: %1\nNo 8bit pallette GeoTiff.").arg(filename));
        delete dataset;
        dataset = 0;

        return;
    }

    xsize = dataset->GetRasterXSize();
    ysize = dataset->GetRasterYSize();

    double adfGeoTransform[6];
    dataset->GetGeoTransform( adfGeoTransform );

    xscale  = adfGeoTransform[1] * DEG_TO_RAD;
    yscale  = adfGeoTransform[5] * DEG_TO_RAD;

    xref1   = adfGeoTransform[0] * DEG_TO_RAD;
    yref1   = adfGeoTransform[3] * DEG_TO_RAD;

    xref2   = xref1 + xsize * xscale;
    yref2   = yref1 + ysize * yscale;

    x = xref1;
    y = yref1;

    maparea = QRectF(xref1, yref1, xsize * xscale, ysize * yscale);

    qDebug() << "Open file:" << filename;
    qDebug() << "ref1" << yref1 * RAD_TO_DEG <<  xref1 * RAD_TO_DEG;
    qDebug() << "ref2" << yref2 * RAD_TO_DEG <<  xref2 * RAD_TO_DEG;
    qDebug() << "size" << xsize << "x" << ysize;

    bestScale  = numScales - 1;
    qreal  diff = 100;
    for(qint32 i = 0; i< numScales; ++i) {
        if(qAbs(xscale - scales[i]) < diff) {
            diff = qAbs(xscale - scales[i]);
            bestScale  = i;
        }
    }

    zoom(bestScale);
}


CMapRaster::~CMapRaster()
{
    if(dataset) delete dataset;

    qDebug() << "CMapRaster::~CMapRaster()";
}


bool CMapRaster::readHeader(const QString& filename, QPointF& ref1, QPointF& ref2, qint32& zmin, qint32& zmax)
{
    GDALDataset * dataset = (GDALDataset*)GDALOpen(filename.toLocal8Bit(),GA_ReadOnly);
    if(dataset == 0) {
        return false;
    }

    char str[1024];
    strncpy(str,dataset->GetProjectionRef(),sizeof(str));
    char * ptr = str;
    OGRSpatialReference oSRS;
    oSRS.importFromWkt(&ptr);
    oSRS.exportToProj4(&ptr);

    if(strcmp("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ", ptr)) {
        if(ptr)free(ptr); delete dataset;
        return false;
    }

    if(ptr) free(ptr);

    qreal xsize = dataset->GetRasterXSize();
    qreal ysize = dataset->GetRasterYSize();

    double adfGeoTransform[6];
    dataset->GetGeoTransform( adfGeoTransform );

    qreal xscale  = adfGeoTransform[1] * DEG_TO_RAD;
    qreal yscale  = adfGeoTransform[5] * DEG_TO_RAD;

    qreal xref1   = adfGeoTransform[0] * DEG_TO_RAD;
    qreal yref1   = adfGeoTransform[3] * DEG_TO_RAD;

    qreal xref2   = xref1 + xsize * xscale;
    qreal yref2   = yref1 + ysize * yscale;

    ref1 = QPointF(xref1, yref1);
    ref2 = QPointF(xref2, yref2);

    qint32 idx  = numScales - 1;
    qreal  diff = 100;
    for(qint32 i = 0; i< numScales; ++i) {
        if(qAbs(xscale - scales[i]) < diff) {
            diff = qAbs(xscale - scales[i]);
            idx  = i;
        }
    }

    zmin = idx;
    zmax = idx + MAX_SCALE_OFFSET;
    if(zmax >= numScales) zmax = numScales - 1;

    delete dataset;
    return true;
}


void CMapRaster::resize(const QSize& size)
{
    IMap::resize(size);

    xCenter = size.width()  >> 1;
    yCenter = size.height() >> 1;
}


void CMapRaster::convertRad2Pt(qreal& u, qreal& v)
{
    u = (u - x) / (xscale * zoomfactor);
    v = (v - y) / (yscale * zoomfactor);
}


void CMapRaster::convertPt2Rad(qreal& u, qreal& v)
{
    u = x + u * xscale * zoomfactor;
    v = y + v * yscale * zoomfactor;
}


void CMapRaster::move(const QPoint& old, const QPoint& next)
{

    qreal width     = buffer.width()  * xscale * zoomfactor;
    qreal height    = buffer.height() * yscale * zoomfactor;
    // move top left point by difference
    x              += (old.x() - next.x()) * xscale * zoomfactor;
    y              += (old.y() - next.y()) * yscale * zoomfactor;

    viewport        = QRectF(x, y, width,  height);

    needsRedraw = true;

    qreal u = xCenter;
    qreal v = yCenter;

    convertPt2Rad(u,v);
    if(!maparea.contains(u,v)) {
        qDebug() << "CMapRaster::move" << maparea << viewport << maparea.intersects(viewport);
        qDebug() << "deleteLater()";
        emit sigAboutToDelete();
        deleteLater();
    }
}


int CMapRaster::zoom(int idx)
{

    if(idx < 0) idx = 0;
    if(idx >= numScales) idx = numScales - 1;

    qreal desired = scales[idx];

    zoomfactor  = desired/xscale;
    if(zoomfactor >= 1.0) {
        zoomfactor = (int)(zoomfactor + 0.5);
    }

    needsRedraw = true;

    if((idx > 6) && ((idx > (bestScale + MAX_SCALE_OFFSET)) || (idx < bestScale))) {
        qDebug() << "CMapRaster::zoom" << (bestScale + MAX_SCALE_OFFSET) << idx << bestScale;
        qDebug() << "deleteLater()";
        emit sigAboutToDelete();
        deleteLater();
    }

    return idx;
}


void CMapRaster::draw(QPainter& p)
{
    if(needsRedraw) {
        draw();
    }
    p.drawPixmap(0,0,buffer);

}


void CMapRaster::draw()
{
    buffer.fill(QColor("#ffffcc"));

    if(!dataset) return;

    QPainter p;
    p.begin(&buffer);

    QRectF intersect = viewport.intersected(maparea);

    // x/y offset [pixel] into file matrix
    qreal xoff = (intersect.left()   - xref1)/xscale;
    qreal yoff = (intersect.bottom() - yref1)/yscale;

    // number of x/y pixel to read
    qreal pxx =  intersect.width()  / xscale;
    qreal pxy = -intersect.height() / yscale;

    // the final image width and height in pixel
    qint32 w   = (qint32)(pxx/zoomfactor) & 0xFFFFFFFC;
    qint32 h   = (qint32)(pxy/zoomfactor);

    GDALRasterBand * pBand;
    pBand = dataset->GetRasterBand(1);

    QImage img(QSize(w,h),QImage::Format_Indexed8);
    img.setColorTable(colortable);

    CPLErr err = pBand->RasterIO(GF_Read
        ,(int)xoff,(int)yoff
        ,pxx,pxy
        ,img.bits()
        ,w,h
        ,GDT_Byte,0,0);

    if(!err) {
        double xx = (intersect.left() - x) / (xscale * zoomfactor), yy = (intersect.bottom() - y)  / (yscale * zoomfactor);
        p.drawPixmap(xx, yy, QPixmap::fromImage(img));
    }

    p.end();
}
