package uk.ac.starlink.ttools.task;

import java.io.IOException;
import java.util.logging.Level;
import uk.ac.starlink.table.StarTable;
import uk.ac.starlink.table.StarTableFactory;
import uk.ac.starlink.table.Tables;
import uk.ac.starlink.ttools.TableTestCase;
import uk.ac.starlink.util.LogUtils;

/**
 * This class provides a regression test for ellipse matching.
 *
 * <p>The input data is a table originally generated by running SExtractor
 * on a file ngc1275.fits.  The input image file, and most of the
 * SExtractor parameters, are as used/generated by the Demo mode of Starlink
 * GAIA v4.4.0 - some alterations to the output format in the config file
 * default.sex (CATALOG_TYPE=FITS_1.0) and the output parameter file
 * default.param (to get appropriate ellipse output columns) were made.
 * The output catalogue includes both Cartesian and Sky coordinate ellipse
 * parameters as calculated by SExtractor.
 * A small proportion of the input rows (you can see which ones by
 * looking at the NUMBER column) were then excluded to make this test work,
 * as described below, and additional columns were added containing
 * the results of an initial run, for later comparison.
 *
 * <p>The test basically takes the ellipses defined by the input file,
 * performs an internal match, and looks at the number of elements in
 * the match group for each row, comparing it with the pre-calculated
 * values also stored in the input file.  Comparing the match group size
 * is not such a good test as comparing the members of each match group,
 * but it's a fairly good proxy and easier to do (the match group ID for a
 * given row might vary according to the details of the match algorithm).
 * However, a straight match along these lines comes up with no matches,
 * since the ellipses are not crowded/large enough.  So we do this for
 * several values (2,3,4) of ellipse scale multiplier, which gives a few
 * tens of matches in each case, enough to be sensitive to whether the
 * match has worked like it did before.  Additionally, the same match
 * is done in two different ways: using Cartesian coordinates, and using
 * sky coordinates, and a check is made that the two give the same results.
 *
 * <p>In fact, the two do not give exactly the same results for two reasons:
 * (1) the sky ellipse match engine is geometrically approximate, since
 * it relies on Cartesian projection, and (2) [I think] the sky and 
 * Cartesian ellipse parameters assigned by SExtractor differ in
 * non-trivial ways for reasons related to geometrical parameterisation
 * (not approximation).  I believe without supporting evidence (i.e. I hope)
 * that (2) is the dominant effect in producing discrepancies here.
 * The results are quite close though.  So, I just removed the rows 
 * for which the discrepancies between the two geometries show up -
 * about 8% of them.
 * 
 * <p>This should be a reliable and moderately stringent regression test
 * of the sky and cartesian ellipse match engines, but I haven't proved
 * it's a good test of their correctness.  I've compared the results by
 * eye with plots in GAIA and as far as I can tell it looks about right.
 *
 * @author   Mark Taylor
 * @since    7 Sep 2011
 */
public class EllipseRegressionTest extends TableTestCase {

    private final StarTable tngc_;

    public EllipseRegressionTest() throws IOException {
        LogUtils.getLogger( "uk.ac.starlink.util" )
                .setLevel( Level.WARNING );
        LogUtils.getLogger( "uk.ac.starlink.ttools.join" )
                .setLevel( Level.WARNING );
        LogUtils.getLogger( "uk.ac.starlink.table.storage" )
                .setLevel( Level.WARNING );
        LogUtils.getLogger( "uk.ac.starlink.fits" )
                .setLevel( Level.WARNING );
        tngc_ =
            Tables.randomTable( new StarTableFactory( true )
                               .makeStarTable( EllipseRegressionTest.class
                                              .getResource( "ngc1275.fits.gz" )
                                              .toString(),
                                               "fits" ) );
    }

    public void testInternalMatch() throws Exception {
        MapEnvironment env0 = new MapEnvironment()
            .setValue( "in", tngc_ )
            .setValue( "action", "identify" )
            .setValue( "matcher", "2d_ellipse" )
            .setValue( "progress", "none" );
        for ( int imult = 2; imult < 5; imult++ ) {
            String cartValues = "X_IMAGE Y_IMAGE "
                              + imult + "*A_IMAGE "
                              + imult + "*B_IMAGE "
                              + "THETA_IMAGE";
            String arcsecFact = "60*60";
            String skyValues = "ALPHA_SKY DELTA_SKY "
                             + imult + "*" + arcsecFact + "*A_WORLD "
                             + imult + "*" + arcsecFact + "*B_WORLD "
                             + "THETA_SKY";
            Object[] cartGroupCol = getGroupSizeCol( "2d_ellipse", cartValues );
            Object[] skyGroupCol = getGroupSizeCol( "skyellipse", skyValues );
            assertArrayEquals( cartGroupCol, skyGroupCol );
            Object[] regressGroupCol =
                getColData( tngc_, getColIndex( tngc_, "gsize_" + imult ) );
            assertArrayEquals( cartGroupCol, regressGroupCol );
        }
    }

    private Object[] getGroupSizeCol( String matcher, String values )
            throws Exception {
        MapEnvironment env = new MapEnvironment()
            .setValue( "in", tngc_ )
            .setValue( "action", "identify" )
            .setValue( "progress", "none" )
            .setValue( "matcher", matcher )
            .setValue( "values", values )
            .setValue( "params", "4" )
            .setValue( "ocmd", "keepcols 'GroupSize'" );
        new TableMatch1().createExecutable( env ).execute();
        return getColData( env.getOutputTable( "omode" ), 0 );
    }
}
