/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "ReprojectService.h"

#include <GeoRectangularProjection.h>
#include <LegendVisitor.h>
//#include <ImagePlotting.h>
#include <GribSatelliteInterpretor.h>
#include "MvException.h"
#include <PaperPoint.h>

ReprojectService::ReprojectService(std::string in, std::string out, double rx, double ry, double /*x1*/, double /*x2*/, double /*y1*/, double /*y2*/) :
    gribIn_(in),
    gribOut_(out),
    resX_(rx),
    resY_(ry)
{
    // empty
}

bool ReprojectService::execute()
{
    magics::GribDecoder grib;

    //     magics::ParameterManager::set("subpage_lower_left_latitude",y1_);
    //     magics::ParameterManager::set("subpage_upper_right_latitude",y2_);
    //     magics::ParameterManager::set("subpage_lower_left_longitude",x1_);
    //     magics::ParameterManager::set("subpage_upper_right_longitude",x2_);
    //     magics::GeoRectangularProjection projection; // fixed for now!

    //     magics::ImagePlotting tool;
    grib.file_name_ = gribIn_;
    grib.open(nullptr);  // it's not clear what purpose the NULL has here - it should be a grib handle, but it seems to be unused
    magics::GribSatelliteInterpretor gribInterpreter;
    //     magics::GeoRectangularProjection transformation;
    //     magics::RasterData rdin;  //input data
    //     gribInterpreter.interpretAsRaster(grib, rdin, transformation);
    //     RasterData<GeoPoint>& rdin = grib.raster();  //input data
    //     RasterData  rdout;                 //output data

    //     if ( !tool.reproject(rdin,rdout, projection, resX_, resY_) )
    //     {
    //	      MagLog::dev() << "ERROR: ImagePlotting<P>::createOutputRaster:" << tool << "\n";
    //	      return false;
    //     }


    gribInterpreter.interpretAsMatrix(grib);
    Matrix* matrix = grib.u();
    if (!matrix) {
        std::string err = "Reprojection-> InterpretAsMatrix failed on file ";
        err += gribIn_;
        throw MvException(err.c_str());
    }

    if (!gribit(*matrix, grib)) {
        return true;
    }
    else {
        throw MvException("Reprojection-> Request failed");
    }
}

int ReprojectService::gribit(magics::Matrix& matrix, magics::GribDecoder& sourceGrib)
{
    // copy from examples of the ecCodes!
    int ret = 0;
    double* values;
    long numberOfPointsAlongAParallel, numberOfPointsAlongAMeridian, numberOfPoints, i, j, k;
    grib_handle* hnew;

    // int option_flags = GRIB_DUMP_FLAG_VALUES |
    //     //GRIB_DUMP_FLAG_OPTIONAL |
    //     GRIB_DUMP_FLAG_READ_ONLY;

    double missingValueInGrib;
    const double missingValueInImage = 65535;  // -1 as an unsigned short.
                                               // Set in TeDecoderMemory::resetMemory()
                                               // and TeRasterParams.h. Missing values in the
                                               // 'image' variable will be set to this.
                                               // NOTE: this could change if the data type
                                               // in ImagePlotting is changed from TeUNSIGNEDSHORT.
    {
        grib_handle* h;

        h = grib_handle_new_from_samples(nullptr, "GRIB1");
        if (!h)
            throw MvException("Reprojection-> Unable to create grib handle");

        // set date, time, parameter, etc
        hnew = grib_util_sections_copy(sourceGrib.id(), h, GRIB_SECTION_PRODUCT, &ret);

        if (ret)
            throw MvException("Reprojection-> Error when copying GRIB product section.");

        grib_handle_delete(h);
    }

    grib_get_double(hnew, "missingValue", &missingValueInGrib);
    grib_set_long(hnew, "bitmapPresent", 1);  // tell the GRIB that it will contain missing values

    // Set bounding box
    grib_set_double(hnew, "latitudeOfFirstGridPointInDegrees", matrix.minY());   // getUpperRightCorner().y());
    grib_set_double(hnew, "longitudeOfFirstGridPointInDegrees", matrix.minX());  // getLowerLeftCorner().x());

    grib_set_double(hnew, "latitudeOfLastGridPointInDegrees", matrix.maxY());   // getLowerLeftCorner().y());
    grib_set_double(hnew, "longitudeOfLastGridPointInDegrees", matrix.maxX());  // getUpperRightCorner().x());

    grib_set_double(hnew, "jDirectionIncrementInDegrees", resX_);
    grib_set_double(hnew, "iDirectionIncrementInDegrees", resY_);

    // Set matrix size
    numberOfPointsAlongAParallel = matrix.columns();
    numberOfPointsAlongAMeridian = matrix.rows();
    grib_set_long(hnew, "numberOfPointsAlongAParallel", numberOfPointsAlongAParallel);
    grib_set_long(hnew, "numberOfPointsAlongAMeridian", numberOfPointsAlongAMeridian);

    numberOfPoints = numberOfPointsAlongAMeridian * numberOfPointsAlongAParallel;
    values = (double*)malloc(numberOfPoints * sizeof(double));
    for (j = 0; j < numberOfPointsAlongAMeridian; j++) {
        for (i = 0; i < numberOfPointsAlongAParallel; i++) {
            k = i + numberOfPointsAlongAParallel * j;
            if (matrix[k] != missingValueInImage)
                values[k] = matrix[k];
            else
                values[k] = missingValueInGrib;
        }
    }

    grib_set_double_array(hnew, "values", values, numberOfPoints);

    if (hnew) {
        // grib_dump_content(h,stdout,"serialize",option_flags, 0);
        FILE* out = fopen(gribOut_.c_str(), "w");
        if (out) {
            const void* mesg;
            size_t mesg_len;
            grib_get_message(hnew, &mesg, &mesg_len);
            fwrite(mesg, 1, mesg_len, out);
            fclose(out);
        }
        else {
            free(values);
            perror(gribOut_.c_str());
            return 1;
        }
        grib_handle_delete(hnew);
    }
    else {
        free(values);
        throw MvException("Reprojection-> Unable to create grib_handle");
    }

    free(values);
    return 0;
}
