/**
 * @file grid_unstruct.c
 *
 * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
 *
 * @version 1.0
 * @author Moritz Hanke <hanke@dkrz.de>
 */
/*
 * Keywords:
 * Maintainer: Moritz Hanke <hanke@dkrz.de>
 * URL: https://doc.redmine.dkrz.de/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>.
 */

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

#include "grid.h"
#include "grid_scrip.h"
#include "utils.h"
#include "dep_list.h"
#include "geometry.h"
#include "utils.h"
#include "ensure_array_size.h"
#include "sphere_part.h"
//#include "bucket_search.h"

// routine declarations

static void get_grid_cell_scrip(struct grid * grid, unsigned cell_index,
                                   struct grid_cell * cell);
static void get_grid_cell2_scrip(struct grid * grid, unsigned cell_index,
                                    struct grid_cell * cell,
                                    struct bounding_circle * bnd_circle);
static unsigned get_size_x_coords_scrip(struct grid * grid);
static unsigned get_size_y_coords_scrip(struct grid * grid);
static unsigned get_size_cell_grid_x_coords_scrip(struct grid * grid);
static unsigned get_size_cell_grid_y_coords_scrip(struct grid * grid);
static unsigned get_num_grid_cells_scrip(struct grid * grid);
static unsigned get_num_grid_corners_scrip(struct grid * grid);
static unsigned get_num_cell_corners_scrip(struct grid * grid, unsigned cell_index);
static struct grid_search * get_grid_search_scrip(struct grid * grid);
static int cell_contains_non_gc_edge_scrip(struct grid * grid, unsigned local_cell_id);
static void delete_grid_scrip(struct grid * grid);

static struct grid_vtable scrip_grid_vtable = {

   .copy                        = NULL,
   .get_2d_extent               = NULL,
   .get_grid_cell               = get_grid_cell_scrip,
   .get_grid_cell2              = get_grid_cell2_scrip,
   .get_size_x_coords           = get_size_x_coords_scrip,
   .get_size_y_coords           = get_size_y_coords_scrip,
   .get_x_coords                = NULL,
   .get_y_coords                = NULL,
   .set_x_coords                = NULL,
   .set_y_coords                = NULL,
   .get_size_cell_grid_x_coords = get_size_cell_grid_x_coords_scrip,
   .get_size_cell_grid_y_coords = get_size_cell_grid_y_coords_scrip,
   .get_num_grid_cells          = get_num_grid_cells_scrip,
   .get_num_grid_corners        = get_num_grid_corners_scrip,
   .get_num_cell_corners        = get_num_cell_corners_scrip,
   .get_num_corner_cells        = NULL,
   .get_num_grid_edges          = NULL,
   .get_num_corner_edges        = NULL,
   .get_num_cell_edges          = NULL,
   .get_corner_edges            = NULL,
   .get_cell_edge_indices       = NULL,
   .get_edge_type               = NULL,
   .get_cell_corner_indices     = NULL,
   .get_corner_cell_indices     = NULL,
   .get_cell_x_coord_indices    = NULL,
   .get_cell_y_coord_indices    = NULL,
   .get_corner_x_coord          = NULL,
   .get_corner_y_coord          = NULL,
   .get_corner_x_coord_index    = NULL,
   .get_corner_y_coord_index    = NULL,
   .get_aux_grid_cell           = NULL,
   .get_cell_neigh_dep_list     = NULL,
   .get_boundary_corners        = NULL,
   .generate_cell_grid          = NULL,
   .generate_edge_grid          = NULL,
   .generate_subgrid            = NULL,
   .pack_grid                   = NULL,
   .get_grid_search             = get_grid_search_scrip,
   .cell_contains_non_gc_edge   = cell_contains_non_gc_edge_scrip,
   .grid_delete                 = delete_grid_scrip
};

struct scrip_grid {

   struct grid_vtable * vtable;

   double * cell_corners_x,       //!< latitude data
          * cell_corners_y,       //!< longitude data
          (*cell_corners_xyz)[3]; //!< 3d coordinates

   unsigned cell_corners_x_by_user; //!< indicates whether cell_corners_x was provided by
                                    //!< the user or automatically generated by a grid
                                    //!< routine
   unsigned cell_corners_y_by_user; //!< indicates whether cell_corners_y was provided by
                                    //!< the user or automatically generated by a grid
                                    //!< routine

   unsigned num_cells;
   unsigned max_corners;

   struct grid_search * grid_search;
};


static unsigned get_num_cell_corners_scrip (struct grid * grid, unsigned cell_index) {

   (void) cell_index;
   struct scrip_grid * scrip_grid = (struct scrip_grid *)grid;

   return scrip_grid->max_corners;
}

static unsigned get_size_x_coords_scrip(struct grid * grid) {

   struct scrip_grid * scrip_grid = (struct scrip_grid *)grid;

   return scrip_grid->num_cells;
}

static unsigned get_size_y_coords_scrip(struct grid * grid) {

   struct scrip_grid * scrip_grid = (struct scrip_grid *)grid;

   return scrip_grid->num_cells;
}

static unsigned get_size_cell_grid_x_coords_scrip(struct grid * grid) {

   struct scrip_grid * scrip_grid = (struct scrip_grid *)grid;

   return scrip_grid->num_cells;
}

static unsigned get_size_cell_grid_y_coords_scrip(struct grid * grid) {

   struct scrip_grid * scrip_grid = (struct scrip_grid *)grid;

   return scrip_grid->num_cells;
}

static unsigned get_num_grid_cells_scrip(struct grid * grid) {

   struct scrip_grid * scrip_grid = (struct scrip_grid *)grid;

   return scrip_grid->num_cells;
}

static unsigned get_num_grid_corners_scrip(struct grid * grid) {

   struct scrip_grid * scrip_grid = (struct scrip_grid *)grid;

   return scrip_grid->num_cells;
}

static void get_grid_cell_scrip(struct grid * grid, unsigned cell_index,
                                struct grid_cell * cell) {

   struct scrip_grid * scrip_grid = (struct scrip_grid *)grid;

   size_t num_corners = get_num_cell_corners_scrip(grid, cell_index);

   // if the memory for the coordinates needs to be reallocated
   if (num_corners > cell->array_size) {
      cell->coordinates_x = realloc (cell->coordinates_x, num_corners * sizeof(cell->coordinates_x[0]));
      cell->coordinates_y = realloc (cell->coordinates_y, num_corners * sizeof(cell->coordinates_y[0]));
      cell->coordinates_xyz = realloc (cell->coordinates_xyz, num_corners * sizeof(cell->coordinates_xyz[0]));
      cell->edge_type = realloc (cell->edge_type, num_corners * sizeof(cell->edge_type[0]));
      cell->array_size = num_corners;
   }

   cell->num_corners = num_corners;

   // set the corners for of the cell
   size_t offset = cell_index*num_corners;
   for (size_t i = 0; i < num_corners; ++i) {

      cell->coordinates_x[i] = scrip_grid->cell_corners_x[offset+i];
      cell->coordinates_y[i] = scrip_grid->cell_corners_y[offset+1];
      cell->coordinates_xyz[i][0] = scrip_grid->cell_corners_xyz[offset+i][0];
      cell->coordinates_xyz[i][1] = scrip_grid->cell_corners_xyz[offset+i][1];
      cell->coordinates_xyz[i][2] = scrip_grid->cell_corners_xyz[offset+i][2];
   }

   // set the edge type for the cell
   for (size_t i = 0; i < cell->num_corners; ++i)
         cell->edge_type[i] = GREAT_CIRCLE;
}

static void get_grid_cell2_scrip(struct grid * grid, unsigned cell_index,
                                    struct grid_cell * cell,
                                    struct bounding_circle * bnd_circle) {

   get_grid_cell_scrip(grid, cell_index, cell);
   if (cell->num_corners == 3) {
      yac_get_cell_circumscribe_circle_unstruct_triangle(cell->coordinates_xyz[0],
                                                         cell->coordinates_xyz[1],
                                                         cell->coordinates_xyz[2],
                                                         bnd_circle);
   } else
      yac_get_cell_bounding_circle(*cell, bnd_circle);
}

static void delete_grid_scrip(struct grid * grid) {

   struct scrip_grid * scrip_grid = (struct scrip_grid *)grid;

   if (!scrip_grid->cell_corners_x_by_user)
      free(scrip_grid->cell_corners_x);
   if (!scrip_grid->cell_corners_y_by_user)
      free(scrip_grid->cell_corners_y);
   free(scrip_grid->cell_corners_xyz);

   if (scrip_grid->grid_search != NULL)
      yac_delete_grid_search(scrip_grid->grid_search);

   free(scrip_grid);
}

struct grid * yac_scrip_grid_new(double * cell_corners_x, double * cell_corners_y,
                                 size_t num_cells, unsigned max_corners) {

   struct scrip_grid *scrip_grid = malloc(1 * sizeof(*scrip_grid));

   scrip_grid->vtable = &scrip_grid_vtable;

   scrip_grid->num_cells = num_cells;
   scrip_grid->max_corners = max_corners;

   double (*cell_corners_xyz)[3] = malloc(num_cells * max_corners * sizeof(*(scrip_grid->cell_corners_xyz)));

   for (size_t i = 0; i < num_cells; ++i)
     {
       size_t offset = i*max_corners;
       for (unsigned k = 0; k < max_corners; ++k)
         {
           LLtoXYZ(cell_corners_x[offset + k],
                   cell_corners_y[offset + k],
                   cell_corners_xyz[offset + k]);
         }
     }

   scrip_grid->cell_corners_x = cell_corners_x;
   scrip_grid->cell_corners_y = cell_corners_y;
   scrip_grid->cell_corners_xyz = cell_corners_xyz;

   scrip_grid->cell_corners_x_by_user = 1 == 1;
   scrip_grid->cell_corners_y_by_user = 1 == 1;

   scrip_grid->grid_search = NULL;

   return (struct grid *)scrip_grid;
}

static struct grid_search * get_grid_search_scrip(struct grid * grid) {

   struct scrip_grid * scrip_grid;

   scrip_grid = (struct scrip_grid *)grid;

   if (scrip_grid->grid_search == NULL)
      scrip_grid->grid_search = yac_sphere_part_search_new(grid);

   return scrip_grid->grid_search;
}

static int cell_contains_non_gc_edge_scrip(struct grid * grid, unsigned local_cell_id) {
  (void)grid;
  (void)local_cell_id;
  return 0;
}
