/* pk-info.pk - Tool introspection-related commands.  */

/* Copyright (C) 2022, 2023, 2024 Jose E. Marchesi */

/* 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/>.
 */

/* Print information about the given Pk_Type.  This function is used
   to implement the `.info type NAME' command.  */

fun pk_info_type = (Pk_Type typ) void:
{
  fun add_common = (Pk_Table table, string class) void:
  {
    table.row;
    table.column ("Class:", "table-header");
    table.column (class);
    if (typ.name != "")
    {
      table.row;
      table.column ("Name:", "table-header");
      table.column (typ.name, "string");
    }
    table.row;
    table.column ("Complete:", "table-header");
    table.column (typ.complete_p ? "yes" : "no", "setting-boolean");
  }

  var table = Pk_Table { num_columns = 2, max_column_size = 80 };

  if (typ.code == PK_TYPE_INTEGRAL)
  {
    add_common (table, "integral");
    table.row;
    table.column ("Signed:", "table-header");
    table.column (typ.signed_p ? "yes" : "no", "setting-boolean");
    table.row;
    table.column ("Size:", "table-header");
    table.column (format ("%u64d#b", typ.size), "offset");
  }
  else if (typ.code == PK_TYPE_OFFSET)
  {
    add_common (table, "offset");
    table.row;
    table.column ("Base type signed:", "table-header");
    table.column (typ.signed_p ? "yes" : "no", "setting-boolean");
    table.row;
    table.column ("Base type size:", "table-header");
    table.column (format ("%u64d#b", typ.size), "offset");
    table.row;
    table.column ("Unit:", "table-header");
    table.column (format ("%u64d#b", typ._unit), "offset");
  }
  else if (typ.code == PK_TYPE_ARRAY)
  {
    add_common (table, "array");
    table.row;
    table.column ("Bounded:", "table-header");
    table.column (typ.bounded_p ? "yes" : "no", "setting-boolean");
  }
  else if (typ.code == PK_TYPE_STRUCT)
  {
    add_common (table,
                (typ.pinned_p ? "pinned " : "")
                + (typ.integral_p ? "integral " : "")
                + (typ.union_p ? "union" : "struct"));

    if (typ.integral_p)
    {
      table.row;
      table.column ("Signed:", "table-header");
      table.column (typ.signed_p ? "yes" : "no", "setting-boolean");
      table.row;
      table.column ("Integral size:", "table-header");
      table.column (format ("%u64d#b", typ.size), "offset");
    }
  }
  else if (typ.code == PK_TYPE_STRING)
    add_common (table, "string");
  else if (typ.code == PK_TYPE_FUNCTION)
    add_common (table, "function");
  else if (typ.code == PK_TYPE_ANY)
    add_common (table, "any");
  else
    add_common (table, "unknown");

  table.print_table;

  /* Some kind of types need to print additional info.  */
  if (typ.code == PK_TYPE_STRUCT)
  {
    var computed_field_names = string[]();

    if (typ.nfields > 0)
      {
        term_begin_class ("table-header");
        print "Fields:\n";
        term_end_class ("table-header");
        var table = Pk_Table { num_columns = 2, indent = 2, max_column_size = 80 };

        table.row ("table-header");
        table.column ("Name");
        table.column ("Type");
        for (var i = 0; i < typ.nfields; ++i)
          {
            if (typ.fcomputed[i] || typ.fnames[i] == "")
              continue;
            table.row;
            table.column (typ.fnames[i]);
            table.column (typ.ftypes[i]);
          }
          table.print_table;

        var ncomputedfields = lambda uint<32>:
          {
            var n = 0U;
            for (var i = 0; i < typ.nfields; ++i)
              if (typ.fcomputed[i])
                ++n;
            return n;
          }();

        if (ncomputedfields > 0)
          {
            term_begin_class ("table-header");
            print "Computed Fields:\n";
            term_end_class ("table-header");
            table = Pk_Table { num_columns = 2, indent = 2, max_column_size = 80 };

            table.row ("table-header");
            table.column ("Name");
            table.column ("Type");
            for (var i = 0; i < typ.nfields; ++i)
              {
                if (!typ.fcomputed[i])
                  continue;

                table.row;
                table.column (typ.fnames[i]);
                table.column (typ.ftypes[i]);
                apush (computed_field_names, typ.fnames[i]);
              }
            table.print_table;
          }
    }

    if (typ.nmethods > 0)
      {
        var table = Pk_Table { num_columns = 2, indent = 2, max_column_size = 80 };
        var nmethods_to_print = 0;

        table.row ("table-header");
        table.column ("Name");
        table.column ("Type");
        for (var i = 0; i < typ.nmethods; ++i)
          {
            /* Do not include getters and setters of computed fields.  */
            var mname = typ.mnames[i];
            if (mname'length > 4
                && (mname[0:4] == "get_" || mname[0:4] == "set_")
                && mname[4:] in computed_field_names)
              continue;

            /* Ditto for pretty-printers.  */
            if (mname == "_print"
                || (mname'length > 6 && mname[0:7] == "_print_"))
              continue;

            table.row;
            table.column (typ.mnames[i]);
            table.column (typ.mtypes[i]);
            nmethods_to_print++;
          }

        if (nmethods_to_print > 0)
          {
            term_begin_class ("table-header");
            print "Methods:\n";
            term_end_class ("table-header");
            table.print_table;
          }
      }
  }
}
