// SPDX-FileCopyrightText: (C) The MADness project
// SPDX-License-Identifier: MIT-0
#include <stdint.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#include <ctss.h>

char ErrFmt[256];

void ProcessFmt(FmtVar *fmtvar, char *cfmt, char *madfmt, int read, int first)
{
   char *bp, *ep;
   int genccode;
   char ch;
   char pfmt[256];
   char lfmt[256];

   *cfmt = 0;
   genccode = first;
   if (first) {
      strcpy(ErrFmt, madfmt);
   }

   bp = madfmt;
   /* Substitute all FORMAT VARIABLES before processing */
   if ((ep = strchr(bp, '\'')) != NULL) {
      char *pf;
      int j;

      pfmt[0] = 0;
      pf = pfmt;
      while (ep) {
         strncpy(pf, bp, ep-bp);
         pf += (ep-bp);
         *pf = 0;
         bp = ep + 1;
         if ((ep = strchr(bp, '\'')) != NULL) {
            strncpy(lfmt, bp, ep-bp);
            lfmt[ep-bp] = 0;
            for (j = 0; fmtvar[j].loc != NULL; j++) {
               if (!strcmp(fmtvar[j].var, lfmt)) {
                  int count;
                  char num[10];
                  count = *fmtvar[j].loc;
                  sprintf(num, "%d", count);
                  strcpy(pf, num);
                  pf += strlen(num);
                  break;
               }
            }
            if (fmtvar[j].loc == NULL) {
               fprintf(stderr, "Undeclared FORMAT VARIABLE: %s\n", lfmt);
               exit(1);
            }
            bp = ep + 1;
            ep = strchr(bp, '\'');
         } else {
            fprintf(stderr, "Unterminated FORMAT VARIABLE: %-7.7s\n", bp-1);
            exit(1);
         }
      }
      strcpy(pf, bp);
      madfmt = pfmt;
   }
   /* Process FORMAT */
   while (*madfmt && *madfmt != '*') {
      int prefix;
      int count;

      ch = *madfmt++;
      if (ch == '/') {
         strcat(cfmt, "\n");
         genccode = TRUE;
         continue;
      }
      else if (isspace(ch) || ch == ',') {
         continue;
      }
      else if (ch == 'S') {
         count = 0;
         if (isdigit(*madfmt)) {
            while (*madfmt && isdigit(*madfmt)) {
               count = (count * 10) + (*madfmt++ - '0');
            }
         }
         while (count) {
            strcat (cfmt, " ");
            count--;
         }
         continue;
      }
      if (isdigit(ch)) {
         prefix = 0;
         madfmt--;
         while (*madfmt && isdigit(*madfmt)) {
            prefix = (prefix * 10) + (*madfmt++ - '0');
         }
         ch = *madfmt++;
      } else {
         prefix = 1;
      }
      if (ch == '(') {
         bp = madfmt;
         if ((ep = strchr(madfmt, ')')) != NULL) {
            strncpy(lfmt, bp, ep-bp);
            lfmt[ep-bp] = 0;
            for (; prefix; prefix--) {
               ProcessFmt(fmtvar, &cfmt[strlen(cfmt)], lfmt, read, FALSE);
            }
            madfmt = ep + 1;
         }
      }
      else if (ch == 'K' || ch == 'I') {
         count = 0;
         while (*madfmt && isdigit(*madfmt)) {
            count = (count * 10) + (*madfmt++ - '0');
         }
         if (read) {
            strcpy(lfmt, (ch == 'K' ? "%llo":"%ld"));
         } else {
            sprintf(lfmt, "%%%dll%s", count, (ch == 'K' ? "o":"d"));
         }
         if (prefix > 1) strcat (lfmt, " ");
         for (; prefix; prefix--) {
            strcat(&cfmt[strlen(cfmt)], lfmt);
         }
      }
      else if (ch == 'C') {
         count = 0;
         while (*madfmt && isdigit(*madfmt)) {
            count = (count * 10) + (*madfmt++ - '0');
         }
         sprintf(lfmt, "%%%ds", prefix * count);
         strcat(&cfmt[strlen(cfmt)], lfmt);
      }
      else if (ch == 'F' || ch == 'E') {
         int precision;
         count = 0;
         while (*madfmt && isdigit(*madfmt)) {
            count = (count * 10) + (*madfmt++ - '0');
         }
         precision = 0;
         if (*madfmt == '.') {
            madfmt++;
            while (*madfmt && isdigit(*madfmt)) {
               precision = (precision * 10) + (*madfmt++ - '0');
            }
         }
         if (read) {
            strcpy(lfmt, (ch == 'F' ? "%lf":"%le"));
         } else {
            sprintf(lfmt, "%%%d.%d%s", count, precision, (ch == 'F' ? "f":"e"));
         }
         if (prefix > 1) strcat (lfmt, " ");
         for (; prefix; prefix--) {
            strcat(&cfmt[strlen(cfmt)], lfmt);
         }
      }
      else if (ch == 'H') {
         if (genccode) {
            genccode = FALSE;
            prefix--;
            switch (*madfmt++) {
            case ' ':
               break;
            case '+':
               strcat(cfmt, "\r");
               break;
            case '0':
               strcat(cfmt, "\n");
               break;
            case '1':
               strcat(cfmt, "\f");
               break;
            case '2':
               strcat(cfmt, "\v\v");
               break;
            case '3':
            case '4':
               break;
            default:
               prefix++;
               madfmt--;
            }
         }
         for (; prefix && *madfmt; prefix--) {
            lfmt[0] = *madfmt++;
            lfmt[1] = 0;
            strcat(&cfmt[strlen(cfmt)], lfmt);
         }
      } else {
         fprintf(stderr, "Unsupported FORMAT specifier: %c\n", ch);
         exit(1);
      }
   }
   if (first) {
      if (!*madfmt) {
         fprintf(stderr, "Unterminated FORMAT: %s\n", ErrFmt);
         exit(1);
      }
      if (!read) {
         strcat(cfmt, "\n");
      }
   }
}
