/****************************************************************************
 *
 * MODULE:       r.viewshed
 *
 * AUTHOR(S):    Laura Toma, Bowdoin College - ltoma@bowdoin.edu
 *               Yi Zhuang - yzhuang@bowdoin.edu

 *               Ported to GRASS by William Richard -
 *               wkrichar@bowdoin.edu or willster3021@gmail.com
 *               Markus Metz: surface interpolation
 *
 * Date:         april 2011
 *
 * PURPOSE: To calculate the viewshed (the visible cells in the
 * raster) for the given viewpoint (observer) location.  The
 * visibility model is the following: Two points in the raster are
 * considered visible to each other if the cells where they belong are
 * visible to each other.  Two cells are visible to each other if the
 * line-of-sight that connects their centers does not intersect the
 * terrain. The terrain is NOT viewed as a tessellation of flat cells,
 * i.e. if the line-of-sight does not pass through the cell center,
 * elevation is determined using bilinear interpolation.
 * The viewshed algorithm is efficient both in
 * terms of CPU operations and I/O operations. It has worst-case
 * complexity O(n lg n) in the RAM model and O(sort(n)) in the
 * I/O-model.  For the algorithm and all the other details see the
 * paper: "Computing Visibility on * Terrains in External Memory" by
 * Herman Haverkort, Laura Toma and Yi Zhuang.
 *
 * COPYRIGHT: (C) 2008 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.
 *
 *****************************************************************************/

#ifndef visibility_h
#define visibility_h

#include <grass/config.h>
#include <grass/iostream/ami.h>

#include "grid.h"

/*  default max distance */
#define INFINITY_DISTANCE -1

/* File/directory name lengths for GRASS compatibility */
#define GNAME_MAX         256
#define GPATH_MAX         4096

typedef struct viewpoint_ {
    dimensionType row, col;
    float elev;
    float target_offset;
} Viewpoint;

typedef enum {
    VISIBLE = 1,
    INVISIBLE = -1,

    /*boolean values for output */
    BOOL_VISIBLE = 1,
    BOOL_INVISIBLE = 0
} VisMode;

typedef struct visCell_ {
    dimensionType row;
    dimensionType col;
    /*   VisMode vis; */
    float angle;
} VisCell;

typedef enum outputMode_ {
    OUTPUT_ANGLE = 0,
    OUTPUT_BOOL = 1,
    OUTPUT_ELEV = 2
} OutputMode;

typedef struct viewOptions_ {

    /* the name of the input raster */
    char inputfname[GNAME_MAX];

    /* the name of the output raster */
    char outputfname[GNAME_MAX];

    float obsElev;
    /* observer elevation above the terrain */

    float tgtElev;
    /* target elevation offset above the terrain */

    float maxDist;
    /* points that are farther than this distance from the viewpoint are
       not visible  */

    float horizontal_angle_min;
    float horizontal_angle_max;
    int doDirection;
    /* exclude points outside of the angle range  */

    OutputMode outputMode;
    /* The mode the viewshed is output;
       - in angle mode, the values recorded are   {NODATA, INVISIBLE, angle}
       - in boolean mode, the values recorded are {BOOL_INVISIBLE, BOOL_VISIBLE}
       - in elev mode, the values recorded are    {NODATA, INVISIBLE, elevation}
     */

    int doCurv;
    /*determines if the curvature of the earth should be considered
       when calculating.  Only implemented for GRASS version. */

    int doRefr;
    double refr_coef;
    /*determines if atmospheric refraction should be considered
       when calculating.  Only implemented for GRASS version. */

    double ellps_a;            /* the parameter of the ellipsoid */
    float cellsize;            /* the cell resolution */
    char streamdir[GPATH_MAX]; /* directory for tmp files */
} ViewOptions;

/*memory visibility grid */
typedef struct memory_visibility_grid_ {
    Grid *grid;
    Viewpoint *vp;
} MemoryVisibilityGrid;

/*io-efficient visibility grid */
typedef struct IOvisibility_grid_ {
    GridHeader *hd;
    Viewpoint *vp;
    AMI_STREAM<VisCell> *visStr;
} IOVisibilityGrid;

/* ------------------------------------------------------------ */
/* visibility output functions */

/*  The following functions are used to convert the visibility results
   recorded during the viewshed computation into the output grid into
   the format required by the user.  x is assumed to be the
   visibility angle computed for a cell during the viewshed
   computation.

   The value passed to this function is the following: x is NODATA if the
   cell is NODATA; x is INVISIBLE if the cell is invisible; x is the
   vertical angle of the cell wrt the viewpoint if the cell is
   visible---the angle is a value in (0,180).
 */
/* these functions assume that x is a value computed during the
   viewshed computation; right now x represents the vertical angle of a
   visible point wrt to the viewpoint; INVISIBLE if invisible; NODATA if
   nodata. They return true if x is visible, invisible but nodata,
   andnodata, respectively  */
int is_visible(float x);
int is_invisible_not_nodata(float x);
int is_invisible_nodata(float x);

/* This function is called when the program runs in
   viewOptions.outputMode == OUTPUT_BOOL. */
float booleanVisibilityOutput(float x);

/* This function is called when the program runs in
   viewOptions.outputMode == OUTPUT_ANGLE.   */
float angleVisibilityOutput(float x);

/* ------------------------------------------------------------ */
/* viewpoint functions */

void print_viewpoint(Viewpoint vp);

/*copy from b to a */
void copy_viewpoint(Viewpoint *a, Viewpoint b);

void set_viewpoint_coord(Viewpoint *vp, dimensionType row, dimensionType col);

void set_viewpoint_elev(Viewpoint *vp, float elev);

/* ------------------------------------------------------------ */
/* MemoryVisibilityGrid functions */

MemoryVisibilityGrid *create_inmem_visibilitygrid(const GridHeader &hd,
                                                  Viewpoint vp);

void free_inmem_visibilitygrid(MemoryVisibilityGrid *visgrid);

void set_inmem_visibilitygrid(MemoryVisibilityGrid *visgrid, float val);

void add_result_to_inmem_visibilitygrid(MemoryVisibilityGrid *visgrid,
                                        dimensionType i, dimensionType j,
                                        float val);

void save_inmem_visibilitygrid(MemoryVisibilityGrid *vigrid,
                               ViewOptions viewopt, Viewpoint vp);

/* ------------------------------------------------------------ */
/* IOVisibilityGrid functions */

/*create grid from given header and viewpoint */
IOVisibilityGrid *init_io_visibilitygrid(const GridHeader &hd, Viewpoint vp);

/*frees a visibility grid */
void free_io_visibilitygrid(IOVisibilityGrid *grid);

/*write cell to stream */
void add_result_to_io_visibilitygrid(IOVisibilityGrid *visgrid, VisCell *cell);

/*void
   addResult(IOVisibilityGrid* visgrid, DimensionType row, DimensionType col,
   VisMode vis);
 */

/* write visibility grid. assume all cells that are not in stream are
   NOT visible.  assume stream is sorted.  */
void save_io_visibilitygrid(IOVisibilityGrid *visgrid, ViewOptions viewoptions,
                            Viewpoint vp);

/*sort stream in grid (i,j) order */
void sort_io_visibilitygrid(IOVisibilityGrid *visGrid);

class IJCompare {
public:
    int compare(const VisCell &, const VisCell &);
};

#endif
