#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "reading_io.h"
#include "significance.h"

static void print_usage(const char *prog) {
  fprintf(stderr,
    "Usage: %s [reading.json]\n\n"
    "Reads a DailyReading in deck-engine's --format json shape - from the\n"
    "given file, or from stdin if no file is given (or it is \"-\") - and\n"
    "prints a significance report: the day's overall level and the top\n"
    "aspects driving it.\n\n"
    "Typical use:\n"
    "  dist/deck-engine ... --format json | %s\n"
    "  dist/deck-engine ... --format json > reading.json && %s reading.json\n",
    prog, prog, prog);
}

static char *read_all(FILE *f, size_t *out_length) {
  size_t cap = 4096;
  size_t len = 0;
  char *buf = malloc(cap);

  size_t n;
  while ((n = fread(buf + len, 1, cap - len, f)) > 0) {
    len += n;
    if (len == cap) {
      cap *= 2;
      buf = realloc(buf, cap);
    }
  }
  buf = realloc(buf, len + 1);
  buf[len] = '\0';
  *out_length = len;
  return buf;
}

/* Display names for the report - deliberately a small local table rather
 * than linking engine/src/astro.c's astro_body_name()/astro_aspect_name()
 * (which would pull in the vendored Astronomy Engine just for cosmetic
 * text). Same policy as reading_io.c's slug tables. */
static const char *body_display_name(Body body) {
  static const char *const names[NUM_BODIES] = {
    "Sun", "Moon", "Mercury", "Venus", "Mars",
    "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto",
  };
  return names[body];
}

static const char *aspect_display_name(AspectType type) {
  static const char *const names[5] = {
    "Conjunction", "Sextile", "Square", "Trine", "Opposition",
  };
  return names[type];
}

static const char *day_level_name(DaySignificance level) {
  switch (level) {
    case DAY_QUIET: return "Quiet";
    case DAY_NOTABLE: return "Notable";
    case DAY_SIGNIFICANT: return "Significant";
    case DAY_MAJOR: return "Major";
  }
  return "Unknown";
}

int main(int argc, char **argv) {
  if (argc > 2 || (argc == 2 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0))) {
    print_usage(argv[0]);
    return argc > 2 ? 1 : 0;
  }

  FILE *in = stdin;
  bool opened_file = false;
  if (argc == 2 && strcmp(argv[1], "-") != 0) {
    in = fopen(argv[1], "r");
    if (!in) {
      fprintf(stderr, "error: could not open %s\n", argv[1]);
      return 1;
    }
    opened_file = true;
  }

  size_t length;
  char *text = read_all(in, &length);
  if (opened_file) fclose(in);

  DailyReading reading;
  bool loaded = reading_load_json(text, length, &reading);
  free(text);
  if (!loaded) {
    fprintf(stderr, "error: could not parse input as deck-engine --format json output\n");
    return 1;
  }

  DailyInterpretation interp;
  interpret_daily_reading(&reading, &interp);

  printf("Day significance: %s\n", day_level_name(interp.day_level));
  if (interpretation_deserves_framing(&interp)) {
    printf("(a major transit today - worth a deeper Celtic Cross look)\n");
  }
  printf("\n");

  if (interp.top_item_count == 0) {
    printf("No notable transits today.\n");
    return 0;
  }

  printf("Top %d significant transit%s:\n", interp.top_item_count,
         interp.top_item_count == 1 ? "" : "s");
  for (int i = 0; i < interp.top_item_count; i++) {
    const SignificantItem *item = &interp.top_items[i];
    printf("  %d. Transiting %s %s natal %s (orb %.1f\xc2\xb0, score %.2f)\n", i + 1,
           body_display_name(item->aspect.transiting_planet),
           aspect_display_name(item->aspect.type),
           body_display_name(item->aspect.natal_planet), item->aspect.orb, item->score);
  }
  return 0;
}
