/****************************************************************************
 *
 * MODULE:       i.cca
 * AUTHOR(S):    David Satnik, Central Washington University, and
 *               Ali R. Vali, University of Texas (original contributors)
 *               Markus Neteler <neteler itc.it>,
 *               Bernhard Reiter <bernhard intevation.de>,
 *               Brad Douglas <rez touchofmadness.com>,
 *               Glynn Clements <glynn gclements.plus.com>,
 *               Jan-Oliver Wagner <jan intevation.de>
 * PURPOSE:      canonical components transformation: takes any number of
 *               (raster) band files and a signature file, and outputs the same
 *               number of raster band files transformed to provide maximum
 *               separability of the categories indicated by the signatures.
 * COPYRIGHT:    (C) 1999-2007 by the GRASS Development Team
 *
 *               This program is free software under the GNU General Public
 *               License (>=v2). Read the file COPYING that comes with GRASS
 *               for details.
 *
 *****************************************************************************/

/*

   Ali Vali contact info:
   Center for Space Research
   WRW 402
   University of Texas
   Austin, TX 78712-1085

   (512) 471-6824

 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <grass/raster.h>
#include <grass/imagery.h>
#include <grass/gmath.h>
#include <grass/glocale.h>
#include "local_proto.h"

int main(int argc, char *argv[])
{
    /* Global variable & function declarations */

    int i, j, k;     /* Loop control variables */
    int bands;       /* Number of image bands */
    int nclass;      /* Number of classes */
    int samptot;     /* Total number of sample points */
    double **mu;     /* Mean vector for image classes */
    double **w;      /* Within Class Covariance Matrix */
    double **p;      /* Between class Covariance Matrix */
    double **l;      /* Diagonal matrix of eigenvalues */
    double **q;      /* Transformation matrix */
    double ***cov;   /* Individual class Covariance Matrix */
    double *nsamp;   /* Number of samples in a given class */
    double *eigval;  /* Eigen value vector */
    double **eigmat; /* Eigen Matrix */
    char tempname[1024];

    /* used to make the color tables */
    CELL *outbandmax; /* will hold the maximums found in the out maps */
    CELL *outbandmin; /* will hold the minimums found in the out maps */
    struct Colors color_tbl;
    struct Signature sigs;
    FILE *sigfp;
    struct Ref refs;
    char **err;
    int *datafds;
    int *outfds;

    struct GModule *module;
    struct Option *grp_opt, *subgrp_opt, *sig_opt, *out_opt;

    /***** Start of main *****/
    G_gisinit(argv[0]);

    module = G_define_module();
    G_add_keyword(_("imagery"));
    G_add_keyword(_("statistics"));
    G_add_keyword("CCA");
    G_add_keyword(_("canonical components analysis"));
    module->description = _("Canonical components analysis (CCA) "
                            "program for image processing.");

    grp_opt = G_define_standard_option(G_OPT_I_GROUP);

    subgrp_opt = G_define_standard_option(G_OPT_I_SUBGROUP);
    subgrp_opt->key = "subgroup";
    subgrp_opt->description = _("Name of input imagery subgroup");

    sig_opt = G_define_option();
    sig_opt->key = "signature";
    sig_opt->type = TYPE_STRING;
    sig_opt->gisprompt = "old,signatures/sig,sigfile";
    sig_opt->required = YES;
    sig_opt->key_desc = "name";
    sig_opt->description = _("File containing spectral signatures");

    out_opt = G_define_standard_option(G_OPT_R_OUTPUT);
    out_opt->description = _("Output raster map prefix name");

    if (G_parser(argc, argv))
        exit(EXIT_FAILURE);

    /* check group, subgroup */
    I_init_group_ref(&refs);
    if (I_find_group(grp_opt->answer) <= 0)
        G_fatal_error(_("Unknown imagery group."));

    if (!I_find_subgroup(grp_opt->answer, subgrp_opt->answer))
        G_fatal_error(_("Subgroup <%s> in group <%s> not found"),
                      subgrp_opt->answer, grp_opt->answer);

    if (I_get_subgroup_ref(grp_opt->answer, subgrp_opt->answer, &refs) <= 0)
        G_fatal_error(_("Unable to find subgroup reference information."));

    /* open and input the signatures file */
    if ((sigfp = I_fopen_signature_file_old(sig_opt->answer)) == NULL)
        G_fatal_error(_("Unable to open the signature file"));

    if (I_read_signatures(sigfp, &sigs) < 0)
        G_fatal_error(_("Error while reading the signatures file."));

    fclose(sigfp);
    nclass = sigs.nsigs;
    if (nclass < 2)
        G_fatal_error(_("Need at least two signatures in signature file."));

    err = I_sort_signatures_by_semantic_label(&sigs, &refs);
    if (err)
        G_fatal_error(_("Signature - group member semantic label mismatch.\n"
                        "Extra signatures for bands: %s\n"
                        "Imagery group bands without signatures: %s"),
                      err[0] ? err[0] : _("none"), err[1] ? err[1] : _("none"));

    /* check the number of input bands */
    bands = refs.nfiles;

    /*memory allocation */
    mu = G_alloc_matrix(nclass, bands);
    w = G_alloc_matrix(bands, bands);
    p = G_alloc_matrix(bands, bands);
    l = G_alloc_matrix(bands, bands);
    q = G_alloc_matrix(bands, bands);
    eigmat = G_alloc_matrix(bands, bands);
    nsamp = G_alloc_vector(nclass);
    eigval = G_alloc_vector(bands);

    cov = (double ***)G_calloc(nclass, sizeof(double **));
    for (i = 0; i < nclass; i++) {
        cov[i] = G_alloc_matrix(bands, bands);
    }

    outbandmax = (CELL *)G_calloc(nclass, sizeof(CELL));
    outbandmin = (CELL *)G_calloc(nclass, sizeof(CELL));
    datafds = (int *)G_calloc(nclass, sizeof(int));
    outfds = (int *)G_calloc(nclass, sizeof(int));

    /*
       Here is where the information regarding
       a) Number of samples per class
       b) Covariance matrix for each class
       are read into the  program
     */

    samptot = 0;
    for (i = 1; i <= nclass; i++) {
        nsamp[i] = sigs.sig[i - 1].npoints;
        samptot += nsamp[i];
        for (j = 1; j <= bands; j++) {
            mu[i][j] = sigs.sig[i - 1].mean[j - 1];
            for (k = 1; k <= j; k++)
                cov[i][j][k] = cov[i][k][j] = sigs.sig[i - 1].var[j - 1][k - 1];
        }
    }

    within(samptot, nclass, nsamp, cov, w, bands);
    between(samptot, nclass, nsamp, mu, p, bands);
    G_math_d_copy(w[0], eigmat[0], bands * bands);
    G_math_eigen(eigmat, eigval, bands);
    G_math_egvorder(eigval, eigmat, bands);
    setdiag(eigval, bands, l);
    getsqrt(w, bands, l, eigmat);
    solveq(q, bands, w, p);
    G_math_d_copy(q[0], eigmat[0], bands * bands);
    G_math_eigen(eigmat, eigval, bands);
    G_math_egvorder(eigval, eigmat, bands);
    G_math_d_AB(eigmat, w, q, bands, bands, bands);

    for (i = 0; i < bands; i++) {
        G_verbose_message("%i. eigen value: %+6.5f", i, eigval[i]);
        G_verbose_message("eigen vector:");
        for (j = 0; j < bands; j++)
            G_verbose_message("%+6.5f ", eigmat[i][j]);
    }

    /* open the cell maps */
    for (i = 1; i <= bands; i++) {
        outbandmax[i] = (CELL)0;
        outbandmin[i] = (CELL)0;

        datafds[i] =
            Rast_open_old(refs.file[i - 1].name, refs.file[i - 1].mapset);

        sprintf(tempname, "%s.%d", out_opt->answer, i);
        outfds[i] = Rast_open_c_new(tempname);
    }

    /* do the transform */
    transform(datafds, outfds, Rast_window_rows(), Rast_window_cols(), q, bands,
              outbandmin, outbandmax);

    /* make grey scale color table */
    Rast_init_colors(&color_tbl);

    /* close the cell maps */
    for (i = 1; i <= bands; i++) {
        Rast_close(datafds[i]);
        Rast_close(outfds[i]);

        if (outbandmin[i] < (CELL)0 || outbandmax[i] > (CELL)255) {
            G_warning(_("The output cell map <%s.%d> has values "
                        "outside the 0-255 range."),
                      out_opt->answer, i);
        }

        Rast_make_grey_scale_colors(&color_tbl, 0, outbandmax[i]);
        sprintf(tempname, "%s.%d", out_opt->answer, i);

        /* write a color table */
        Rast_write_colors(tempname, G_mapset(), &color_tbl);
    }

    I_free_signatures(&sigs);
    I_free_group_ref(&refs);

    /*free memory */
    G_free_matrix(mu);
    G_free_matrix(w);
    G_free_matrix(p);
    G_free_matrix(l);
    G_free_matrix(q);
    G_free_matrix(eigmat);
    for (i = 0; i < nclass; i++)
        G_free_matrix(cov[i]);
    G_free(cov);

    G_free_vector(nsamp);
    G_free_vector(eigval);

    G_free(outbandmax);
    G_free(outbandmin);
    G_free(datafds);
    G_free(outfds);

    exit(EXIT_SUCCESS);
}
