#include <stdlib.h>
#include <string.h>
#include "test_raster3d_lib.h"
#include "grass/interpf.h"

static int test_coordinate_transform(void);
static int test_region(void);

/* *************************************************************** */
/* Perfrome the coordinate transformation tests ****************** */
/* *************************************************************** */
int unit_test_coordinate_transform(void)
{
    int sum = 0;

    G_message("\n++ Running raster3d coordinate transform unit tests ++");

    sum += test_coordinate_transform();
    sum += test_region();

    if (sum > 0)
        G_warning("\n-- raster3d coordinate transform unit tests failure --");
    else
        G_message("\n-- raster3d coordinate transform unit tests finished "
                  "successfully --");

    return sum;
}

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

int test_coordinate_transform(void)
{
    int sum = 0;

    double north, east, top;
    int col = 0, row = 0, depth = 0;

    RASTER3D_Region region, default_region;
    RASTER3D_Map *map = NULL;

    /* We need to set up a specific region for the new raster3d map.
     * First we safe the default region. */
    Rast3d_get_window(&default_region);
    Rast3d_region_copy(&region, &default_region);

    region.bottom = 0.0;
    region.top = 1000;
    region.south = 1000;
    region.north = 8500;
    region.west = 5000;
    region.east = 10000;
    region.rows = 15;
    region.cols = 10;
    region.depths = 5;

    Rast3d_adjust_region(&region);

    map = Rast3d_open_new_opt_tile_size("test_coordinate_transform",
                                        RASTER3D_USE_CACHE_XYZ, &region,
                                        FCELL_TYPE, 32);

    /* The window is the same as the map region ... of course */
    Rast3d_set_window_map(map, &region);

    G_message("Test the upper right corner, coordinates must be col = 9, row = "
              "0, depth = 4");

    /*
       ROWS
       1000 1500 2000 2500 3000 3500 4000 4500 5000 5500 6000 6500 7000 7500
       8000 8500
       |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|
       15   14   13   12   11   10    9    8    7    6    5    4    3    2    1
       0

       COLS
       5000 5500 6000 6500 7000 7500 8000 8500 9000 9500 10000
       |....|....|....|....|....|....|....|....|....|....|
       0    1    2    3    4    5    6    7    8    9   10

       DEPTHS
       0   200  400  600  800  1000
       |....|....|....|....|....|
       0    1    2    3    4    5
     */
    north = 8499.9;
    east = 9999.9;
    top = 999.9;

    Rast3d_location2coord(&(map->region), north, east, top, &col, &row, &depth);
    printf("Rast3d_location2coord col %i row %i depth %i\n", col, row, depth);
    if (region.cols - 1 != col || 0 != row || region.depths - 1 != depth) {
        G_message("Error in Rast3d_location2coord");
        sum++;
    }

    G_message("Test the lower left corner, coordinates must be col = 0 row = "
              "15 depth = 0");

    north = 1000.0;
    east = 5000.0;
    top = 0.0;

    Rast3d_location2coord(&(map->region), north, east, top, &col, &row, &depth);
    printf("Rast3d_location2coord col %i row %i depth %i\n", col, row, depth);
    if (0 != col || 15 != row || 0 != depth) {
        G_message("Error in Rast3d_location2coord");
        sum++;
    }

    G_message("Test the center, coordinates must be col = 4 row = 7 depth = 2");

    north = 4750.0;
    east = 7499.9;
    top = 500.0;

    Rast3d_location2coord(&(map->region), north, east, top, &col, &row, &depth);
    printf("Rast3d_location2coord col %i row %i depth %i\n", col, row, depth);
    if ((region.cols - 1) / 2 != col || (region.rows - 1) / 2 != row ||
        (region.depths - 1) / 2 != depth) {
        G_message("Error in Rast3d_location2coord");
        sum++;
    }

    G_message("Test the n=3000.1, e=7000.1 and t=800.1, coordinates must be "
              "col = 4 row = 10 depth = 4");

    north = 3000.1;
    east = 7000.1;
    top = 800.1;

    Rast3d_location2coord(&(map->region), north, east, top, &col, &row, &depth);
    printf("Rast3d_location2coord col %i row %i depth %i\n", col, row, depth);
    if (4 != col || map->region.rows - 5 != row || 4 != depth) {
        G_message("Error in Rast3d_location2coord");
        sum++;
    }

    G_message("Test the n=2999.9, e=6999.9 and t=799.9, coordinates must be "
              "col = 3 row = 11 depth = 3");

    north = 2999.9;
    east = 6999.9;
    top = 799.9;

    Rast3d_location2coord(&(map->region), north, east, top, &col, &row, &depth);
    printf("Rast3d_location2coord col %i row %i depth %i\n", col, row, depth);
    if (3 != col || map->region.rows - 4 != row || 3 != depth) {
        G_message("Error in Rast3d_location2coord");
        sum++;
    }

    Rast3d_close(map);

    G_remove("grid3", "test_coordinate_transform");

    return sum;
}

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

int test_region(void)
{
    int sum = 0;
    RASTER3D_Region region, new_region;

    Rast3d_get_window(&region);
    region.bottom = 0.0;
    region.top = 1000;
    region.south = 10000;
    region.north = 20000;
    region.west = 5000;
    region.east = 10000;
    region.rows = 20;
    region.cols = 10;
    region.depths = 5;
    region.ew_res = 0;
    region.ns_res = 0;
    region.tb_res = 0;

    /* Test region adjustment */
    Rast3d_adjust_region(&region);

    if (region.ew_res != 500) {
        G_message("Error in Rast3d_adjust_region: region.ew_res != 500");
        sum++;
    }

    if (region.ns_res != 500) {
        G_message("Error in Rast3d_adjust_region: region.ew_res != 500");
        sum++;
    }

    if (region.tb_res != 200) {
        G_message("Error in Rast3d_adjust_region: region.ew_res != 500");
        sum++;
    }

    /* Test the region copy */
    Rast3d_region_copy(&new_region, &region);

    if (region.bottom != new_region.bottom) {
        G_message(
            "Error in Rast3d_region_copy: region.bottom != new_region.bottom");
        sum++;
    }

    if (region.cols != new_region.cols) {
        G_message(
            "Error in Rast3d_region_copy: region.cols != new_region.cols");
        sum++;
    }

    if (region.depths != new_region.depths) {
        G_message(
            "Error in Rast3d_region_copy: region.depths != new_region.depths");
        sum++;
    }

    if (region.east != new_region.east) {
        G_message(
            "Error in Rast3d_region_copy: region.east != new_region.east");
        sum++;
    }

    if (region.ew_res != new_region.ew_res) {
        G_message(
            "Error in Rast3d_region_copy: region.ew_res != new_region.ew_res");
        sum++;
    }

    if (region.north != new_region.north) {
        G_message(
            "Error in Rast3d_region_copy: region.north != new_region.north");
        sum++;
    }

    if (region.ns_res != new_region.ns_res) {
        G_message(
            "Error in Rast3d_region_copy: region.ns_res != new_region.ns_res");
        sum++;
    }

    if (region.proj != new_region.proj) {
        G_message(
            "Error in Rast3d_region_copy: region.proj != new_region.proj");
        sum++;
    }

    if (region.rows != new_region.rows) {
        G_message(
            "Error in Rast3d_region_copy: region.rows != new_region.rows");
        sum++;
    }

    if (region.south != new_region.south) {
        G_message(
            "Error in Rast3d_region_copy: region.south != new_region.south");
        sum++;
    }

    if (region.tb_res != new_region.tb_res) {
        G_message(
            "Error in Rast3d_region_copy: region.tb_res != new_region.tb_res");
        sum++;
    }

    if (region.top != new_region.top) {
        G_message("Error in Rast3d_region_copy: region.top != new_region.top");
        sum++;
    }

    if (region.west != new_region.west) {
        G_message(
            "Error in Rast3d_region_copy: region.west != new_region.west");
        sum++;
    }

    if (region.zone != new_region.zone) {
        G_message(
            "Error in Rast3d_region_copy: region.zone != new_region.zone");
        sum++;
    }

    return sum;
}
