/*
 * Copyright(C) 1999-2020 National Technology & Engineering Solutions
 * of Sandia, LLC (NTESS).  Under the terms of Contract DE-NA0003525 with
 * NTESS, the U.S. Government retains certain rights in this software.
 *
 * See packages/seacas/LICENSE for details
 */

#include "smalloc.h" // for smalloc_ret
#include "structs.h" // for vtx_data
#include <stdio.h>   // for NULL, fprintf, printf, FILE

/* Change from a FORTRAN graph style to our graph data structure. */

int reformat(int *              start,     /* start of edge list for each vertex */
             int *              adjacency, /* edge list data */
             int                nvtxs,     /* number of vertices in graph */
             int *              pnedges,   /* ptr to number of edges in graph */
             int *              vwgts,     /* weights for all vertices */
             float *            ewgts,     /* weights for all edges */
             struct vtx_data ***pgraph     /* ptr to array of vtx data for graph */
)
{
  extern FILE *     Output_File;      /* output file or null */
  struct vtx_data **graph     = NULL; /* array of vtx data for graph */
  struct vtx_data * links     = NULL; /* space for data for all vtxs */
  int *             edges     = NULL; /* space for all adjacency lists */
  float *           eweights  = NULL; /* space for all edge weights */
  int *             eptr      = NULL; /* steps through adjacency list */
  int *             eptr_save = NULL; /* saved index into adjacency list */
  float *           wptr      = NULL; /* steps through edge weights list */
  int               self_edge;        /* number of self loops detected */
  int               size;             /* length of all edge lists */
  double            sum;              /* sum of edge weights for a vtx */
  int               using_ewgts;      /* are edge weights being used? */
  int               using_vwgts;      /* are vertex weights being used? */
  int               i, j;             /* loop counters */

  using_ewgts = (ewgts != NULL);
  using_vwgts = (vwgts != NULL);

  graph   = smalloc_ret((nvtxs + 1) * sizeof(struct vtx_data *));
  *pgraph = graph;
  if (graph == NULL) {
    return (1);
  }

  graph[1] = NULL;

  /* Set up all the basic data structure for the vertices. */
  /* Replace many small mallocs by a few large ones. */
  links = smalloc_ret((nvtxs) * sizeof(struct vtx_data));
  if (links == NULL) {
    return (1);
  }

  for (i = 1; i <= nvtxs; i++) {
    graph[i] = links++;
  }

  graph[1]->edges = NULL;
  graph[1]->ewgts = NULL;

  /* Now fill in all the data fields. */
  if (start != NULL) {
    *pnedges = start[nvtxs] / 2;
  }
  else {
    *pnedges = 0;
  }
  size  = 2 * (*pnedges) + nvtxs;
  edges = smalloc_ret(size * sizeof(int));
  if (edges == NULL) {
    return (1);
  }

  if (using_ewgts) {
    eweights = smalloc_ret(size * sizeof(float));
    if (eweights == NULL) {
      return (1);
    }
  }

  if (start != NULL) {
    eptr = adjacency + start[0];
    wptr = ewgts;
  }
  self_edge = 0;

  for (i = 1; i <= nvtxs; i++) {
    if (using_vwgts) {
      graph[i]->vwgt = *(vwgts++);
    }
    else {
      graph[i]->vwgt = 1;
    }
    if (start != NULL) {
      size = start[i] - start[i - 1];
    }
    else {
      size = 0;
    }
    graph[i]->nedges = size + 1;
    graph[i]->edges  = edges;
    *edges++         = i;
    eptr_save        = eptr;
    for (j = size; j; j--) {
      if (*eptr != i) {
        *edges++ = *eptr++;
      }
      else { /* Self edge, skip it. */
        if (!self_edge) {
          printf("WARNING: Self edge (%d,%d) being ignored\n", i, i);
          if (Output_File != NULL) {
            fprintf(Output_File, "WARNING: Self edge (%d,%d) being ignored\n", i, i);
          }
        }
        ++self_edge;
        eptr++;
        --(graph[i]->nedges);
        --(*pnedges);
      }
    }
    if (using_ewgts) {
      graph[i]->ewgts = eweights;
      eweights++;
      sum = 0;
      for (j = size; j; j--) {
        if (*eptr_save++ != i) {
          sum += *wptr;
          *eweights++ = *wptr++;
        }
        else {
          wptr++;
        }
      }
      graph[i]->ewgts[0] = -sum;
    }
    else {
      graph[i]->ewgts = NULL;
    }
  }
  if (self_edge > 1) {
    printf("WARNING: %d self edges were detected and ignored\n", self_edge);
    if (Output_File != NULL) {
      fprintf(Output_File, "WARNING: %d self edges were detected and ignored\n", self_edge);
    }
  }

  return (0);
}
