

#include "SDDS.h"
#include "mdb.h"
#include "scan.h"

#define CLO_SR_DOWNTIME_FILE 0
#define CLO_STATION_SCHEDULE_FILE 1
#define CLO_APS_SCHEDULE_FILE 2
#define OPTIONS 3

char *option[OPTIONS] = {
  "srDownTimeFile",
  "stationScheduleFile",
  "apsScheduleFile",
};

long debug = 0;

static char *USAGE = "stationDownTime [outputfile]  \n\
 -srDownTimeFile=<filename> -stationScheduleFile=<filename>  \n\
outputfile              SDDS output file for writing the data. \n\
srDownTimeFile          file contains SR down time data \n\
stationScheduleFile     beamline station scheduleFile. \n\
apsScheduleFile          APS schedule file.\n\
Calculates PSS station SR downtime. Programed by H. Shang ("__DATE__").\n";

double computeDownTime(double *time, long startIndex, long endIndex, short *down, double start, double end, long rows);

int main(int argc, char **argv) {
  SCANNED_ARG *s_arg;
  SDDS_DATASET out_data, sr_data, schedule_data, sr_schedule;
  long i_arg, i, j, rows, srRows;
  double *srStartDownTime, *srEndDownTime, *startTime, *endTime, globalSRDownTime, globalAvailableTime;
  double totalSRDownTime, totalAvailableTime, totalDeliveredTime, totalScheduleTime;
  char *outputFile = NULL, *srFile = NULL, *scheduleFile = NULL, *apsScheduleFile = NULL;
  double *apsStart = NULL, *apsEnd = NULL, apsRows = 0, maintenance, downtime, scheduletime, start, end;
  long startIndex, endIndex;

  rows = srRows = 0;
  srStartDownTime = srEndDownTime = NULL;
  startTime = endTime = NULL;

  argc = scanargs(&s_arg, argc, argv);
  if (argc < 2)
    bomb(NULL, USAGE);

  for (i_arg = 1; i_arg < argc; i_arg++) {
    if (s_arg[i_arg].arg_type == OPTION) {
      switch (match_string(s_arg[i_arg].list[0], option, OPTIONS, 0)) {
      case CLO_SR_DOWNTIME_FILE:
        srFile = s_arg[i_arg].list[1];
        break;
      case CLO_STATION_SCHEDULE_FILE:
        scheduleFile = s_arg[i_arg].list[1];
        break;
      case CLO_APS_SCHEDULE_FILE:
        apsScheduleFile = s_arg[i_arg].list[1];
        break;
      default:
        fprintf(stderr, "unknown option - %s given.\n", s_arg[i_arg].list[0]);
        break;
      }
    } else {
      if (!outputFile)
        outputFile = s_arg[i_arg].list[0];
      else
        SDDS_Bomb("too many filenames given");
    }
  }

  if (!scheduleFile || !srFile)
    SDDS_Bomb("Schedule file or sr downtime file  not provided.");

  if (!outputFile)
    SDDS_Bomb("output file has to be provided.");

  if (!SDDS_InitializeInput(&sr_data, srFile))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  if (SDDS_ReadPage(&sr_data) <= 0)
    SDDS_Bomb("Error in reading sr downtime file.");
  srRows = SDDS_CountRowsOfInterest(&sr_data);
  if (!(srStartDownTime = (double *)SDDS_GetColumn(&sr_data, "StartTime")) ||
      !(srEndDownTime = (double *)SDDS_GetColumn(&sr_data, "EndTime")) ||
      !SDDS_GetParameterAsDouble(&sr_data, "TotalDownTime", &globalSRDownTime) ||
      !SDDS_GetParameterAsDouble(&sr_data, "TotalUserTime", &globalAvailableTime))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  if (!SDDS_Terminate(&sr_data))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);

  if (!SDDS_InitializeInput(&sr_schedule, apsScheduleFile))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  if (SDDS_ReadPage(&sr_schedule) <= 0)
    SDDS_Bomb("Error in reading APS schedule file.");
  apsRows = SDDS_CountRowsOfInterest(&sr_schedule);
  if (!(apsStart = (double *)SDDS_GetColumn(&sr_schedule, "StartTime")) ||
      !(apsEnd = (double *)SDDS_GetColumn(&sr_schedule, "EndTime")) ||
      !SDDS_Terminate(&sr_schedule))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);

  if (!SDDS_InitializeInput(&schedule_data, scheduleFile))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  if (!SDDS_InitializeCopy(&out_data, &schedule_data, outputFile, "w") ||
      !SDDS_DefineSimpleColumn(&out_data, "SRDownTime", "seconds", SDDS_DOUBLE) ||
      !SDDS_DefineSimpleColumn(&out_data, "MaintenanceTime", "seconds", SDDS_DOUBLE) ||
      !SDDS_DefineSimpleColumn(&out_data, "UserDownTime", "seconds", SDDS_DOUBLE) ||
      !SDDS_DefineSimpleColumn(&out_data, "PSSDownTime", "seconds", SDDS_DOUBLE) ||
      !SDDS_DefineSimpleColumn(&out_data, "ActiveBeamTime", "seconds", SDDS_DOUBLE) ||
      !SDDS_DefineSimpleParameter(&out_data, "TotalAvailableTime", "hours", SDDS_DOUBLE) ||
      !SDDS_DefineSimpleParameter(&out_data, "TotalDeliveredTime", "hours", SDDS_DOUBLE) ||
      !SDDS_DefineSimpleParameter(&out_data, "TotalSRDownTime", "hours", SDDS_DOUBLE) ||
      !SDDS_DefineSimpleParameter(&out_data, "GlobalSRDownTime", "hours", SDDS_DOUBLE) ||
      !SDDS_WriteLayout(&out_data))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  while (SDDS_ReadPage(&schedule_data) > 0) {
    if (!SDDS_CopyPage(&out_data, &schedule_data))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
    rows = SDDS_CountRowsOfInterest(&schedule_data);
    totalScheduleTime = totalSRDownTime = totalAvailableTime = totalDeliveredTime = 0;
    /* fprintf(stdout, "rows=%d\n", rows); */
    if (rows > 0) {
      if (!(startTime = (double *)SDDS_GetColumn(&schedule_data, "StartTime")) ||
          !(endTime = (double *)SDDS_GetColumn(&schedule_data, "EndTime")))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
      for (i = 0; i < rows; i++) {
        /*compute maintenance time */
        maintenance = downtime = scheduletime = 0;
        startIndex = endIndex = -1;
        for (j = 0; j < apsRows; j++) {
          if (startTime[i] > apsEnd[j])
            continue;
          if (endTime[i] <= apsStart[j])
            break;
          start = MAX(startTime[i], apsStart[j]);
          end = MIN(endTime[i], apsEnd[j]);
          scheduletime += end - start;
        }
        maintenance = endTime[i] - startTime[i] - scheduletime;
        /*compute beamline sr downtime */
        for (j = 0; j < srRows; j++) {
          if (startTime[i] > srEndDownTime[j])
            continue;
          if (endTime[i] <= srStartDownTime[j])
            break;
          start = MAX(startTime[i], srStartDownTime[j]);
          end = MIN(endTime[i], srEndDownTime[j]);
          downtime += end - start;
          /*  fprintf(stdout, "row=%d, j=%d, start=%f, end=%f, diff=%f, downtime=%f\n",i, j, start, end, (end-start)/3600, downtime/3600); */
        }
        if (maintenance > 0 && downtime > maintenance)
          downtime -= maintenance;
        totalScheduleTime += scheduletime;
        totalSRDownTime += downtime;
        if (!SDDS_SetRowValues(&out_data, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, i,
                               "SRDownTime", downtime, "MaintenanceTime", maintenance, NULL))
          SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
      }
    }
    totalDeliveredTime = (totalScheduleTime - totalSRDownTime) / 3600;

    if (!SDDS_SetParameters(&out_data, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE,
                            "GlobalSRDownTime", globalSRDownTime,
                            "TotalSRDownTime", totalSRDownTime / 3600,
                            "TotalAvailableTime", totalScheduleTime / 3600,
                            "TotalDeliveredTime", totalDeliveredTime,
                            NULL))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
    if (!SDDS_WritePage(&out_data))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);

    free(startTime);
    free(endTime);
    startTime = endTime = NULL;
  }
  free(srStartDownTime);
  free(srEndDownTime);
  free(apsStart);
  free(apsEnd);
  if (!SDDS_Terminate(&out_data) || !SDDS_Terminate(&schedule_data))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  free_scanargs(&s_arg, argc);
  return 0;
}
