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

match.c (C-Munipack project)
Copyright (C) 2003 David Motl, dmotl@volny.cz

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

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

#include "comfun.h"
#include "console.h"
#include "cmpack_common.h"
#include "cmpack_catfile.h"
#include "cmpack_match.h"
#include "cmpack_phtfile.h"
#include "matfun.h"
#include "matread.h"
#include "matwrite.h"
#include "matsolve.h"
#include "matsimple.h"
#include "matstack.h"
#include "match.h"
#include "matphilnr.h"

/**********************   LOCAL FUNCTIONS   ******************************/

/* Clean context */
static void match_clear(CmpackMatch *lc)
{
	/* Free allocated memory */
	cmpack_free(lc->i1);
	cmpack_free(lc->x1);
	cmpack_free(lc->y1);
	if (lc->wcs) 
		cmpack_wcs_destroy(lc->wcs);
	if (lc->con) 
		cmpack_con_destroy(lc->con);
	PhiLnRClear(lc);
}

/*********************   PUBLIC FUNCTIONS   *******************************/

/* Context initialization */
CmpackMatch *cmpack_match_init(void)
{
	CmpackMatch *lc = (CmpackMatch*)cmpack_calloc(1, sizeof(CmpackMatch));
	lc->refcnt    = 1;
	lc->maxstar   = 10;
	lc->nstar     = 5;
	lc->clip      = 2.5;
	lc->method    = CMPACK_MATCH_STANDARD;
	lc->maxoffset = 5.0;
	return lc;
}

/* Increment the reference counter */
CmpackMatch *cmpack_match_reference(CmpackMatch *ctx)
{
	ctx->refcnt++;
	return ctx;
}

/* Decrement reference counter / detroy the instance */
void cmpack_match_destroy(CmpackMatch *ctx)
{
	if (ctx) {
		ctx->refcnt--;
		if (ctx->refcnt==0) {
			match_clear(ctx);
			cmpack_free(ctx);
		}
	}
}

/* Attach console */
void cmpack_match_set_console(CmpackMatch *ctx, CmpackConsole *con)
{
	if (con!=ctx->con) {
		if (ctx->con) 
			cmpack_con_destroy(ctx->con);
		ctx->con = con;
		if (ctx->con) 
			cmpack_con_reference(ctx->con);
	}
}

/* Set max. number of stars used for matching */
void cmpack_match_set_maxstars(CmpackMatch *ctx, int nstars)
{
	ctx->maxstar = nstars;
}

/* Get max. number of stars used for matching */
int cmpack_match_get_maxstars(CmpackMatch *ctx)
{
	return ctx->maxstar;
}

/* Set number of vertices of polygons */
void cmpack_match_set_vertices(CmpackMatch *ctx, int vertices)
{
	ctx->nstar = vertices;
}

/* Get number of vertices of polygons */
int cmpack_match_get_vertices(CmpackMatch *ctx)
{
	return ctx->nstar;
}

/* Set clipping threshold */
void cmpack_match_set_threshold(CmpackMatch *ctx, double threshold)
{
	ctx->clip = threshold;
}

/* Get clipping threshold */
double cmpack_match_get_threshold(CmpackMatch *ctx)
{
	return ctx->clip;
}

/* Set matching method */
void cmpack_match_set_method(CmpackMatch *ctx, CmpackMatchMethod method)
{
	ctx->method = method;
}

/* Get matching method */
CmpackMatchMethod cmpack_match_get_method(CmpackMatch *ctx)
{
	return ctx->method;
}

/* Set Max. offset for 'sparse fields' method */
void cmpack_match_set_maxoffset(CmpackMatch *ctx, double maxoffset)
{
	ctx->maxoffset = maxoffset;
}

/* Get Max. offset for 'sparse fields' method */
double cmpack_match_get_maxoffset(CmpackMatch *ctx)
{
	return ctx->maxoffset;
}

/* Ignore the specified objects during matching */
void cmpack_match_set_ignore(CmpackMatch *ctx, int src_obj, int ref_obj)
{
	ctx->ignore[0] = ref_obj;
	ctx->ignore[1] = src_obj;
}

/* Set coordinates of an object on the reference frame */
void cmpack_match_set_objref(CmpackMatch *ctx, int src_obj, int ref_obj)
{
	ctx->setref[0] = ref_obj;
	ctx->setref[1] = src_obj;
}

/* Set coordinates of an object on the reference frame */
void cmpack_match_set_objpos(CmpackMatch *ctx, int ref_id, double x, double y)
{
	ctx->setpos_ref_id = ref_id;
	ctx->setpos_xy[0] = x;
	ctx->setpos_xy[1] = y;
}

/* Get number of objects on the reference frame */
int cmpack_match_refcount(CmpackMatch *ctx)
{
	return ctx->c1;
}

/* Read reference file */
int cmpack_match_readref_pht(CmpackMatch *lc, CmpackPhtFile *pht)
{
	char msg[MAXLINE];

	/* Check source file */
	if (!pht) {
		printout(lc->con, 0, "Invalid reference file context");
		return CMPACK_ERR_INVALID_PAR;
	}

	/* Read reference stars */
	ReadRef(lc, pht);  
	sprintf(msg,"No. of stars   : %d",lc->c1);
	printout(lc->con, 1, msg);

  	/* Check reference file */
	if (lc->method==CMPACK_MATCH_STANDARD) {
		if (lc->c1<lc->nstar) {
	        printout(lc->con, 0,"Too few stars in reference file!");
			return CMPACK_ERR_FEW_POINTS_REF;
		}
	} else {
		if (lc->c1<1) {
			printout(lc->con, 0,"Too few stars in reference file!");
			return CMPACK_ERR_FEW_POINTS_REF;
		}
	}

  	/* Normal return */
  	return 0;
}

/* Read reference file */
int cmpack_match_readref_cat(CmpackMatch *lc, CmpackCatFile *cat)
{
	char msg[MAXLINE];

	/* Check source file */
	if (!cat) {
		printout(lc->con, 0, "Invalid reference file context");
		return CMPACK_ERR_INVALID_PAR;
	}

	/* Read reference stars */
	ReadCat(lc, cat);
	sprintf(msg,"No. of stars   : %d",lc->c1);
	printout(lc->con, 1, msg);

  	/* Check reference file */
	if (lc->method==CMPACK_MATCH_STANDARD) {
		if (lc->c1<lc->nstar) {
	        printout(lc->con, 0,"Too few stars in reference file!");
			return CMPACK_ERR_FEW_POINTS_REF;
		}
	} else {
		if (lc->c1<1) {
			printout(lc->con, 0,"Too few stars in reference file!");
			return CMPACK_ERR_FEW_POINTS_REF;
		}
	}

  	/* Normal return */
  	return 0;
}

static int match(CmpackMatch *lc, CmpackPhtFile *infile, CmpackPhtFile *outfile)
{
	char	msg[MAXLINE];
	int		res;

	/* Read input file */
    ReadSrc(lc, infile);
	sprintf(msg,"Number of stars: %d", lc->c2);
	printout(lc->con, 1, msg);

    /* Find coincidences */
	switch (lc->method) 
	{
	case CMPACK_MATCH_AUTO:
		/* Use standard method if we have enough stars */
		if (lc->c1>=lc->nstar && lc->c2>=lc->nstar) 
			res = Solve(lc);
		else
			res = Simple(lc);
		break;
	case CMPACK_MATCH_STANDARD:
		/* Always use standard method */
		res = Solve(lc);
	    break;
	case CMPACK_MATCH_SPARSE_FIELDS:
		/* Always use method for sparse fields */
		res = Simple(lc);
		break;
	case CMPACK_MATCH_PHILNR:
		/* Always use Phi-LnR method */
		res = PhiLnRMatch(lc);
		break;
	default:
		printout(lc->con, 0, "Unsupported matching method");
		res = CMPACK_ERR_INVALID_PAR;
    }

	if (res==0) {
		if (lc->matched_stars>0) {
			sprintf(msg,"Result         : %d matched (%.0f%%)", lc->matched_stars, (100.0*lc->matched_stars)/lc->c2);
			printout(lc->con, 1, msg);
		} else {
			printout(lc->con, 1,"Result     : Coincidences not found!");
			cmpack_matrix_identity(&lc->matrix);
		}
		MatWrite(lc, outfile);
	}

    /* Normal return */
    return res;
}

int cmpack_match(CmpackMatch *lc, CmpackPhtFile *file, int *mstars)
{
	int res;

	if (mstars)
		*mstars = 0;
	lc->matched_stars = 0;
	lc->width1 = lc->height1 = lc->width2 = lc->height2 = 0;
	cmpack_matrix_identity(&lc->matrix);

	if (!file) {
		printout(lc->con, 0,"Invalid photometry file context");
		return CMPACK_ERR_INVALID_PAR;
	}
	res = match(lc, file, file);
	if (res==0 && mstars)
		*mstars = lc->matched_stars;
	return res;
}

int cmpack_match_get_offset(CmpackMatch *lc, double *offset_x, double *offset_y)
{
	double x, y;

	if (!lc) 
		return CMPACK_ERR_INVALID_CONTEXT;
	if (lc->matched_stars==0)
		return CMPACK_ERR_UNDEF_VALUE;

	x = 0.5*lc->width2;
	y = 0.5*lc->height2;
	cmpack_matrix_transform_point(&lc->matrix, &x, &y);
	if (offset_x)
		*offset_x = x - 0.5*lc->width1;
	if (offset_y)
		*offset_y = y - 0.5*lc->height1;
	return 0;
}

int cmpack_match_get_matrix(CmpackMatch *lc, CmpackMatrix *matrix)
{
	if (!lc) 
		return CMPACK_ERR_INVALID_CONTEXT;

	cmpack_matrix_copy(matrix, &lc->matrix);
	return 0;
}


/* Clear output */
void match_frame_reset(CmpackMatch *lc)
{
	lc->matched_stars = 0;
	if (lc->xref && lc->c2>0)
		memset(lc->xref, 255, lc->c2*sizeof(int));
	cmpack_matrix_identity(&lc->matrix);
}
