/*****************************************************************************
 *
 * This MobilityDB code is provided under The PostgreSQL License.
 * Copyright (c) 2016-2025, Université libre de Bruxelles and MobilityDB
 * contributors
 *
 * MobilityDB includes portions of PostGIS version 3 source code released
 * under the GNU General Public License (GPLv2 or later).
 * Copyright (c) 2001-2025, PostGIS contributors
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without a written
 * agreement is hereby granted, provided that the above copyright notice and
 * this paragraph and the following two paragraphs appear in all copies.
 *
 * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
 * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
 * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
 * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 *****************************************************************************/

/**
 * @file
 * @brief A simple program that reads from a CSV file synthetic trip data in
 * Brussels generated by the MobilityDB-BerlinMOD generator,
 * https://github.com/MobilityDB/MobilityDB-BerlinMOD
 * assembles the trips from the individual observations, and write them in a
 * CSV file named "berlinmod_trips_new.csv" which corresponds to the file
 * "berlinmod_trips.csv" in the data directory.
 *
 * The input file is
 * - `berlinmod_instants.csv`: individual observations from 55 trips of 5 cars
 *   during 4 days obtained from the generator at scale factor 0.005.
 *   This file is generated by the program `05_berlinmod_disassemble`.
 * In the above file, the coordinates are given in the 3857 coordinate system,
 * https://epsg.io/3857
 * and the timestamps are given in the Europe/Brussels time zone.
 * The input file can be generated by running the program
 * `05_berlinmod_disassemble.c` in the current directory
 * This simple program does not cope with erroneous inputs, such as missing
 * fields or invalid values.
 *
 * The program can be build as follows
 * @code
 * gcc -Wall -g -I/usr/local/include -o 03_berlinmod_assemble 03_berlinmod_assemble.c -L/usr/local/lib -lmeos
 * @endcode
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <meos.h>
#include <meos_geo.h>

/* Maximum number of instants in an input trip */
#define MAX_NO_INSTS 64000
/* Number of instants in a batch for printing a marker */
#define NO_INSTS_BATCH 1000
/* Maximum length in characters of a header record in the input CSV file */
#define MAX_LEN_HEADER 1024
/* Maximum length in characters of a point in the input data */
#define MAX_LEN_POINT 100
/* Maximum length in characters of a date in the input data */
#define MAX_LEN_DATE 12
/* Maximum length in characters of a timestamp in the input data */
#define MAX_LEN_TIMESTAMP 32
/* Maximum number of trips */
#define MAX_TRIPS 5

typedef struct
{
  int tripid;      /* Identifier of a trip */
  int vehid;       /* Identifier of a vehicle */
  DateADT day;     /* Day of a trip */
  int seq;         /* Sequence number of a trip in a day of a vehicle */
  int no_instants; /* Number of input instants */
  TInstant *trip_instants[MAX_NO_INSTS]; /* Array of instants for a trip */
} trip_record;

/* Main program */
int main(void)
{
  /* Input variables for reading a record */
  int tripid;
  int vehid;
  DateADT day;
  int seq;
  GSERIALIZED *point;
  TimestampTz t;
  /* Allocate space to build the trips */
  trip_record trips[MAX_TRIPS] = {0};
  /* Number of trips */
  int no_trips = 0;
  /* Iterator variables */
  int i, j;
  /* Exit value initialized to failure to quickly exit upon error */
  int exit_value = EXIT_FAILURE;

  /* Get start time */
  clock_t tm;
  tm = clock();

  /* Initialize MEOS */
  meos_initialize();

  /* Substitute the full file path in the first argument of fopen */
  FILE *file = fopen("data/berlinmod_instants.csv", "r");
  if (! file)
  {
    printf("Error opening input file\n");
    goto cleanup;
  }

  int no_records = 0;
  int no_nulls = 0;
  char header_buffer[MAX_LEN_HEADER];
  char point_buffer[MAX_LEN_POINT];
  char date_buffer[MAX_LEN_DATE];
  char timestamp_buffer[MAX_LEN_TIMESTAMP];

  /* Read the first line of the file with the headers */
  fscanf(file, "%1023s\n", header_buffer);

  printf("Reading the instants (one '*' marker every %d instants)\n",
    NO_INSTS_BATCH);

  /* Continue reading the file */
  do
  {
    int read = fscanf(file, "%d,%d,%10[^,],%d,%99[^,],%31[^\n]\n",
      &tripid, &vehid, date_buffer, &seq, point_buffer,
      timestamp_buffer);
    if (ferror(file))
    {
      printf("Error reading input file");
      goto cleanup;
    }
    if (read != 6)
    {
      printf("Record with missing values ignored\n");
      no_nulls++;
      continue;
    }

    no_records++;
    if (no_records % NO_INSTS_BATCH == 0)
    {
      printf("*");
      fflush(stdout);
    }

    /* Transform the string representing the date into a date value */
    day = date_in(date_buffer);
    /* Transform the string representing the trip into a temporal value */
    point = geom_in(point_buffer, -1);
    /* Transform the string representing the timestamp into a timestamp value */
    t = timestamp_in(timestamp_buffer, -1);
    /* Transform the string representing the trip into a temporal value */
    TInstant *inst = tpointinst_make(point, t);
    /* Free the point as it is not needed anymore */
    free(point);

    /* Find the place to store the new instant */
    int tripIdx = -1;
    for (i = 0; i < MAX_TRIPS; i++)
    {
      if (trips[i].tripid == tripid)
      {
        tripIdx = i;
        break;
      }
    }
    if (tripIdx < 0)
    {
      tripIdx = no_trips++;
      if (tripIdx == MAX_TRIPS)
      {
        printf("\nThe maximum number of trips in the input file is bigger than %d\n",
          MAX_TRIPS);
        fclose(file);
        goto cleanup;
      }
    }

    if (trips[tripIdx].no_instants == MAX_NO_INSTS)
    {
      printf("\nThe maximum number of instants for trips in the input file is bigger than %d\n",
        MAX_NO_INSTS);
      goto cleanup;
    }

    /*
     * Store the input values
     */
    trips[tripIdx].tripid = tripid;
    trips[tripIdx].vehid = vehid;
    trips[tripIdx].day = day;
    trips[tripIdx].seq = seq;
    trips[tripIdx].trip_instants[trips[tripIdx].no_instants++] = inst;
  } while (!feof(file));

  /* Close the input file */
  fclose(file);
  printf("\n%d records read from file 'berlinmod_instants.csv'.\n"
    "%d incomplete records ignored.\n", no_records, no_nulls);
  printf("%d trips read.\n", no_trips);

  /* Open the output file */
  file = fopen("data/berlinmod_trips_new.csv", "w+");
  if (ferror(file))
  {
    printf("Error reading output file");
    goto cleanup;
  }

  printf("Assembled trips written to file 'berlinmod_trips_new.csv'\n");

  /* Write the header line */
  fprintf(file,"tripid,vehid,day,seqno,trip\n");

  /* Loop for each trip */
  for (i = 0; i < no_trips; i++)
  {
    /* Construct a trip and print some information about it */
    Temporal *trip = (Temporal *) tsequence_make(trips[i].trip_instants,
      trips[i].no_instants, true, true, LINEAR, true);
    printf("TripId: %d, Number of input instants: %d, Distance travelled %lf\n",
      trips[i].tripid, trips[i].no_instants, tpoint_length(trip));

    /* Write line in the CSV file */
    char *date_str = date_out(trips[i].day);
    size_t length;
    /* Encode using server machine endian */
    char *trip_str = temporal_as_hexwkb(trip, WKB_EXTENDED, &length);
    fprintf(file,"%d,%d,%s,%d,%s\n", trips[i].tripid, trips[i].vehid, date_str,
      trips[i].seq, trip_str);
    free(trip); free(date_str); free(trip_str);
  }

  /* Close the file */
  fclose(file);

  /* Calculate the elapsed time */
  tm = clock() - tm;
  double time_taken = ((double) tm) / CLOCKS_PER_SEC;
  printf("The program took %f seconds to execute\n", time_taken);

  exit_value = EXIT_SUCCESS;

/* Clean up */
cleanup:

 /* Free memory */
  for (i = 0; i < no_trips; i++)
    for (j = 0; j < trips[i].no_instants; j++)
      free(trips[i].trip_instants[j]);

  /* Finalize MEOS */
  meos_finalize();

  return exit_value;
}
