//! @file moids-to-string.c
//! @author J. Marcel van der Veer

//! @section Copyright
//!
//! This file is part of Algol68G - an Algol 68 compiler-interpreter.
//! Copyright 2001-2026 J. Marcel van der Veer [algol68g@algol68genie.nl].

//! @section License
//!
//! This program is free software; you can redistribute it and/or modify it 
//! under the terms of the GNU General Public License as published by the 
//! Free Software Foundation; either version 3 of the License, or 
//! (at your option) any later version.
//!
//! This program is distributed in the hope that it will be useful, but 
//! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
//! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
//! more details. You should have received a copy of the GNU General Public 
//! License along with this program. If not, see [http://www.gnu.org/licenses/].

//! @section Synopsis
//!
//! Pretty-print a MOID.

#include "a68g.h"
#include "a68g-postulates.h"

// A pretty printer for moids.
// For example "PROC (REF STRUCT (REF SELF, UNION (INT, VOID))) REF SELF"
// for a procedure yielding a pointer to an object of its own mode.

void moid_to_string_2 (char *, MOID_T *, int *, NODE_T *);

//! @brief Add string to MOID text.

void add_to_moid_text (char *dst, char *str, int *w)
{
  a68g_bufcat (dst, str, BUFFER_SIZE);
  (*w) -= strlen (str);
}

//! @brief Find a tag, searching symbol tables towards the root.

TAG_T *find_indicant_global (TABLE_T * table, MOID_T * mode)
{
  if (table != NO_TABLE) {
    for (TAG_T *s = INDICANTS (table); s != NO_TAG; FORWARD (s)) {
      if (MOID (s) == mode) {
        return s;
      }
    }
    return find_indicant_global (PREVIOUS (table), mode);
  } else {
    return NO_TAG;
  }
}

//! @brief Pack to string.

void pack_to_string (char *b, PACK_T * p, int *w, BOOL_T text, NODE_T * idf)
{
  for (; p != NO_PACK; FORWARD (p)) {
    moid_to_string_2 (b, MOID (p), w, idf);
    if (text) {
      if (TEXT (p) != NO_TEXT) {
        add_to_moid_text (b, " ", w);
        add_to_moid_text (b, TEXT (p), w);
      }
    }
    if (p != NO_PACK && NEXT (p) != NO_PACK) {
      add_to_moid_text (b, ", ", w);
    }
  }
}

//! @brief Moid to string 2.

void moid_to_string_2 (char *b, MOID_T * n, int *w, NODE_T * idf)
{
// Oops. Should not happen.
  if (n == NO_MOID) {
    add_to_moid_text (b, "null", w);;
    return;
  }
// Reference to self through REF or PROC.
  if (is_postulated (A68G (postulates), n)) {
    add_to_moid_text (b, "SELF", w);
    return;
  }
// If declared by a mode-declaration, present the indicant.
  if (idf != NO_NODE && !IS (n, STANDARD)) {
    TAG_T *indy = find_indicant_global (TABLE (idf), n);
    if (indy != NO_TAG) {
      add_to_moid_text (b, NSYMBOL (NODE (indy)), w);
      return;
    }
  }
// Write the standard modes.
  if (n == M_HIP) {
    add_to_moid_text (b, "HIP", w);
  } else if (n == M_ERROR) {
    add_to_moid_text (b, "ERROR", w);
  } else if (n == M_UNDEFINED) {
    add_to_moid_text (b, "unresolved mode", w);
  } else if (n == M_C_STRING) {
    add_to_moid_text (b, "C-STRING", w);
  } else if (n == M_COMPLEX || n == M_COMPL) {
    add_to_moid_text (b, "COMPLEX", w);
  } else if (n == M_LONG_COMPLEX || n == M_LONG_COMPL) {
    add_to_moid_text (b, "LONG COMPLEX", w);
  } else if (n == M_LONG_LONG_COMPLEX || n == M_LONG_LONG_COMPL) {
    add_to_moid_text (b, "LONG LONG COMPLEX", w);
  } else if (n == M_STRING) {
    add_to_moid_text (b, "STRING", w);
  } else if (n == M_PIPE) {
    add_to_moid_text (b, "PIPE", w);
  } else if (n == M_SOUND) {
    add_to_moid_text (b, "SOUND", w);
  } else if (n == M_SOUND_DATA) {
    add_to_moid_text (b, "SOUND_DATA", w);
  } else if (n == M_COLLITEM) {
    add_to_moid_text (b, "COLLITEM", w);
  } else if (IS (n, IN_TYPE_MODE)) {
    add_to_moid_text (b, "\"SIMPLIN\"", w);
  } else if (IS (n, OUT_TYPE_MODE)) {
    add_to_moid_text (b, "\"SIMPLOUT\"", w);
  } else if (IS (n, ROWS_SYMBOL)) {
    add_to_moid_text (b, "\"ROWS\"", w);
  } else if (n == M_VACUUM) {
    add_to_moid_text (b, "\"VACUUM\"", w);
  } else if (IS (n, VOID_SYMBOL) || IS (n, STANDARD) || IS (n, INDICANT)) {
    if (DIM (n) > 0) {
      int k = DIM (n);
      if ((*w) >= k * strlen ("LONG ") + strlen (NSYMBOL (NODE (n)))) {
        while (k--) {
          add_to_moid_text (b, "LONG ", w);
        }
        add_to_moid_text (b, NSYMBOL (NODE (n)), w);
      } else {
        add_to_moid_text (b, "..", w);
      }
    } else if (DIM (n) < 0) {
      int k = -DIM (n);
      if ((*w) >= k * strlen ("LONG ") + strlen (NSYMBOL (NODE (n)))) {
        while (k--) {
          add_to_moid_text (b, "LONG ", w);
        }
        add_to_moid_text (b, NSYMBOL (NODE (n)), w);
      } else {
        add_to_moid_text (b, "..", w);
      }
    } else if (DIM (n) == 0) {
      add_to_moid_text (b, NSYMBOL (NODE (n)), w);
    }
// Write compounded modes.
  } else if (IS_REF (n)) {
    if ((*w) >= strlen ("REF ..")) {
      add_to_moid_text (b, "REF ", w);
      moid_to_string_2 (b, SUB (n), w, idf);
    } else {
      add_to_moid_text (b, "REF ..", w);
    }
  } else if (IS_FLEX (n)) {
    if ((*w) >= strlen ("FLEX ..")) {
      add_to_moid_text (b, "FLEX ", w);
      moid_to_string_2 (b, SUB (n), w, idf);
    } else {
      add_to_moid_text (b, "FLEX ..", w);
    }
  } else if (IS_ROW (n)) {
    int j = strlen ("[] ..") + (DIM (n) - 1) * strlen (",");
    if ((*w) >= j) {
      int k = DIM (n) - 1;
      add_to_moid_text (b, "[", w);
      while (k-- > 0) {
        add_to_moid_text (b, ",", w);
      }
      add_to_moid_text (b, "] ", w);
      moid_to_string_2 (b, SUB (n), w, idf);
    } else if (DIM (n) == 1) {
      add_to_moid_text (b, "[] ..", w);
    } else {
      int k = DIM (n);
      add_to_moid_text (b, "[", w);
      while (k--) {
        add_to_moid_text (b, ",", w);
      }
      add_to_moid_text (b, "] ..", w);
    }
  } else if (IS_STRUCT (n)) {
    int j = strlen ("STRUCT ()") + (DIM (n) - 1) * strlen (".., ") + strlen ("..");
    if ((*w) >= j) {
      POSTULATE_T *save = A68G (postulates);
      make_postulate (&A68G (postulates), n, NO_MOID);
      add_to_moid_text (b, "STRUCT (", w);
      pack_to_string (b, PACK (n), w, A68G_TRUE, idf);
      add_to_moid_text (b, ")", w);
      free_postulate_list (A68G (postulates), save);
      A68G (postulates) = save;
    } else {
      int k = DIM (n);
      add_to_moid_text (b, "STRUCT (", w);
      while (k-- > 0) {
        add_to_moid_text (b, ",", w);
      }
      add_to_moid_text (b, ")", w);
    }
  } else if (IS_UNION (n)) {
    int j = strlen ("UNION ()") + (DIM (n) - 1) * strlen (".., ") + strlen ("..");
    if ((*w) >= j) {
      POSTULATE_T *save = A68G (postulates);
      make_postulate (&A68G (postulates), n, NO_MOID);
      add_to_moid_text (b, "UNION (", w);
      pack_to_string (b, PACK (n), w, A68G_FALSE, idf);
      add_to_moid_text (b, ")", w);
      free_postulate_list (A68G (postulates), save);
      A68G (postulates) = save;
    } else {
      int k = DIM (n);
      add_to_moid_text (b, "UNION (", w);
      while (k-- > 0) {
        add_to_moid_text (b, ",", w);
      }
      add_to_moid_text (b, ")", w);
    }
  } else if (IS (n, PROC_SYMBOL) && DIM (n) == 0) {
    if ((*w) >= strlen ("PROC ..")) {
      add_to_moid_text (b, "PROC ", w);
      moid_to_string_2 (b, SUB (n), w, idf);
    } else {
      add_to_moid_text (b, "PROC ..", w);
    }
  } else if (IS (n, PROC_SYMBOL) && DIM (n) > 0) {
    int j = strlen ("PROC () ..") + (DIM (n) - 1) * strlen (".., ") + strlen ("..");
    if ((*w) >= j) {
      POSTULATE_T *save = A68G (postulates);
      make_postulate (&A68G (postulates), n, NO_MOID);
      add_to_moid_text (b, "PROC (", w);
      pack_to_string (b, PACK (n), w, A68G_FALSE, idf);
      add_to_moid_text (b, ") ", w);
      moid_to_string_2 (b, SUB (n), w, idf);
      free_postulate_list (A68G (postulates), save);
      A68G (postulates) = save;
    } else {
      int k = DIM (n);
      add_to_moid_text (b, "PROC (", w);
      while (k-- > 0) {
        add_to_moid_text (b, ",", w);
      }
      add_to_moid_text (b, ") ..", w);
    }
  } else if (IS (n, SERIES_MODE) || IS (n, STOWED_MODE)) {
    int j = strlen ("()") + (DIM (n) - 1) * strlen (".., ") + strlen ("..");
    if ((*w) >= j) {
      add_to_moid_text (b, "(", w);
      pack_to_string (b, PACK (n), w, A68G_FALSE, idf);
      add_to_moid_text (b, ")", w);
    } else {
      int k = DIM (n);
      add_to_moid_text (b, "(", w);
      while (k-- > 0) {
        add_to_moid_text (b, ",", w);
      }
      add_to_moid_text (b, ")", w);
    }
  } else {
    char str[SMALL_BUFFER_SIZE];
    ASSERT (a68g_bufprt (str, (size_t) SMALL_BUFFER_SIZE, "\\%d", ATTRIBUTE (n)) >= 0);
    add_to_moid_text (b, str, w);
  }
}

//! @brief Pretty-formatted mode "n"; "w" is a measure of width.

char *moid_to_string (MOID_T * n, int w, NODE_T * idf)
{
#define MAX_MTS 8
// We use a static buffer of MAX_MTS strings. This value 8 should be safe.
// No more than MAX_MTS calls can be pending in for instance printf.
// Instead we could allocate each string on the heap but that leaks memory. 
  static int mts_buff_ptr = 0;
  static char mts_buff[8][BUFFER_SIZE];
  char *a = &(mts_buff[mts_buff_ptr][0]);
  mts_buff_ptr++;
  if (mts_buff_ptr >= MAX_MTS) {
    mts_buff_ptr = 0;
  }
  a[0] = NULL_CHAR;
  if (w >= BUFFER_SIZE) {
    w = BUFFER_SIZE - 1;
  }
  A68G (postulates) = NO_POSTULATE;
  if (n != NO_MOID) {
    moid_to_string_2 (a, n, &w, idf);
  } else {
    a68g_bufcat (a, "null", BUFFER_SIZE);
  }
  return a;
#undef MAX_MTS
}
