
/*********************************************************************
 * E.S.O. - VLT project
 *
 * "@(#) $Id: kmclipm_test_priv_reconstruct.c,v 1.7 2013-10-08 11:12:02 aagudo Exp $"
 *
 * Tests for functions in src/kmclipm_priv_constants.c/.h
 *
 * who       when        what
 * --------  ----------  ----------------------------------------------
 * aagudo    2011-09-06  created
 */

/**
    @defgroup kmclipm_test_priv_reconstruct Unit tests for the private functions in kmclipm_priv_reconstruct

    Unit test for the functions in the module @ref kmclipm_priv_reconstruct

    @{
*/


/*------------------------------------------------------------------------------
 *              Includes
 *----------------------------------------------------------------------------*/
#include <math.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include "kmclipm_constants.h"
#include "kmclipm_priv_reconstruct.h"
#include "kmclipm_priv_error.h"

cpl_msg_severity my_level= CPL_MSG_WARNING;

/*------------------------------------------------------------------------------
 *              Implementation
 *----------------------------------------------------------------------------*/
int compareAndSaveNeighborGrid (const char* testName, const char * info,
          const gridDefinition gd, neighbors ***nb, const samples *sampleList)
{
  int retVal = 1;
  FILE *data;
  char *dataFilename;
  char *refFilename;
  char *idlFilename;
  char *cmpCommand;
  int p, il, iy, ix, n;

  dataFilename = cpl_sprintf( "%s/test_data/kmclipm_test_priv_find_neighbors_%s.dat", ".", testName);
  idlFilename = cpl_sprintf( "%s/test_data/kmclipm_test_priv_find_neighbors_%s.idl", ".", testName);
  refFilename = cpl_sprintf("%s/ref/kmclipm_test_priv_find_neighbors_%s.ref", getenv("srcdir"), testName);
  int totalNeighborCount = 0;

  data = fopen(dataFilename, "w");
  fprintf(data, "Test: %s\n", info);
  fprintf(data, "Samples:\n");
  for (p=0; p<sampleList->no_samples; p++) {
    fprintf(data, " %2d  ->  %+.2f / %+.2f / %+.2f  :  %+.2f\n",
            p, sampleList->x[p],sampleList->y[p],
            sampleList->l[p],sampleList->value[p]);
  }
  fprintf(data, "Grid definition:\n");
  fprintf(data, "x: dim %2d   %+.3f...%+.3f  delta %.3f\n",
          gd.x.dim, gd.x.start, gd.x.start+(gd.x.dim-1)*gd.x.delta, gd.x.delta);
  fprintf(data, "y: dim %2d   %+.3f...%+.3f  delta %.3f\n",
          gd.y.dim, gd.y.start, gd.y.start+(gd.y.dim-1)*gd.y.delta, gd.y.delta);
  fprintf(data, "l: dim %2d   %+.3f...%+.3f  delta %.3f\n",
          gd.l.dim, gd.l.start, gd.l.start+(gd.l.dim-1)*gd.l.delta, gd.l.delta);
  fprintf(data, "Grid pixel neighbors:\n");
  for (il=0; il<gd.l.dim; il++) {
    for (iy=0; iy<gd.y.dim; iy++) {
      for (ix=0; ix<gd.x.dim; ix++) {
        totalNeighborCount += nb[ix][iy][il].no_neighbors;
        fprintf(data, "%d / %d / %d  (%+.2f / %+.2f / %+.2f) : %d \n",
                ix, iy, il,
                gd.x.start + ix * gd.x.delta, gd.y.start + iy * gd.y.delta,
                gd.l.start + il * gd.l.delta, nb[ix][iy][il].no_neighbors);
        for (n=0; n<nb[ix][iy][il].no_neighbors; n++) {
          int k = nb[ix][iy][il].idx[n];
          fprintf(data, "\t   (%+.2f / %+.2f / %+.2f)  %.3f -> %d \n",
              sampleList->x[k],sampleList->y[k],sampleList->l[k],
              nb[ix][iy][il].distance[n], k);
        }
      }
    }
  }
  fprintf(data, "Sample readout neighbors:\n");
  for (p=0; p<sampleList->no_samples; p++) {
    fprintf(data, " %2d  ->  %+.2f / %+.2f / %+.2f\n",
            p, sampleList->x[p],sampleList->y[p],sampleList->l[p]);
    for (il=0; il<gd.l.dim; il++) {
      for (iy=0; iy<gd.y.dim; iy++) {
        for (ix=0; ix<gd.x.dim; ix++) {
         for (n=0; n<nb[ix][iy][il].no_neighbors; n++) {
            int k = nb[ix][iy][il].idx[n];
            if (k == p) {
              fprintf(data, "\t(%+.2f / %+.2f / %+.2f)  %.3f\n",
                  gd.x.start + ix * gd.x.delta, gd.y.start + iy * gd.y.delta,
                  gd.l.start + il * gd.l.delta, nb[ix][iy][il].distance[n]);
            }
         }
        }
      }
    }
  }

  fclose(data);

  data = fopen(idlFilename, "w");
  fprintf(data, "%d\n", sampleList->no_samples);
  for (p=0; p<sampleList->no_samples; p++) {
    fprintf(data, "%f %f %f %f\n", sampleList->x[p],sampleList->y[p],
            sampleList->l[p],sampleList->value[p]);
  }
  fprintf(data, "%d\n", gd.x.dim * gd.y.dim * gd.l.dim);
  for (il=0; il<gd.l.dim; il++) {
    for (iy=0; iy<gd.y.dim; iy++) {
      for (ix=0; ix<gd.x.dim; ix++) {
        fprintf(data, "%f %f %f\n", gd.x.start + ix * gd.x.delta,
                gd.y.start + iy * gd.y.delta, gd.l.start + il * gd.l.delta);
      }
    }
  }
  fprintf(data, "%d\n", totalNeighborCount);
  for (il=0; il<gd.l.dim; il++) {
    for (iy=0; iy<gd.y.dim; iy++) {
      for (ix=0; ix<gd.x.dim; ix++) {
        for (n=0; n<nb[ix][iy][il].no_neighbors; n++) {
          int k = nb[ix][iy][il].idx[n];
          fprintf(data, "%f %f %f %f %f %f\n",
              gd.x.start + ix * gd.x.delta, gd.y.start + iy * gd.y.delta,
              gd.l.start + il * gd.l.delta,
              sampleList->x[k],sampleList->y[k],sampleList->l[k]);
        }
      }
    }
  }
  fclose(data);

  cmpCommand = cpl_sprintf("cmp %s %s", refFilename, dataFilename);
  int test = system(cmpCommand);
  retVal = WEXITSTATUS(test);    /* WEXITSTATUS is defined in <sys/wait.h> */

  if (retVal) {
    for (il=0; il<gd.l.dim; il++) {
      for (iy=0; iy<gd.y.dim; iy++) {
        for (ix=0; ix<gd.x.dim; ix++) {
          printf("%d (", nb[ix][iy][il].no_neighbors);
          for (n=0; n<nb[ix][iy][il].no_neighbors; n++) {
            int k = nb[ix][iy][il].idx[n];
            printf("%.1f/%.1f/%.1f->%.3f ", sampleList->x[k],sampleList->x[k],
                   sampleList->l[k],nb[ix][iy][il].distance[n]);
          }
          printf(")\t");
        }
        printf("\n");
      }
      printf("\n");
    }
  }

  cpl_free(cmpCommand);
  cpl_free(idlFilename);
  cpl_free(dataFilename);
  cpl_free(refFilename);

  return retVal;
}

void test_kmclipm_priv_reconstruct_nnlut_get_timestamp()
{
/*erwtest*/
    /* invalid tests */

    /* valid tests */
    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_priv_reconstruct_nnlut_read()
{
/*erwtest*/
    /* invalid tests */

    /* valid tests */
    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_priv_reconstruct_nnlut_write()
{
/*erwtest*/
    /* invalid tests */

    /* valid tests */
    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_priv_reconstruct_nearestneighbor()
{
/*erwtest*/
    /* invalid tests */

    /* valid tests */
    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_priv_reconstruct_cubicspline()
{
/*erwtest*/
    /* invalid tests */

    /* valid tests */
    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_priv_setup_grid()
{
/*erwtest*/
    /* invalid tests */

    /* valid tests */
    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_priv_find_neighbors()
{
    int il, ix, iy;

    samples sampleList;
    sampleList.no_samples = 0;
    gridDefinition gd;
    neighbors ***nb = NULL;

    gd.lamdaDistanceScale = 1.0;
    gd.x.start = -3;
    gd.x.delta = 2;
    gd.x.dim = (3 - gd.x.start) / gd.x.delta + 1;
    gd.y.start = -4;
    gd.y.delta = 2;
    gd.y.dim = (4 - gd.y.start) / gd.y.delta + 1;
    gd.l.start = 0;
    gd.l.delta = 1;
    gd.l.dim = (2 - gd.l.start) / gd.l.delta + 1;
    gd.neighborHood.distance = 0.5;
    gd.neighborHood.scale = PIXEL;
    gd.neighborHood.type = N_CUBE;
    /*
    float distance = 0.5;
    enum neighborhoodType nbhType = N_CUBE;
    enum scaleType distType = PIXEL;
    */
    sampleList.no_samples = gd.x.dim * gd.y.dim * gd.l.dim;
    sampleList.x = (float *) cpl_calloc(sampleList.no_samples, sizeof(float));
    sampleList.y = (float *) cpl_calloc(sampleList.no_samples, sizeof(float));
    sampleList.l = (float *) cpl_calloc(sampleList.no_samples, sizeof(float));
    sampleList.value = (float *) cpl_calloc(sampleList.no_samples, sizeof(float));

    /*
     *  test for illegal inputs
     */
    /* missing sample list */
    cpl_test_null(nb = (neighbors ***) kmclipm_priv_find_neighbors (gd, NULL));
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    cpl_free(nb); nb = NULL;

    /* grid dimensions not greater than 0 */
    gridDefinition gde = gd;
    gde.x.dim = 0;
    cpl_test_null(nb = (neighbors ***) kmclipm_priv_find_neighbors (gde, &sampleList));
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    cpl_free(nb); nb = NULL;
    gde.x.dim = gd.x.dim;

    /* reverse grid axis */
    gde.y.delta = -1.0;
    cpl_test_null(nb = (neighbors ***) kmclipm_priv_find_neighbors (gde, &sampleList));
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    cpl_error_reset();
    cpl_free(nb); nb = NULL;

    /* negative distance for valid neighbors */
    gd.neighborHood.distance = -1.;
    cpl_test_null(nb = (neighbors ***) kmclipm_priv_find_neighbors (gd, &sampleList));
    gd.neighborHood.distance = 0.5;
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    cpl_error_reset();
    cpl_free(nb); nb = NULL;

    /* invalid neighborhood type */
    gd.neighborHood.type = -1;
    cpl_test_null(nb = (neighbors ***) kmclipm_priv_find_neighbors (gd, &sampleList));
    gd.neighborHood.type = N_CUBE;
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    cpl_error_reset();
    cpl_free(nb); nb = NULL;

    /* invalid scaling type */
    gd.neighborHood.scale = -1;
    cpl_test_null(nb = (neighbors ***) kmclipm_priv_find_neighbors (gd, &sampleList));
    gd.neighborHood.scale = PIXEL;
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    cpl_error_reset();
    cpl_free(nb); nb = NULL;

    const char *info;
    info = "one to one map";
    cpl_msg_info("kmclipm_test_priv_find_neighbors","%s", info);
    for (ix=0; ix<gd.x.dim; ix++) {
      for (iy=0; iy<gd.y.dim; iy++) {
        for (il=0; il<gd.l.dim; il++) {
          int p = ix + iy * gd.x.dim + il * gd.x.dim * gd.y.dim;
          sampleList.x[p] = gd.x.start + ix * gd.x.delta;
          sampleList.y[p] = gd.y.start + iy * gd.y.delta;
          sampleList.l[p] = gd.l.start + il * gd.l.delta;
          sampleList.value[p] = sqrt(sampleList.x[p]*sampleList.x[p] +
                                     sampleList.y[p]*sampleList.y[p] +
                                     sampleList.l[p]*sampleList.l[p]);
        }
      }
    }
    gd.neighborHood.distance = 0.5;
    gd.neighborHood.scale = PIXEL;
    gd.neighborHood.type = N_CUBE;

    /*Alex: added because valgind threw conditional jump warnings*/
    gd.method = LINEAR_WEIGHTED_NEAREST_NEIGHBOR;

    cpl_test_nonnull(nb = (neighbors ***) kmclipm_priv_find_neighbors (gd, &sampleList));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, compareAndSaveNeighborGrid("one", info, gd, nb,&sampleList));
    cpl_test_error(CPL_ERROR_NONE);
    kmclipm_priv_cleanup_neighborlist(nb, gd);

    info = "second layer shifted in l-direction by 0.6, "
           "distance=0.5 nbhType=N_CUBE distType=PIXEL";
    cpl_msg_info("kmclipm_test_priv_find_neighbors", "%s", info);
    for (ix=0; ix<gd.x.dim; ix++) {
          for (iy=0; iy<gd.y.dim; iy++) {
            for (il=0; il<gd.l.dim; il++) {
              float ill;
              if (il ==1) {
                ill = 1.6;
              } else {
                ill = il;
              }
              int p = ix + iy * gd.x.dim + il * gd.x.dim * gd.y.dim;
              sampleList.x[p] = gd.x.start + ix * gd.x.delta;
              sampleList.y[p] = gd.y.start + iy * gd.y.delta;
              sampleList.l[p] = gd.l.start + ill * gd.l.delta;
              sampleList.value[p] = sqrt(sampleList.x[p]*sampleList.x[p] +
                                         sampleList.y[p]*sampleList.y[p] +
                                         sampleList.l[p]*sampleList.l[p]);
            }
          }
        }
    gd.neighborHood.distance = 0.5;
    gd.neighborHood.type = N_CUBE;
    gd.neighborHood.scale = PIXEL;
    cpl_test_nonnull(nb = (neighbors ***) kmclipm_priv_find_neighbors (gd, &sampleList));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, compareAndSaveNeighborGrid ("two", info, gd, nb, &sampleList));
    cpl_test_error(CPL_ERROR_NONE);
    kmclipm_priv_cleanup_neighborlist(nb, gd);

    info = "second layer shifted by 0.6 in l-direction and by 0.4 in "
           "y-direction, distance=0.5 nbhType=N_CUBE distType=PIXEL";
    cpl_msg_info("kmclipm_test_priv_find_neighbors", "%s", info);
    for (ix=0; ix<gd.x.dim; ix++) {
          for (iy=0; iy<gd.y.dim; iy++) {
            for (il=0; il<gd.l.dim; il++) {
              float ill, iyy;
              if (il ==1) {
                ill = 1.6;
                iyy = iy+0.4;
              } else {
                ill = il;
                iyy = iy;
              }
              int p = ix + iy * gd.x.dim + il * gd.x.dim * gd.y.dim;
              sampleList.x[p] = gd.x.start + ix * gd.x.delta;
              sampleList.y[p] = gd.y.start + iyy * gd.y.delta;
              sampleList.l[p] = gd.l.start + ill * gd.l.delta;
              sampleList.value[p] = sqrt(sampleList.x[p]*sampleList.x[p] +
                                         sampleList.y[p]*sampleList.y[p] +
                                         sampleList.l[p]*sampleList.l[p]);
            }
          }
        }
    gd.neighborHood.distance = 0.5;
    gd.neighborHood.type = N_CUBE;
    gd.neighborHood.scale = PIXEL;
    cpl_test_nonnull(nb = (neighbors ***) kmclipm_priv_find_neighbors (gd, &sampleList));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, compareAndSaveNeighborGrid ("three", info, gd, nb, &sampleList));
    cpl_test_error(CPL_ERROR_NONE);
    kmclipm_priv_cleanup_neighborlist(nb, gd);

    info = "second layer shifted by 0.6 in l-direction and by 0.4 in "
           "y-direction, distance=0.5 nbhType=N_SPERE distType=PIXEL";
    cpl_msg_info("kmclipm_test_priv_find_neighbors", "%s", info);
    for (ix=0; ix<gd.x.dim; ix++) {
          for (iy=0; iy<gd.y.dim; iy++) {
            for (il=0; il<gd.l.dim; il++) {
              float ill, iyy;
              if (il ==1) {
                ill = 1.6;
                iyy = iy+0.4;
              } else {
                ill = il;
                iyy = iy;
              }
              int p = ix + iy * gd.x.dim + il * gd.x.dim * gd.y.dim;
              sampleList.x[p] = gd.x.start + ix * gd.x.delta;
              sampleList.y[p] = gd.y.start + iyy * gd.y.delta;
              sampleList.l[p] = gd.l.start + ill * gd.l.delta;
              sampleList.value[p] = sqrt(sampleList.x[p]*sampleList.x[p] +
                                         sampleList.y[p]*sampleList.y[p] +
                                         sampleList.l[p]*sampleList.l[p]);
            }
          }
        }
    gd.neighborHood.distance = 0.5;
    gd.neighborHood.type = N_SPHERE;
    gd.neighborHood.scale = PIXEL;
    cpl_test_nonnull(nb = (neighbors ***) kmclipm_priv_find_neighbors (gd, &sampleList));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, compareAndSaveNeighborGrid ("four", info, gd, nb, &sampleList));
    cpl_test_error(CPL_ERROR_NONE);
    kmclipm_priv_cleanup_neighborlist(nb, gd);

    info = "second layer shifted by 0.6 in l-direction and by 0.4 in "
           "y-direction, distance=0.9 nbhType=N_SPHERE distType=UNIT";
    cpl_msg_info("kmclipm_test_priv_find_neighbors", "%s", info);
    for (ix=0; ix<gd.x.dim; ix++) {
          for (iy=0; iy<gd.y.dim; iy++) {
            for (il=0; il<gd.l.dim; il++) {
              float ill, iyy;
              if (il ==1) {
                ill = 1.6;
                iyy = iy+0.4;
              } else {
                ill = il;
                iyy = iy;
              }
              int p = ix + iy * gd.x.dim + il * gd.x.dim * gd.y.dim;
              sampleList.x[p] = gd.x.start + ix * gd.x.delta;
              sampleList.y[p] = gd.y.start + iyy * gd.y.delta;
              sampleList.l[p] = gd.l.start + ill * gd.l.delta;
              sampleList.value[p] = sqrt(sampleList.x[p]*sampleList.x[p] +
                                         sampleList.y[p]*sampleList.y[p] +
                                         sampleList.l[p]*sampleList.l[p]);
            }
          }
        }
    gd.neighborHood.distance = 0.9;
    gd.neighborHood.type = N_SPHERE;
    gd.neighborHood.scale = UNIT;
    cpl_test_nonnull(nb = (neighbors ***) kmclipm_priv_find_neighbors (gd, &sampleList));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, compareAndSaveNeighborGrid ("five", info, gd, nb, &sampleList));
    cpl_test_error(CPL_ERROR_NONE);
    kmclipm_priv_cleanup_neighborlist(nb, gd);

    info = "x-direction tiled by one pixel, "
           "distance=0.5 nbhType=N_CUBE distType=PIXEL";
    cpl_msg_info("kmclipm_test_priv_find_neighbors", "%s", info);
    for (ix=0; ix<gd.x.dim; ix++) {
          for (iy=0; iy<gd.y.dim; iy++) {
            for (il=0; il<gd.l.dim; il++) {
              int p = ix + iy * gd.x.dim + il * gd.x.dim * gd.y.dim;
              sampleList.x[p] = gd.x.start + ix * gd.x.delta + (float) (iy) /
                                (float) (gd.y.dim) * gd.x.delta;
              sampleList.y[p] = gd.y.start + iy * gd.y.delta;
              sampleList.l[p] = gd.l.start + il * gd.l.delta;
              sampleList.value[p] = sqrt(sampleList.x[p]*sampleList.x[p] +
                                         sampleList.y[p]*sampleList.y[p] +
                                         sampleList.l[p]*sampleList.l[p]);
            }
          }
        }
    gd.neighborHood.distance = 0.5;
    gd.neighborHood.type = N_CUBE;
    gd.neighborHood.scale = PIXEL;
    cpl_test_nonnull(nb = (neighbors ***) kmclipm_priv_find_neighbors (gd, &sampleList));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, compareAndSaveNeighborGrid ("six", info, gd, nb, &sampleList));
    cpl_test_error(CPL_ERROR_NONE);
    kmclipm_priv_cleanup_neighborlist(nb, gd);

    cpl_free(sampleList.x);
    cpl_free(sampleList.y);
    cpl_free(sampleList.l);
    cpl_free(sampleList.value);

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_priv_cleanup_neighborlist()
{
/*erwtest*/
    /* invalid tests */

    /* valid tests */
    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_priv_delete_alien_ifu_cal_data()
{
/*erwtest*/
    /* invalid tests */

    /* valid tests */
    cpl_test_error(CPL_ERROR_NONE);
}

/**
    @brief  Executes the unit tests for all functions in the module @ref kmclipm_priv_reconstruct

    @return Error code, 0 for NONE.
*/
int main()
{
    /* For DEVELOPERS:
     * to omit all CPL_MSG_INFO output, apply following statement in the shell
     * before executing "make check":
     *      setenv CPL_MSG_LEVEL warning
     *  (or export CPL_MSG_LEVEL=warning)
     */

    cpl_test_init("<usd-help@eso.org>", my_level);

    char *my_path = cpl_sprintf("%s/test_data", ".");
    mkdir(my_path, 0777);
    cpl_free(my_path);

    cpl_msg_set_level_from_env();
    my_level = cpl_msg_get_level();

    test_kmclipm_priv_reconstruct_nnlut_get_timestamp();
    test_kmclipm_priv_reconstruct_nnlut_read();
    test_kmclipm_priv_reconstruct_nnlut_write();
    test_kmclipm_priv_reconstruct_nearestneighbor();
    test_kmclipm_priv_reconstruct_cubicspline();
    test_kmclipm_priv_setup_grid();
    test_kmclipm_priv_find_neighbors();
    test_kmclipm_priv_cleanup_neighborlist();
    test_kmclipm_priv_delete_alien_ifu_cal_data();

    cpl_msg_info("","All tests done.");
    cpl_msg_set_level(CPL_MSG_WARNING);
    return cpl_test_end(0);
}

/** @} */
