/**
 * @file geometry.h
 * @brief Structs and interfaces for investigating the geometry and relations of cells
 *
 * The functions are used to determine relations between
 * source and target cells. Complete cells are constructed
 * on the sphere by connecting cell vertices along either
 * great circles (along the orthodrome) or directly (along
 * the loxodrome).
 *
 * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
 *                                 Rene Redler <rene.redler@mpimet.mpg.de>
 *
 * @version 1.0
 * @author Moritz Hanke <hanke@dkrz.de>
 *         Rene Redler <rene.redler@mpimet.mpg.de>
 */
/*
 * Keywords:
 * Maintainer: Moritz Hanke <hanke@dkrz.de>
 *             Rene Redler <rene.redler@mpimet.mpg.de>
 * URL: https://redmine.dkrz.de/doc/YAC/html/index.html
 *
 * This file is part of YAC.
 *
 * YAC 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 3 of the License, or
 * (at your option) any later version.
 *
 * YAC 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 YAC.  If not, see <http://www.gnu.org/licenses/gpl.txt>.
 */

#ifndef GEOMETRY_H
#define GEOMETRY_H

#include "grid.h"

struct line {
   struct {
      double x;
      double y;
   } p1, p2;
};

struct point {
   double lon, lat;
};

struct edge {
   struct point points[2];
   enum edge_type edge_type;
};

/**
 * defines a spherical cap, which is used as a convex hull to describe the extents of subdomain
 */
struct bounding_circle {

   //! the middle point of the spherical cap in spherical coordinates (in rad)
   double base_point[2];
   //! the middle point of the spherical cap in cartesian coordinates (is a unit vector)
   double base_vector[3];
   //! angle between the middle point and the boundary of the spherical cap
   double inc_angle; // height >= pi -> everything
};

/** \example test_check_overlap.c
 * This contains examples on how to use check_overlap_cells.
 */

/**
 * checks whether two cells overlap \n
 * @param[in] cell_a
 * @param[in] cell_b
 * @return 0 if both cells do not overlap
 * @see check_overlap_cells
 */
int check_overlap_cells (struct grid_cell const cell_a, 
                         struct grid_cell const cell_b);

/** \example test_point_in_cell.c
 * This contains examples on how to use point_in_cell.
 */

/**
 * checks whether a given point is within a given cell \n
 * @param[in] point
 * @param[in] cell
 * @return 0 if the point is not in the cell
 */
int point_in_cell (struct point point, struct grid_cell cell);

/**
 * computes the angle between two longitude coordinates (in rad) \n
 * takes into account that longitude have a period of 2 pi
 * @param[in] a_lon
 * @param[in] b_lon
 * @return angle between both coordinates (in rad)
 */
double get_angle(double a_lon, double b_lon);

/** \example test_find_overlap.c
 * This contains an example on how to use find_overlapping_cells.
 */

/**
 * searches for overlapping cells between grids \n
 * the user needs to provide a dependency list containing an initial search result that is used as a starting point, which is then refined
 * @param[in] src_grid source %grid
 * @param[in] tgt_grid target %grid
 * @param[in] initial_src_to_tgt_dep dependency list containing for each source cell a list of target cell that might overlap
 * @param[out] src_to_tgt_dep dependency list containing the result of the search
 */
void find_overlapping_cells (struct grid * src_grid, struct grid * tgt_grid,
                             struct dep_list initial_src_to_tgt_dep, 
                             struct dep_list * src_to_tgt_dep);

/**
 * searches for all overlapping cells of a given grid that overlap with a
 * given cell\n
 * the user needs to provide initial search results that are used as a starting
 * point, which is then refined
 * @param[in]     src_cell             cell for which the search is to conducted
 * @param[in]     tgt_grid             grid on which overlapping cells are to
 *                                     be searched
 * @param[in]     initial_dep          initial search results
 * @param[in]     num_initial_deps     number of dependencies in initial_dep
 * @param[in,out] deps                 NULL or pointer to pointer returned by a
 *                                     standard allocation routine
 * @param[in,out] deps_size            number of elements that fit into deps or
 *                                     0 if deps equals NULL
 * @param[out]    num_deps             number of overlapping cells
 * @param[in]     src_index            any number (no entry in
 *                                     tgts_already_touched is allowed to be
 *                                     bigger than src_index)
 * @param[in,out] tgts_already_touched array with the size of the target grid
 *                                     and no entry bigger than src_index
 * @param[in,out] stack                NULL or pointer to pointer returned by a
 *                                     standard allocation routine
 * @param[in,out] stack_size           number of elements that fit into stack
 *                                     or 0 if stack equals NULL
 * @remarks the user is responsible to free the memory associated with deps
 *          and stack
 */
void find_overlapping_cells_s (struct grid_cell src_cell,
                               struct grid * tgt_grid,
                               unsigned const * initial_dep,
                               unsigned num_initial_deps, unsigned ** deps,
                               unsigned * deps_size, unsigned * num_deps,
                               unsigned src_index,
                               unsigned * tgts_already_touched,
                               unsigned ** stack, unsigned * stack_size);

/** \example test_gcxgc.c
 * This contains examples on how to use gcxgc.
 */

/**
 * computes the intersection points of two great circles \n
 * based on http://www.geoclub.de/viewtopic.php?f=54&t=29689
 * @param[in] edge_a edge defining a great circle
 * @param[in] edge_b edge defining a great circle
 * @param[out] p intersection point
 * @param[out] q intersection point
 * @return  0 if the intersection points are neither on edge a or b \n
 *          1st bit will be set if p is on edge a \n
 *          2nd bit will be set if q is on edge a \n
 *          3rd bit will be set if p is on edge b \n
 *          4th bit will be set if q is on edge b
  *         5th bit will be set if both great circles are identically
 *
 * \remark the user can provide NULL for p and/or q in that case the intersection points will not be returned
 */
int gcxgc (struct edge edge_a, struct edge edge_b,
           struct point * p, struct point * q);

/** \example test_gcxgc_vec.c
 * This contains examples on gcxgc_vec
 */

/**
 * computes the intersection points of two great circles \n
 * based on http://www.geoclub.de/viewtopic.php?f=54&t=29689
 * @param[in] a first point of edge a the is on a great circle
 * @param[in] b second point of edge a the is on a great circle
 * @param[in] c first point of edge b the is on a great circle
 * @param[in] d second point of edge b the is on a great circle
 * @param[out] p intersection point
 * @param[out] q intersection point
 * @return  0 if the intersection points are neither on edge a or b \n
 *          1st bit will be set if p is on edge a \n
 *          2nd bit will be set if q is on edge a \n
 *          3rd bit will be set if p is on edge b \n
 *          4th bit will be set if q is on edge b
  *         5th bit will be set if both great circles are identically
 *
 * \remark the user can provide NULL for p and/or q in that case the intersection points will not be returned
 */
 int gcxgc_vec (double a[3], double b[3], double c[3], double d[3],
                double p[3], double q[3]);

/** \example test_loncxlatc_vec.c
 * This contains examples on loncxlatc_vec
 */

/** \brief compute the intersection point of a meridian and a parallel
 *
 * compute the intersection points of a circle of longitude (defined by a and b)
 * and a circle of latitude (defined by c and d)
 * if p and q are != NULL they contain the intersection points
 * the return value is:
 *      - 0 if the intersection points are neither between (a and b) or (c and d)
 *      - -1 if an error occurred
 *      - 1st bit will be set if p is between a and b
 *      - 2nd bit will be set if q is between a and b
 *      - 3rd bit will be set if p is between c and d
 *      - 4th bit will be set if q is between c and d
 **/
int loncxlatc_vec (double a[3], double b[3], double c[3], double d[3],
                   double p[3], double q[3]);

/** \example test_latcxlatc_vec.c
 * This contains examples on latcxlatc_vec
 */

/** \brief compute the intersection point two circles of latitude
 *
 * compute the intersection points of two circle of latitude
 * if p and q are != NULL they contain the intersection points
 * the return value is:
 *      - 0 if the intersection points are neither between (a and b) or (c and d)
 *      - -1 if an error occurred
 *      - 1st bit will be set if p is between a and b
 *      - 2nd bit will be set if q is between a and b
 *      - 3rd bit will be set if p is between c and d
 *      - 4th bit will be set if q is between c and d
 *      - 5th bit will be set if both edges are on the same circle of latitude
 **/
int latcxlatc_vec (double a[3], double b[3], double c[3], double d[3],
                   double p[3], double q[3]);

/** \example test_loncxlonc_vec.c
 * This contains examples on loncxlonc_vec
 */

/** \brief compute the intersection point two circles of longitude
 *
 * compute the intersection points of two circle of longitude
 * if p and q are != NULL they contain the intersection points
 * the return value is:
 *      - 0 if the intersection points are neither between (a and b) or (c and d)
 *      - -1 if an error occurred
 *      - 1st bit will be set if p is between a and b
 *      - 2nd bit will be set if q is between a and b
 *      - 3rd bit will be set if p is between c and d
 *      - 4th bit will be set if q is between c and d
 *      - 5th bit will be set if both edges are on the same circle of longitude
 **/
int loncxlonc_vec (double a[3], double b[3], double c[3], double d[3],
                   double p[3], double q[3]);

/** \example test_gcxlatc.c
 * This contains examples on 
 */

/**
 * compute the intersection points of a great circles
 * and a circle of latitude \n
 * based on http://geospatialmethods.org/spheres/GCIntersect.html
 * @param[in] edge_a edge defining a great circle
 * @param[in] edge_b edge defining a circle of latitude
 * @param[out] p intersection point
 * @param[out] q intersection point
 * @return  0 if the intersection points are neither on edge a or b \n
 *         -1 if the two circles do not intersect \n
 *          1st bit will be set if p is on edge a \n
 *          2nd bit will be set if q is on edge a \n
 *          3rd bit will be set if p is on edge b \n
 *          4th bit will be set if q is on edge b
 *
 * \remarks if -1 is returned neither p or q is set
 * \remarks if the two circles only have one intersection point,
 *          p and q will be identically, but only the p bits will be set
 */
int gcxlatc (struct edge edge_a, struct edge edge_b,
             struct point * p, struct point * q);

/** \example test_gcxlatc_vec.c
 * This contains examples on gcxlatc_vec
 */

/**
 * compute the intersection points of a great circles
 * and a circle of latitude \n
 * based on http://geospatialmethods.org/spheres/GCIntersect.html
 * @param[in] edge_a edge defining a great circle
 * @param[in] edge_b edge defining a circle of latitude
 * @param[out] p intersection point
 * @param[out] q intersection point
 * @return  0 if the intersection points are neither on edge a or b \n
 *         -1 if the two circles do not intersect \n
 *          1st bit will be set if p is on edge a \n
 *          2nd bit will be set if q is on edge a \n
 *          3rd bit will be set if p is on edge b \n
 *          4th bit will be set if q is on edge b
 *
 * \remarks if -1 is returned neither p or q is set
 * \remarks if the two circles only have one intersection point,
 *          p and q will be identically, but only the p bits will be set
 */
int gcxlatc_vec(double a[3], double b[3], double c[3], double d[3],
                double p[3], double q[3]);

/**
 * computes the intersection points of two edges
 * @param[in]  edge_a       edge a
 * @param[in]  edge_b       edge b
 * @param[out] intersection intersection point
 * @return  0 if the edges do not intersect \n
 *          1 if the edges intersect
 *
 * \remarks if intersection is not NULL and the edges intersect
 *          the intersection point is returned
 * \remarks only one intersection is returned even if the edges intersect twice
 */
int intersect (struct edge const edge_a, struct edge const edge_b,
               struct point * intersection);

/**
 * computes the intersection points of two edges
 * @param[in]  edge_type_a type of edge a
 * @param[in]  a           first point of edge a
 * @param[in]  b           second point of edge a
 * @param[in]  edge_type_b type of edge b
 * @param[in]  c           first point of edge b
 * @param[in]  d           second point of edge b
 * @param[out] p           first intersection point
 * @param[out] q           second intersection point
 * @return  0 if the intersection points are neither on edge a or b \n
 *         -1 if the two circles do not intersect \n
 *          1st bit will be set if p is on edge a \n
 *          2nd bit will be set if q is on edge a \n
 *          3rd bit will be set if p is on edge b \n
 *          4th bit will be set if q is on edge b \n
 *          5th bit will be set if both circles are identically
 *
 * \remarks if -1 is returned neither p or q is set
 * \remarks if the two circles only have one intersection point,
 *          p and q will be identically, but only the p bits will be set
 */
int intersect_vec (enum edge_type edge_type_a, double a[3], double b[3],
                   enum edge_type edge_type_b, double c[3], double d[3],
                   double p[3], double q[3]);

/**
 * gets the bounding circle for a grid cell
 * @param[in] cell grid cell (coordinates have to be in radian)
 * @param[out] bnd_circle bounding circle of the grid cell
 */
void get_cell_bounding_circle(struct grid_cell cell,
                              struct bounding_circle * bnd_circle);

/** \example test_bnd_circle.c
 * These are some examples on how to use \ref get_grid_bounding_circle.
 */

/**
 * gets a bounding circle for a grid
 * @param[in] grid
 * @param[out] bnd_circle bounding circle of the grid
 */
void get_grid_bounding_circle(struct grid * grid,
                              struct bounding_circle * bnd_circle);

/**
 * gets all cells of a grid that have an overlap with the area defined by the given bounding circle
 * @param[in] grid
 * @param[in] extent bounding circle defining an area against which all cells of the grid are going to checked
 * @param[in,out] matching_cells array containing the matching cells (if required the array will be reallocated)
 * @param[in,out] curr_matching_cells_array_size size of matching_cells array (if the matching_cells array is reallocated the size will be updated as well)
 * @param[in,out] local_ids local ids of the matching cells (if required the array will be reallocated)
 * @param[in,out] curr_local_ids_array_size size of matching_cells array (if the local_ids array is reallocated the size will be updated as well)
 * @param[out] num_matching_cells number of matching cells
 * @param[in] offset number of cells/local ids already in the matching_cells and local_ids array (these will not be overwritten)
 *
 * \remark *matching_cells == NULL and *curr_matching_cells_array_size == 0 are valid input values
 * \remark *local_ids == NULL and *curr_local_ids_array_size == 0 are valid input values
 * \remark if matching_cells == NULL or local_ids == NULL the respective data is not returned by this routine
 */
void get_matching_grid_cells(struct grid * grid, struct bounding_circle extent,
                             struct grid_cell ** matching_cells,
                             unsigned * curr_matching_cells_array_size,
                             unsigned ** local_ids, unsigned * curr_local_ids_array_size,
                             unsigned * num_matching_cells, unsigned offset);

/**
 * checks whether two extents overlap
 * @param[in] extent_a bounding circle
 * @param[in] extent_b bounding circle
 * @return 0 if the bounding circles do not overlap
 */
unsigned extents_overlap(struct bounding_circle * extent_a,
                         struct bounding_circle * extent_b);

/**
 * checks whether a point is within a bounding circle
 * @param[in] point point to be checked (coordinates in radian)
 * @param[in] bnd_circle bounding circle
 * @return 0 if point is not within the bounding circle
 */
unsigned point_in_bounding_circle(struct point point,
                                  struct bounding_circle * bnd_circle);

/**
 * converts lon-lat coordinates into xyz ones
 * @param[in]  lon   longitude coordinates in radian
 * @param[in]  lat   latitude coordinates in radian
 * @param[out] p_out xyz coordinates
 */
void LLtoXYZ(double lon, double lat, double p_out[]);

/**
 * converts lon-lat coordinates into xyz ones
 * @param[in]  lon   longitude coordinates in deg
 * @param[in]  lat   latitude coordinates in deg
 * @param[out] p_out xyz coordinates
 */
void LLtoXYZ_deg(double lon, double lat, double p_out[]);

/**
 * converts lon-lat coordinates into xyz ones
 * @param[in]  p_in xyz coordinates
 * @param[out] lon  longitude coordinate in radian
 * @param[out] lat  latitude coordinate in radian
 */
void XYZtoLL(double p_in[], double * lon, double * lat);

/**
 * computes the great circle distance in rad for two points given in xyz coordinates
 * @param[in] a_vector point coordinates of point a
 * @param[in] b_vector point coordinates of point b
 * @return great circle distance in rad between both points
 */
double get_vector_angle(double a_vector[3], double b_vector[3]);

/**
 * computes the great circle distance in rad for two points given in lon-lat coordinates
 * @param[in] a point coordinates of point a (in rad)
 * @param[in] b point coordinates of point b (in rad)
 * @return great circle distance in rad between both points
 */
double get_point_angle(struct point * a, struct point * b);

#endif // GEOMETRY_H
