#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "global.h"

int get_ref_window(struct Ref *ref, int *ref_list, struct Cell_head *cellhd)
{
    int i;
    int count;
    struct Cell_head win;

    /* from all the files in the group, get max extends and min resolutions */
    count = 0;
    for (i = 0; i < ref->nfiles; i++) {
	if (!ref_list[i])
	    continue;

	if (count++ == 0) {
	    Rast_get_cellhd(ref->file[i].name,
			    ref->file[i].mapset, cellhd);
	}
	else {
	    Rast_get_cellhd(ref->file[i].name,
			    ref->file[i].mapset, &win);
	    /* max extends */
	    if (cellhd->north < win.north)
		cellhd->north = win.north;
	    if (cellhd->south > win.south)
		cellhd->south = win.south;
	    if (cellhd->west > win.west)
		cellhd->west = win.west;
	    if (cellhd->east < win.east)
		cellhd->east = win.east;
	    /* min resolution */
	    if (cellhd->ns_res > win.ns_res)
		cellhd->ns_res = win.ns_res;
	    if (cellhd->ew_res > win.ew_res)
		cellhd->ew_res = win.ew_res;
	}
    }

    /* if the north-south is not multiple of the resolution,
     *    round the south downward
     */
    cellhd->rows = (cellhd->north - cellhd->south) / cellhd->ns_res + 0.5;
    cellhd->south = cellhd->north - cellhd->rows * cellhd->ns_res;

    /* do the same for the west */
    cellhd->cols = (cellhd->east - cellhd->west) / cellhd->ew_res + 0.5;
    cellhd->west = cellhd->east - cellhd->cols * cellhd->ew_res;

    return 1;
}

int georef_window(struct Ortho_Image_Group *group, struct Cell_head *w1,
                  struct Cell_head *w2, double res)
{
    double n, e, z1, ad;
    double n0, e0;
    double aver_z;
    struct _corner {
        double n, e;
    } nw, ne, se, sw;

    /* get an average elevation from the active control points */
    /* for mountainous areas this is a very rough approximation
     * which would become more accurate only if actual elevation 
     * values are used */
    get_aver_elev(&group->control_points, &aver_z);
    G_debug(1, "Aver elev = %f", aver_z);

    /* compute ortho ref of all corners */

    I_georef(w1->west, w1->north, &e0, &n0, group->E12, group->N12, 1);
    I_inverse_ortho_ref(e0, n0, aver_z, &e, &n, &z1, &group->camera_ref,
			group->XC, group->YC, group->ZC, group->MI);

    G_debug(1, "NORTH WEST CORNER");
    G_debug(1, "group->E12 = %f %f %f,", group->E12[0], group->E12[1],
	    group->E12[2]);
    G_debug(1, "group->N12 = %f %f %f,", group->N12[0], group->N12[1],
	    group->N12[2]);
    G_debug(1, "image  x = %f y = %f, photo x = %f y = %f", w1->west,
	    w1->north, e0, n0);
    G_debug(1, "target x = %f y = %f", e, n);

    w2->north = w2->south = n;
    w2->west = w2->east = e;
    nw.n = n;
    nw.e = e;

    I_georef(w1->east, w1->north, &e0, &n0, group->E12, group->N12, 1);
    I_inverse_ortho_ref(e0, n0, aver_z, &e, &n, &z1, &group->camera_ref,
			group->XC, group->YC, group->ZC, group->MI);

    G_debug(1, "NORTH EAST CORNER");
    G_debug(1, "image  x = %f y = %f, photo x = %f y = %f", w1->east,
	    w1->north, e0, n0);
    G_debug(1, "target x = %f y = %f", e, n);

    ne.n = n;
    ne.e = e;
    if (n > w2->north)
	w2->north = n;
    if (n < w2->south)
	w2->south = n;
    if (e > w2->east)
	w2->east = e;
    if (e < w2->west)
	w2->west = e;

    I_georef(w1->west, w1->south, &e0, &n0, group->E12, group->N12, 1);
    I_inverse_ortho_ref(e0, n0, aver_z, &e, &n, &z1, &group->camera_ref,
			group->XC, group->YC, group->ZC, group->MI);

    G_debug(1, "SOUTH WEST CORNER");
    G_debug(1, "image  x = %f y = %f, photo x = %f y = %f", w1->west,
	    w1->south, e0, n0);
    G_debug(1, "target x = %f y = %f", e, n);

    sw.n = n;
    sw.e = e;
    if (n > w2->north)
	w2->north = n;
    if (n < w2->south)
	w2->south = n;
    if (e > w2->east)
	w2->east = e;
    if (e < w2->west)
	w2->west = e;

    I_georef(w1->east, w1->south, &e0, &n0, group->E12, group->N12, 1);
    I_inverse_ortho_ref(e0, n0, aver_z, &e, &n, &z1, &group->camera_ref,
			group->XC, group->YC, group->ZC, group->MI);

    G_debug(1, "SOUTH EAST CORNER");
    G_debug(1, "image  x = %f y = %f, photo x = %f y = %f", w1->east,
	    w1->south, e0, n0);
    G_debug(1, "target x = %f y = %f", e, n);

    se.n = n;
    se.e = e;
    if (n > w2->north)
	w2->north = n;
    if (n < w2->south)
	w2->south = n;
    if (e > w2->east)
	w2->east = e;
    if (e < w2->west)
	w2->west = e;

    /* resolution */
    if (res > 0)
	w2->ew_res = w2->ns_res = res;
    else {
	/* this results in ugly res values, and ns_res != ew_res */
	/* and is no good for rotation */
	/*
	w2->ns_res = (w2->north - w2->south) / w1->rows;
	w2->ew_res = (w2->east - w2->west) / w1->cols;
	*/

	/* alternative: account for rotation and order > 1 */

	/* N-S extends along western and eastern edge */
	w2->ns_res = (sqrt((nw.n - sw.n) * (nw.n - sw.n) +
			  (nw.e - sw.e) * (nw.e - sw.e)) +
		     sqrt((ne.n - se.n) * (ne.n - se.n) +
			  (ne.e - se.e) * (ne.e - se.e))) / (2.0 * w1->rows);

	/* E-W extends along northern and southern edge */
	w2->ew_res = (sqrt((nw.n - ne.n) * (nw.n - ne.n) +
			  (nw.e - ne.e) * (nw.e - ne.e)) +
		     sqrt((sw.n - se.n) * (sw.n - se.n) +
			  (sw.e - se.e) * (sw.e - se.e))) / (2.0 * w1->cols);

	/* make ew_res = ns_res */
	w2->ns_res = (w2->ns_res + w2->ew_res) / 2.0;
	w2->ew_res = w2->ns_res;

	/* nice round values */
	if (w2->ns_res > 1) {
	    if (w2->ns_res < 10) {
		/* round to first decimal */
		w2->ns_res = (int)(w2->ns_res * 10 + 0.5) / 10.0;
		w2->ew_res = w2->ns_res;
	    }
	    else {
		/* round to integer */
		w2->ns_res = (int)(w2->ns_res + 0.5);
		w2->ew_res = w2->ns_res;
	    }
	}
    }

    /* adjust extents */
    ad = w2->north > 0 ? 0.5 : -0.5;
    w2->north = (int) (ceil(w2->north / w2->ns_res) + ad) * w2->ns_res;
    ad = w2->south > 0 ? 0.5 : -0.5;
    w2->south = (int) (floor(w2->south / w2->ns_res) + ad) * w2->ns_res;
    ad = w2->east > 0 ? 0.5 : -0.5;
    w2->east = (int) (ceil(w2->east / w2->ew_res) + ad) * w2->ew_res;
    ad = w2->west > 0 ? 0.5 : -0.5;
    w2->west = (int) (floor(w2->west / w2->ew_res) + ad) * w2->ew_res;

    w2->rows = (w2->north - w2->south + w2->ns_res / 2.0) / w2->ns_res;
    w2->cols = (w2->east - w2->west + w2->ew_res / 2.0) / w2->ew_res;

    G_debug(1, "FINAL");
    G_debug(1, "east = %f \n west = %f \n north = %f \n south = %f",
	    w2->east, w2->west, w2->north, w2->south);
    G_debug(1, "RESOLUTION");
    G_debug(1, "EW = %f", w2->ew_res);
    G_debug(1, "NS = %f", w2->ns_res);

    return 0;
}
