/****************************************************************************
 *
 * 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 - 2011 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 _EVENTLIST_H
#define _EVENTLIST_H

#include "grid.h"
#include "visibility.h"

#include <grass/iostream/ami.h>

#define ENTERING_EVENT 1
#define EXITING_EVENT  -1
#define CENTER_EVENT   0

typedef struct event_ {
    dimensionType row, col; // location of the center of cell
    // 3 elevation values:
    // elev[0]: entering
    // elev[1]: center
    // elev[2]: exiting
    surface_type elev[3]; // elevation here
    double angle;
    char eventType;

    // type of the event: ENTERING_EVENT,  EXITING_EVENT, CENTER_EVENT
} AEvent;

/* ------------------------------------------------------------ */
/*determines if the point at row,col is outside the maximum distance
   limit wrt viewpoint.   Return 1 if the point is outside
   limit, 0 if point is inside limit. */
int is_point_outside_max_dist(Viewpoint vp, GridHeader hd, dimensionType row,
                              dimensionType col, float maxDist);

/*determines if the point at row,col is within min and max angle (in 0-360, 0 is
 * east, CCW) */
int is_point_inside_angle(Viewpoint vp, dimensionType row, dimensionType col,
                          float minAngle, float maxAngle);

/*sort the event list by the angle around the viewpoint) */
void sort_event_list(AMI_STREAM<AEvent> **eventList);

class RadialCompare {
public:
    int compare(const AEvent &, const AEvent &);
};
int radial_compare_events(const void *a, const void *b);

/*sort the event list by the distance from the viewpoint */
class DistanceCompare {
public:
    int compare(const AEvent &, const AEvent &);
};
void print_event(AEvent a, int debug_level);

/*computes the distance from the event to the viewpoint. Note: all 3
   //events associate to a cell are considered at the same distance, from
   //the center of the cell to the viewpoint */
double get_square_distance_from_viewpoint(const AEvent &a, const Viewpoint &vp);

/*sort the event list in distance order */
void sort_event_list_by_distance(AMI_STREAM<AEvent> **eventList, Viewpoint vp);

/*return the angle from this event wrt viewpoint; the type of the
   //event is taken into position to compute a different amngle for each
   //event associated with a cell */
double calculate_event_angle(AEvent *e, Viewpoint *vp);

/*compute the gradient of the CENTER of this event wrt viewpoint. For
   //efficiency it does not compute the gradient, but the square of the
   //tan of the gradient. Assuming all gradients are computed the same
   //way, this is correct. */
double calculate_center_gradient(AEvent *e, Viewpoint *vp);

/*calculate the exit angle corresponding to this cell */
double calculate_exit_angle(dimensionType row, dimensionType col,
                            Viewpoint *vp);

/*calculate the enter angle corresponding to this cell */
double calculate_enter_angle(dimensionType row, dimensionType col,
                             Viewpoint *vp);

/*calculate the exact position of the given event, and store them in x
   //and y. */
void calculate_event_position(AEvent e, dimensionType viewpointRow,
                              dimensionType viewpointCol, double *y, double *x);
/* calculate the neighbouring row, col of the given event, and store them in x
   //and y. */
void calculate_event_row_col(AEvent e, dimensionType viewpointRow,
                             dimensionType viewpointCol, int *y, int *x);

double calculate_angle(double eventX, double eventY, double viewpointX,
                       double viewpointY);

#endif
