/*
**  Written by Bill Brown  1992 
**  US Army Construction Engineering Research Lab
*/

/*
** Copyright USA CERL 1992. All rights reserved.
*/


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <grass/gis.h>
#include <grass/glocale.h>

#define REQ_KEYS 8

static int get_bool();
static int read_old_format();
static void pr_winerr ();
static void edge_sort();
static int compare_wind();

static int vers_major = 4;
static int vers_minor = 1;
static int Suppress_warn = 0;

/* ********************************************************************
 * If Suppress_warn is 0, a warning will be printed if less than 95% of
 * the window when the view was saved overlaps the current window.
*/

int G_3dview_warning(int b)
{
    Suppress_warn = b? 0:1;

    return 0;
}


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

int G_get_3dview_defaults(
struct G_3dview *v,
struct Cell_head *w)
{
    if(!v || !w)
	return(-1);

    v->exag = 1.0;
    v->fov = 40.0; 
    v->from_to[0][0] = (w->east + w->west) / 2.0;
    v->from_to[0][1] = w->south - (w->north - w->south); 
    v->from_to[0][2] = w->north - w->south; 
    v->from_to[1][0] = (w->east + w->west) / 2.0; 
    v->from_to[1][1] = (w->north + w->south) / 2.0; 
    v->from_to[1][2] = 0.0; 

    v->twist = 0.0;
    v->mesh_freq = 15;
    v->poly_freq = 1;
    v->display_type = 2;
    v->colorgrid = v->fringe = v->surfonly = v->lightson = v->doavg = 0;
    v->dozero = v->shading = 1;
    strcpy(v->bg_col,"black");
    strcpy(v->grid_col,"white");
    strcpy(v->other_col,"red");
    v->ambient = v->shine = 0.3;
    v->lightcol[0] = v->lightcol[1] = v->lightcol[2] = 0.8;
    v->lightpos[0] = w->west;
    v->lightpos[1] = w->north;
    v->lightpos[2] = (w->east - w->west) / 2.0;
    v->lightpos[3] = 1.0;     /* local source */
    
    v->vwin.north = w->north;
    v->vwin.south = w->south;
    v->vwin.east  = w->east ;
    v->vwin.west  = w->west ;
    v->vwin.format = w->format;
    v->vwin.compressed = w->compressed;
    v->vwin.proj = w->proj;
    v->vwin.zone = w->zone;
    v->vwin.ew_res = w->ew_res;
    v->vwin.ns_res = w->ns_res; 
    v->vwin.cols = w->cols;
    v->vwin.rows = w->rows;

    return (1);

}

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


/* ********************************************************************
 * Library routine for saving info in a 3d.view file.  Requires:
 *
 *    name of file
 *    name of mapset
 *    address of a struct G_3dview with values set. 
 *
 *    The address of a window (struct Cell_head *) may 
 *    be passed, or if NULL is passed, the Cell_head structure inside 
 *    the G_3dview struct will be used.  e.g., if you called
 *    G_get_3dview_defaults with the Cell_head you want saved, the 
 *    G_3dview returned already contains the new Cell_head. But if 
 *    you're using all the keywords, so didn't need defaults, pass this
 *    routine the address of a Cell_head.
 *
 *    These keywords are constant in all 3d.view files:
 * 
 *		PGM_ID
 *		       cell keywords:
 *		north
 *		south
 *		east
 *		west
 *		rows 
 *		cols
 *		     required keywords:
 *		TO_EASTING
 *		TO_NORTHING
 *		TO_HEIGHT
 *		FROM_EASTING
 *		FROM_NORTHING
 *		FROM_HEIGHT
 *		Z_EXAG
 *		FIELD_VIEW
 *		     optional keywords: (defaults provided when reading)
 *		TWIST
 *		MESH_FREQ
 *		POLY_RES
 *              DOAVG
 *		DISPLAY_TYPE         User should call G_get_3dview_defaults
 *		DOZERO               before filling a G_3dview struct
 *		COLORGRID            to be written if not using all of
 *		SHADING              the optional keywords.
 *		FRINGE
 *              BG_COL
 *              GRID_COL
 *              OTHER_COL
 *		LIGHTS_ON
 *		LIGHTPOS
 *		LIGHTCOL
 *		LIGHTAMBIENT
 *		SHINE
 *		SURFACEONLY
 *
 *      
 * Returns -1 on failure
*/

int G_put_3dview(
char *fname,
char *mapset,
struct G_3dview *View,
struct Cell_head *Win)
{
    FILE *fp;
    char err_buf[80];

    if(NULL == (fp = G_fopen_new("3d.view",fname))){
	sprintf(err_buf,_("Unable to open %s for writing"), fname);
	G_warning (err_buf);
	return(-1);
    }

    fprintf(fp,"# %01d.%02d\n", vers_major,vers_minor);
    fprintf(fp,"PGM_ID: %s\n", View->pgm_id);

    if(Win){
	fprintf(fp,"north: %f\n",Win->north);
	fprintf(fp,"south: %f\n",Win->south);
	fprintf(fp,"east: %f\n",Win->east);
	fprintf(fp,"west: %f\n",Win->west);
	fprintf(fp,"rows: %d\n",Win->rows);
	fprintf(fp,"cols: %d\n",Win->cols);
    }
    else{
	fprintf(fp,"north: %f\n",View->vwin.north);
	fprintf(fp,"south: %f\n",View->vwin.south);
	fprintf(fp,"east: %f\n",View->vwin.east);
	fprintf(fp,"west: %f\n",View->vwin.west);
	fprintf(fp,"rows: %d\n",View->vwin.rows);
	fprintf(fp,"cols: %d\n",View->vwin.cols);
    }

    fprintf(fp,"TO_EASTING: %f\n",View->from_to[1][0]);
    fprintf(fp,"TO_NORTHING: %f\n",View->from_to[1][1]);
    fprintf(fp,"TO_HEIGHT: %f\n",View->from_to[1][2]);
    fprintf(fp,"FROM_EASTING: %f\n",View->from_to[0][0]);
    fprintf(fp,"FROM_NORTHING: %f\n",View->from_to[0][1]);
    fprintf(fp,"FROM_HEIGHT: %f\n",View->from_to[0][2]);
    fprintf(fp,"Z_EXAG: %f\n",View->exag);
    fprintf(fp,"TWIST: %f\n",View->twist);
    fprintf(fp,"FIELD_VIEW: %f\n",View->fov);
    fprintf(fp,"MESH_FREQ: %d\n",View->mesh_freq);
    fprintf(fp,"POLY_RES: %d\n",View->poly_freq);
    fprintf(fp,"DOAVG: %d\n",View->doavg);
    fprintf(fp,"DISPLAY_TYPE: %d\n",View->display_type);
    fprintf(fp,"DOZERO: %d\n",View->dozero);
    
    fprintf(fp,"COLORGRID: %d\n",View->colorgrid); /* 1 = use color */
    fprintf(fp,"SHADING: %d\n",View->shading);
    fprintf(fp,"FRINGE: %d\n",View->fringe);
    fprintf(fp,"BG_COL: %s\n",View->bg_col);
    fprintf(fp,"GRID_COL: %s\n",View->grid_col);
    fprintf(fp,"OTHER_COL: %s\n",View->other_col);
    fprintf(fp,"SURFACEONLY: %d\n",View->surfonly);
    fprintf(fp,"LIGHTS_ON: %d\n",View->lightson);
    fprintf(fp,"LIGHTPOS: %f %f %f %f\n",View->lightpos[0],View->lightpos[1],
				    View->lightpos[2],View->lightpos[3]);
    fprintf(fp,"LIGHTCOL: %f %f %f\n",View->lightcol[0],
				    View->lightcol[1],View->lightcol[2]);
    fprintf(fp,"LIGHTAMBIENT: %f\n",View->ambient);
    fprintf(fp,"SHINE: %f\n",View->shine);

    fclose(fp);
    return (1);
}

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



/* ********************************************************************
 * returns -1 on failure, 0 for old (through 4.0) d.3d format file
 * or if the window was not saved in the 3dview file,
 * 2 if file was written using current version of this routine, otherwise 1.
 * Defaults provided for non-required keywords.  If reading an old format,
 * the window boundaries are not checked against the current window since
 * boundaries weren't saved.
*/

int G_get_3dview(
char *fname,
char *mapset,
struct G_3dview *View)
{
    struct Cell_head curwin;
    FILE *fp;
    char err_buf[80], buffer[80], keystring[24], boo[8], nbuf[128], ebuf[128];
    int current = 0;                /* current version flag */
    int lap, v_maj, v_min, wind_keys = 0, reqkeys = 0;


    mapset = G_find_file2 ("3d.view", fname, mapset);
    if(mapset != NULL)
    {

	if(NULL == (fp = G_fopen_old("3d.view",fname,mapset))){
	    sprintf(err_buf,_("Unable to open %s for reading"), fname);
	    G_warning (err_buf);
	    return(-1);
	}

	G_get_set_window (&curwin);
	G_get_3dview_defaults(View, &curwin);

	if(NULL != fgets(buffer, 80, fp)){
	    if(buffer[0] != '#'){ /* old d.3d format */
		rewind(fp);
		if(0 <= read_old_format(View, fp))
		    return(0);
		else
		    return(-1);	
	    }
	    else{
		sscanf(buffer,"#%d.%d\n", &v_maj,&v_min);
		if(v_maj == vers_major && v_min == vers_minor)
		    current = 1;   /* same version */
	    }
	}

	while(NULL != fgets(buffer, 75, fp)){
	    if(buffer[0] != '#'){

		sscanf(buffer,"%[^:]:", keystring);

		if(!strcmp(keystring,"PGM_ID")){
		    sscanf(buffer,"%*s%s", (View->pgm_id));
		    continue;
		}
		if(!strcmp(keystring,"north")){
		    sscanf(buffer,"%*s%lf", &(View->vwin.north));
		    ++wind_keys;
		    continue;
		}
		if(!strcmp(keystring,"south")){
		    sscanf(buffer,"%*s%lf", &(View->vwin.south));
		    ++wind_keys;
		    continue;
		}
		if(!strcmp(keystring,"east")){
		    sscanf(buffer,"%*s%lf", &(View->vwin.east));
		    ++wind_keys;
		    continue;
		}
		if(!strcmp(keystring,"west")){
		    sscanf(buffer,"%*s%lf", &(View->vwin.west));
		    ++wind_keys;
		    continue;
		}
		if(!strcmp(keystring,"rows")){
		    sscanf(buffer,"%*s%d", &(View->vwin.rows));
		    ++wind_keys;
		    continue;
		}
		if(!strcmp(keystring,"cols")){
		    sscanf(buffer,"%*s%d", &(View->vwin.cols));
		    ++wind_keys;
		    continue;
		}
		if(!strcmp(keystring,"TO_EASTING")){
		    sscanf(buffer,"%*s%f", &(View->from_to[1][0]));
		    ++reqkeys;
		    continue;
		}
		if(!strcmp(keystring,"TO_NORTHING")){
		    sscanf(buffer,"%*s%f", &(View->from_to[1][1]));
		    ++reqkeys;
		    continue;
		}
		if(!strcmp(keystring,"TO_HEIGHT")){
		    sscanf(buffer,"%*s%f", &(View->from_to[1][2]));
		    ++reqkeys;
		    continue;
		}
		if(!strcmp(keystring,"FROM_EASTING")){
		    sscanf(buffer,"%*s%f", &(View->from_to[0][0]));
		    ++reqkeys;
		    continue;
		}
		if(!strcmp(keystring,"FROM_NORTHING")){
		    sscanf(buffer,"%*s%f", &(View->from_to[0][1]));
		    ++reqkeys;
		    continue;
		}
		if(!strcmp(keystring,"FROM_HEIGHT")){
		    sscanf(buffer,"%*s%f", &(View->from_to[0][2]));
		    ++reqkeys;
		    continue;
		}
		if(!strcmp(keystring,"Z_EXAG")){
		    sscanf(buffer,"%*s%f", &(View->exag));
		    ++reqkeys;
		    continue;
		}
		if(!strcmp(keystring,"MESH_FREQ")){
		    sscanf(buffer,"%*s%d", &(View->mesh_freq));
		    continue;
		}
		if(!strcmp(keystring,"POLY_RES")){
		    sscanf(buffer,"%*s%d", &(View->poly_freq));
		    continue;
		}
		if(!strcmp(keystring,"DOAVG")){
		    sscanf(buffer,"%*s%d", &(View->doavg));
		    continue;
		}
		if(!strcmp(keystring,"FIELD_VIEW")){
		    sscanf(buffer,"%*s%f", &(View->fov));
		    ++reqkeys;
		    continue;
		}
		if(!strcmp(keystring,"TWIST")){
		    sscanf(buffer,"%*s%f", &(View->twist));
		    continue;
		}
		if(!strcmp(keystring,"DISPLAY_TYPE")){
		    sscanf(buffer,"%*s%d", &View->display_type);
		    continue;
		}
		if(!strcmp(keystring,"DOZERO")){
		    sscanf(buffer,"%*s%s", boo);
		    View->dozero = get_bool(boo);
		    continue;
		}
		if(!strcmp(keystring,"COLORGRID")){
		    sscanf(buffer,"%*s%s", boo);
		    View->colorgrid = get_bool(boo);
		    continue;
		}
		if(!strcmp(keystring,"FRINGE")){
		    sscanf(buffer,"%*s%s", boo);
		    View->fringe = get_bool(boo);
		    continue;
		}
		if(!strcmp(keystring,"SHADING")){
		    sscanf(buffer,"%*s%s", boo);
		    View->shading = get_bool(boo);
		    continue;
		}
		if(!strcmp(keystring,"BG_COL")){
		    sscanf(buffer,"%*s%s", View->bg_col);
		    continue;
		}
		if(!strcmp(keystring,"GRID_COL")){
		    sscanf(buffer,"%*s%s", View->grid_col);
		    continue;
		}
		if(!strcmp(keystring,"OTHER_COL")){
		    sscanf(buffer,"%*s%s", View->other_col);
		    continue;
		}
		if(!strcmp(keystring,"SURFACEONLY")){
		    sscanf(buffer,"%*s%s", boo);
		    View->surfonly = get_bool(boo);
		    continue;
		}
		if(!strcmp(keystring,"LIGHTS_ON")){
		    sscanf(buffer,"%*s%s", boo);
		    View->lightson = get_bool(boo);
		    continue;
		}
		if(!strcmp(keystring,"LIGHTPOS")){
		    sscanf(buffer,"%*s%f%f%f%f", &(View->lightpos[0]),
			&(View->lightpos[1]), &(View->lightpos[2]),
			&(View->lightpos[3]));
		    continue;
		}
		if(!strcmp(keystring,"LIGHTCOL")){
		    sscanf(buffer,"%*s%f%f%f", &(View->lightcol[0]),
			&(View->lightcol[1]), &(View->lightcol[2]));
		    continue;
		}
		if(!strcmp(keystring,"LIGHTAMBIENT")){
		    sscanf(buffer,"%*s%f", &(View->ambient));
		    continue;
		}
		if(!strcmp(keystring,"SHINE")){
		    sscanf(buffer,"%*s%f", &(View->shine));
		    continue;
		}
	    }
	}

	fclose(fp);

	if(reqkeys != REQ_KEYS)   /* required keys not found */
	    return (-1);

	/* fill rest of View->vwin */  
	if(wind_keys == 6){
	    View->vwin.ew_res = (View->vwin.east - View->vwin.west) / 
							    View->vwin.cols;
	    View->vwin.ns_res = (View->vwin.north - View->vwin.south) / 
							    View->vwin.rows;
	}
	else
	    return(0);    /* older format */


	if(!Suppress_warn){
	    if(95 > (lap = compare_wind(&(View->vwin),&curwin))){

		fprintf(stderr,_("GRASS window when view was saved:\n"));
		G_format_northing(View->vwin.north, nbuf, G_projection()) ;
		fprintf(stderr,"north:   %s\n", nbuf);
		G_format_northing(View->vwin.south, nbuf, G_projection()) ;
		fprintf(stderr,"south:   %s\n", nbuf);
		G_format_easting (View->vwin.east,  ebuf, G_projection()) ;
		fprintf(stderr,"east:    %s\n", ebuf);
		G_format_easting (View->vwin.west,  ebuf, G_projection()) ;
		fprintf(stderr,"west:    %s\n", ebuf);
		pr_winerr(lap,fname);
	    }
	}
    }
    else{
	sprintf(err_buf,_("Unable to open %s for reading"), fname);
	G_warning (err_buf);
	return(-1);
    }
    if(current) return(2);
    return(1);
}

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



/* *********************************************************************
 * returns the percentage of savedwin that overlaps curwin 
*/

static int compare_wind(
struct Cell_head *savedwin, 
struct Cell_head *curwin)
{
float e_ings[4], n_ings[4], area_lap, area_saved;
int outside = 0;

    if(savedwin->north < curwin->south) outside = 1;
    if(savedwin->south > curwin->north) outside = 1;
    if(savedwin->east < curwin->west) outside = 1;
    if(savedwin->west > curwin->east) outside = 1;
    if(outside) return (0);
    
    e_ings[0] = savedwin->west;
    e_ings[1] = savedwin->east;
    e_ings[2] = curwin->west;
    e_ings[3] = curwin->east;
    edge_sort(e_ings);

    n_ings[0] = savedwin->south;
    n_ings[1] = savedwin->north;
    n_ings[2] = curwin->south;
    n_ings[3] = curwin->north;
    edge_sort(n_ings);

    area_lap = (e_ings[2] - e_ings[1]) * (n_ings[2] - n_ings[1]);
    area_saved = (savedwin->east - savedwin->west) * 
				(savedwin->north - savedwin->south);

    return ((int)(area_lap * 100.0 / area_saved));
}

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


static int get_bool( char *str)
{
    if (str[0] == 'y' || str[0] == 'Y')
	return (1);
    if (str[0] == 'n' || str[0] == 'N')
	return (0);
    return (atoi(str) ? 1 : 0);
}
    
/*********************************************************************/


static void pr_winerr (
    int vis,         /* % of saved window overlapping current window */
    char *viewname)
{
    char err_buf[80];

    switch(vis)
    {
    case 0:
	sprintf(err_buf,_(" Window saved in \"%s\" is completely outside of current GRASS window."), viewname);
	G_warning (err_buf);
	break;
    default:
	sprintf(err_buf,_(" Only %d%% of window saved in \"%s\" overlaps with current GRASS window."), vis, viewname);
	G_warning (err_buf);
	break;

    }
}

/*********************************************************************/
/* sorts 4 floats from lowest to highest */

static void edge_sort (float sides[4])
{
    int i,j;
    float temp;

    for (i = 0; i < 4; ++i){
	for (j = i + 1; j < 4; ++j){
	    if (sides[j] < sides[i]){    /* then swap */
		temp = sides[i];
		sides[i] = sides[j];
		sides[j] = temp;
	    }
	}
    }
}

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


static int read_old_format(
struct G_3dview *v,
FILE *fp)
{
    char buffer[80];
    int req_keys = 0;
    double td;
    char boo[8];


        strcpy((v->pgm_id), "d.3d");
	if(1 == sscanf(fgets(buffer, 80, fp),"%f",&(v->from_to[1][0])) )
	    ++req_keys;
	if(1 == sscanf(fgets(buffer, 80, fp),"%f",&(v->from_to[1][1])) )
	    ++req_keys;
	if(1 == sscanf(fgets(buffer, 80, fp),"%f",&(v->from_to[1][2])) )
	    ++req_keys;
	if(1 == sscanf(fgets(buffer, 80, fp),"%f",&(v->from_to[0][0])) )
	    ++req_keys;
	if(1 == sscanf(fgets(buffer, 80, fp),"%f",&(v->from_to[0][1])) )
	    ++req_keys;
	if(1 == sscanf(fgets(buffer, 80, fp),"%f",&(v->from_to[0][2])) )
	    ++req_keys;
	if(1 == sscanf(fgets(buffer, 80, fp),"%f",&(v->exag)) )
	    ++req_keys;
	sscanf(fgets(buffer, 80, fp),"%d",&(v->mesh_freq)) ;
	if(1 == sscanf(fgets(buffer, 80, fp),"%f",&(v->fov)) )
	    ++req_keys;
	if(1 == sscanf(fgets(buffer, 80, fp),"%lf",&td) ){  /* resolution */
	    v->vwin.rows = (v->vwin.north - v->vwin.south) / td ;
	    v->vwin.cols = (v->vwin.east - v->vwin.west) / td ;
	    v->vwin.ew_res = v->vwin.ns_res = td; 
	}

	sscanf(fgets(buffer, 80, fp),"%s",boo) ; /* linesonly */
	    v->display_type = get_bool(boo)? 1 : 3;
	sscanf(fgets(buffer, 80, fp),"%s",boo) ;
	    v->dozero = get_bool(boo);
	sscanf(fgets(buffer, 80, fp),"%s",v->grid_col) ;
	if (! strcmp(v->grid_col, "color"))
	    v->colorgrid = 1;

	sscanf(fgets(buffer, 80, fp),"%s",v->other_col) ;
	sscanf(fgets(buffer, 80, fp),"%s",v->bg_col) ;
	sscanf(fgets(buffer, 80, fp),"%s",boo) ;
	    v->doavg = get_bool(boo);

	if(v->exag){  /* old 3d.view files saved height with no exag */ 
	    v->from_to[0][2] /= v->exag;
	    v->from_to[1][2] /= v->exag;
	}


	fclose(fp) ;
	if(req_keys == REQ_KEYS)
	    return (1);
	else 
	    return (-1);
}

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



