### ----------------------------------------------------
### -- Gnome::Gtk3
### -- Licenses: Artistic-2.0
### -- Authors: Marcel Timmerman
### -- File: xbin/gnome-gtk3-source-skim-tool.raku
### ----------------------------------------------------
### -- Chunk 3 of 5
-max-text ~= "=item Maximum value is $max.\n" if ?$max;

    my Str $default-text = '';
    $default-text ~= "=item Default value is $prop-default.\n" if ?$prop-default;

    $property-doc ~= Q:qq:to/EOHEADER/;

      =comment -----------------------------------------------------------------------
      =comment #TP:0:$prop-name:
      =head2 $prop-name
      $prop-blurp

      $type-string$flags-text$min-max-text$default-text
      EOHEADER

    $property-doc-entries{$prop-name} = $property-doc;
#note "end prop";
  }
#note "end of all props";
  print "\n";

  if ?$property-doc-entries {
    note "add property information to $output-file";
    $output-file.IO.spurt( Q:to/EODOC/, :append);

      #-------------------------------------------------------------------------------
      =begin pod
      =head1 Properties
      EODOC

    for $property-doc-entries.keys.sort -> $property {
      note "save property $property";
      $output-file.IO.spurt( $property-doc-entries{$property}, :append);
    }

    $output-file.IO.spurt( "=end pod\n", :append);
  }
}

#`{{
#-------------------------------------------------------------------------------
sub get-properties ( Str:D $source-content is copy ) {

  return unless $source-content;

  my Str $property-doc;
  my Str $property-name;

  print "Find properties ";
  my Hash $property-doc-entries = %();
  loop {
#print "\n";
    print ".";
    $*OUT.flush;

    $property-doc = '';
    $property-name = '';

#`{{ Needed to find array-name?
    # Get the array name if g_object_class_install_properties() is used
    $source-content ~~ m/ 'g_object_class_install_propert' [ y || ies ]
                          \s* '(' \s* <alnum>+ \s* ',' \s* <alnum>+ \s* ','
                          \s* $<array-name> = (<alnum>+) \s* ')'
                        /;
    my Str $array-name = ($/<array-name> // '').Str;
}}

#`{{
    $source-content ~~ m/
      $<property-doc> = [
          [ '/**'                           # start c-comment block
            \s+ '*' \s+ [<alnum>||'-']+     # first line has Gtk class name
           ':' [<alnum>||'-']+ ':'          # and a :property name:
            [^^ \s+ '*' <!before '/'> .*? $$ ]*    # anything else but '*/'
            \s* '*/'                        # till the end of c-comment
          ]?                                # sometimes there's no comment block
          \s+ [ 'g_object_interface_install_property' .*? ||
                                            # sometimes a call for interfaces
            'props[' <-[\]]>+ ']' \s* '=' \s*
                                            # sometimes there's an array def
          ]                             # anything else
        'g_param_spec_'                     # till prop spec starts
        .*? ');'                            # till the spec ends
      ]
    /;
}}

#`{{
    # Try to find a c-doc first followed by a g_param_spec_*()
    $source-content ~~ m/
      $<property-doc> = [
        '/**'                               # start c-comment block
        \s+ '*' \s+ [ <alnum> || '-' ]+     # first line has Gtk class name
        ':' [ <alnum> || '-' ]+ ':'         # and a :property name:
        .*? '*/'                            # till the end of c-comment
      ]
      \s* <alnum>+ \s* '=' \s*              # assign to variable
      $<property-spec> = [
        'g_param_spec_'                     # prop spec starts
        .*? ');'                            # till the spec ends
      ]
    /;

    my Str $search-prop-doc = ~($<property-doc> // '');
}}


#`{{
    # Try next search if no doc
    if !$search-prop-doc { .... }
    $source-content ~~ m/
      $<property-doc> = [
        [ '/**'                           # start c-comment block
          \s+ '*' \s+ [<alnum>||'-']+     # first line has Gtk class name
         ':' [<alnum>||'-']+ ':'          # and a :property name:
          .*? '*/'                        # till the end of c-comment
        ]?                                # sometimes there's no comment block

        \s+ [ 'g_object_interface_install_property' .*? ||
                                          # sometimes a call for interfaces
         'g_object_class_install_property' .*? ||
                                          # sometimes a call for classes
         'settings_install_property_parser' .*? ||
                                          # settings does it differently
          <alnum>*? $array-name '[' <-[\]]>+ ']' \s* '=' \s*
                                          # sometimes there's an array def
        ]                                 # anything else
        'g_param_spec_'                   # till prop spec starts
        .*? ');'                          # till the spec ends
      ]
    /;
}}

    my Str ( $search-prop-doc, $search-prop-spec) =
      try-prop-search1($source-content);

#    # Try next search if no doc
#    unless (?$search-prop-doc and ?$search-prop-spec) {
#      ( $search-prop-doc, $search-prop-spec) =
#        try-prop-search2($source-content);
#    }


#note "pdoc 1: prop = $search-prop-doc\nspec = $search-prop-spec";

    last unless ?$search-prop-doc or ?$search-prop-spec;

    # Skip deprecated properties
    next if $search-prop-doc ~~ m/ '*' \s+ 'Deprecated:' / or
            $search-prop-spec ~~ m/'G_PARAM_DEPRECATED'/;


#    my Bool $has-doc = ($search-prop-doc ~~ m/ '/**' / ?? True !! False);
    my Str ( $prop-name, $prop-blurp);
    my Str $prop-args-string;
#`{{
    if ?$search-prop-doc {
      $search-prop-doc ~~ m/
        ^^ \s+ '*' \s+ $*lib-class-name ':'
        $<prop-name> = [ <-[:]> [<alnum> || '-']+ ]
      /;
      $property-name = ~($<prop-name> // '');

      # Remove classname and property name
      $search-prop-doc ~~
        s/ \s+ '*' \s+ $*lib-class-name ':' $<prop-name> ':' \n //;

      $search-prop-doc = primary-doc-changes($search-prop-doc);
      $search-prop-doc = cleanup-source-doc($search-prop-doc);
    }

    else {
      $search-prop-doc = '';
    }

#    $prop-blurp ~~ s/ \s* \* \s+ Since ':' \s+ \d+ \. \d+ \s*//;
#note "blurp: $prop-blurp";
}}
  ( $prop-name, $prop-blurp) = get-prop-doc($search-prop-doc);

  my Array $flags;
  my Str $gtype-string;
  my $prop-default;
  my Str $prop-g-type = '';
  my Str ( $min, $max);
  my Array $args;
    ( $flags, $gtype-string, $prop-g-type, $prop-default, $min, $max, $args
    ) = process-prop-args($search-prop-spec);   # if ?$search-prop-spec;

  # Arguments have also documentation. See if it is larger and if so,
  # Take the larger part.

#note 'A2: ', @$args[2];
  $prop-blurp = (($prop-blurp//'').chars > (@$args[2]//'').chars)
                ?? $prop-blurp !! @$args[2]//'';

#`{{
    my Str $prop-spec-type = '';
    my Str $prop-g-type = '';
    if ?$search-prop-spec {
      $search-prop-spec ~~
        m/ \s+ 'g_param_spec_' $<prop-type> = [ <alnum>+ ] \s* '(' /;
      $prop-spec-type = ~($<prop-type> // '' );
      $prop-g-type = 'G_TYPE_' ~ $prop-spec-type.uc;
note "property: $property-name, types: $prop-spec-type, $prop-g-type";
      $prop-args-string = $search-prop-spec;
    }
}}

#`{{
    my Str $prop-args-string = $search-prop-doc;
    my Str ( $prop-name, $prop-blurp);

    if ?$search-prop-doc {
#      $prop-name = $property-name;
      $search-prop-doc = primary-doc-changes($search-prop-doc);
      $search-prop-doc = cleanup-source-doc($search-prop-doc);
    }

    else {
      $search-prop-doc = '';
    }
}}
#`{{
    $prop-args ~~ s/ .*? 'g_param_spec_' $prop-spec-type \s* '(' //;
    $prop-args ~~ s/ '));' //;
#note 'prop-args 0: ', $prop-args;

    # process arguments. first rename commas in strings into _COMMA_
    my Bool $in-string = False;
    my Str $temp-prop-args = $prop-args;
    $prop-args = '';
    for $temp-prop-args.split('')[1..*-2] -> $c {
#note "C: '$c'";
      if $c eq '"' {
        $in-string = !$in-string;
        $prop-args ~= '"';
        next;
      }

      if $in-string and $c eq ',' {
        $prop-args ~= '_COMMA_';
      }

      else {
        $prop-args ~= $c;
      }
    }
note 'prop-args 1: ', $prop-args;

    # Now we can split on the real commas and substitute the comma back in str
    my @args = ();
    for $prop-args.split(/ \s* ',' \s* /) -> $arg is copy {
      $arg ~~ s/ '_COMMA_' /,/;
      $arg ~~ s/ 'P_(' //;
      $arg ~~ s/ ')' //;
      $arg ~~ s:g/ \" \s+ \" //;
      $arg ~~ s:g/ \" //;
      @args.push($arg);
    }
note "prop args 2: ", @args.join(', ');

    $prop-name = @args[0];
    $prop-blurp = ?$search-prop-doc ?? $search-prop-doc !! @args[2];
#    $prop-blurp ~~ s/ \s* \* \s+ Since ':' \s+ \d+ \. \d+ \s*//;
#note "blurp: $prop-blurp";

    my Str $flags;
    my Str $gtype-string;
    my $prop-default;
    my Str $prop-doc = '';
    given $prop-spec-type {

      when 'boolean' {
        $prop-default = @args[3] ~~ 'TRUE' ?? True !! False;
        $flags = @args[4];

        $prop-doc = Q:qq:to/EOP/;
          $prop-blurp

          Default value: $prop-default
          EOP
      }

      when 'string' {
        $prop-default = @args[3] ~~ 'NULL' ?? 'Any' !! @args[3];
        $flags = @args[4];

        $prop-doc = Q:qq:to/EOP/;
          $prop-blurp

          Default value: $prop-default
          EOP
      }

      when 'enum' {
        $gtype-string = @args[3];
        $prop-default = @args[4] ~~ 'TRUE' ?? True !! False;
        $flags = @args[5];

        $prop-doc = Q:qq:to/EOP/;
          $prop-blurp

          Default value: $prop-default
          EOP
      }

      when 'object' {
        $gtype-string = @args[3];
        $flags = @args[4];

        $prop-doc = Q:qq:to/EOP/;
          $prop-blurp

          Widget type: $gtype-string
          EOP
      }

      when '' {
      }

    }
}}
#note "pdoc 2: $prop-blurp";

#    if ?$search-prop-doc {
#      $search-prop-doc ~= "Widget type: $gtype-string\n" if ?$gtype-string;
#      $search-prop-doc ~= "Flags: $flags\n" if ?$flags;
#    }

#    else {
#      $search-prop-doc = $prop-doc;
#      $search-prop-doc = cleanup-source-doc(
#        primary-doc-changes($search-prop-doc)
#      );
#    }

#    $search-prop-doc ~~ s:g/\n\n/\n/;


#note "pdoc 3: ", $prop-blurp;

#    $search-prop-doc ~~ s/ \* \s* Since ':' \s* \d+\.\d+ \s* \*? //;

    my Str $flags-text = '';
    for @$flags -> $ftext {
      $flags-text ~= "=item $ftext.\n";
    }

    my Str $min-max-text = '';
    $min-max-text ~= "=item Minimum value is $min.\n" if ?$min;
    $min-max-text ~= "=item Maximum value is $max.\n" if ?$max;

    my Str $default-text = '';
    $default-text ~= "=item Default value is $prop-default.\n" if ?$prop-default;

    $property-doc ~= Q:qq:to/EOHEADER/;

      =comment -----------------------------------------------------------------------
      =comment #TP:0:$prop-name:
      =head2 $prop-name

      $prop-blurp

      The B<Gnome::GObject::Value> type of property I<$prop-name> is C<$prop-g-type>.

      $flags-text$min-max-text$default-text
      EOHEADER

    $property-doc-entries{$prop-name} = $property-doc;
#note "end prop";
  }
#note "end of all props";
  print "\n";

  if ?$property-doc-entries {
    note "add property information to $output-file";
    $output-file.IO.spurt( Q:to/EODOC/, :append);

      #-------------------------------------------------------------------------------
      =begin pod
      =head1 Properties
      EODOC

#`{{
    $output-file.IO.spurt( Q:to/EODOC/, :append);

      #-------------------------------------------------------------------------------
      =begin pod
      =head1 Properties

      An example of using a string type property of a B<Gnome::Gtk3::Label> object. This is just showing how to set/read a property, not that it is the best way to do it. This is because a) The class initialization often provides some options to set some of the properties and b) the classes provide many methods to modify just those properties. In the case below one can use C<new(:label('my text label'))> or C<.set-text('my text label')>.

        my Gnome::Gtk3::Label $label .= new;
        my Gnome::GObject::Value $gv .= new(:init(G_TYPE_STRING));
        $label.get-property( 'label', $gv);
        $gv.set-string('my text label');

      =head2 Supported properties
      EODOC
}}

    for $property-doc-entries.keys.sort -> $property {
      note "save property $property";
      $output-file.IO.spurt( $property-doc-entries{$property}, :append);
    }

    $output-file.IO.spurt( "=end pod\n", :append);
  }

#  $output-file.IO.spurt( $property-doc, :append);
}
}}

#-------------------------------------------------------------------------------
# Try to find a c-doc first followed by a g_param_spec_*()
sub property-search ( $source-content is rw --> List ) {
  my Str ( $doc, $spec);

  # $source-content is cleaned only once
  state Bool $keep-cleening = True;
  while $keep-cleening {
    # cleanp some define c-calls which might interfere
    $source-content ~~ m/
      $<property-spec> = [
        'g_param_spec_' [
          pool || ref || unref ||
          sink || get || set || steal || internal
        ]
#        .*? ');'                            # till the spec ends
      ]
    /;

    my $property-spec = ($<property-spec> // '').Str;
#note $property-spec;

    $keep-cleening = ?$property-spec;
    $source-content ~~ s/ $property-spec // if $keep-cleening;
  }

  ( $doc, $spec) = try-prop-search1($source-content);
  return ( $doc, $spec) if (?$doc and ?$spec);

  ( $doc, $spec) = try-prop-search2($source-content);
  return ( $doc, $spec) if (?$doc and ?$spec);

  # Keep this one at the last option. It checks for props without doc
  ( $doc, $spec) = try-prop-search-last($source-content);
  return ( $doc, $spec) if ?$spec;

  ( '', '')
}

#-------------------------------------------------------------------------------
# Try to find a c-doc first followed by a g_param_spec_*()
sub try-prop-search1 ( $source-content is rw --> List ) {
  $source-content ~~ m/
    $<property-doc> = [
      '/**'                               # start c-comment block
      \s+ '*' \s+ <alnum>+                # first line has a class name
      ':' [ <alnum> || '-' ]+ ':'         # and a :property name:
      .*? '*/'                            # till the end of c-comment
    ]

    $<T> = [\s* <alnum>+ \s* '=' \s*]     # assign to variable

    $<property-spec> = [
      'g_param_spec_'                     # prop spec starts
      .*? ');'                            # till the spec ends
    ]
  /;

  my Str $T = ~($<T> // '');
  my Str $property-doc = ~($<property-doc> // '');
  my Str $property-spec = ~($<property-spec> // '');
  return ( '', '') unless (?$property-doc and ?$property-spec);

  $source-content ~~ s/ $property-doc $T $property-spec//;

  ( $property-doc, $property-spec)
}

#-------------------------------------------------------------------------------
# Try to find a c-doc first followed by a g_param_spec_*()
sub try-prop-search2 ( $source-content is rw --> List ) {

#note "try-prop-search 2:, ", $source-content ~~ m/'/**' \s+ '*' \s+ <alnum>+ ':' [ <alnum> || '-' ]+ ':'/;

  $source-content ~~ m/
    $<property-doc> = [
      '/**'                               # start c-comment block
      \s+ '*' \s+ <alnum>+                # first line has a class name
      ':' [ <alnum> || '-' ]+ ':'         # and a :property name:
      .*? '*/'                            # till the end of c-comment
    ]

    $<T> = [
      \s*
      props '[' <-[\]]>+ ']' \s* '=' \s*  # sometimes there's an array def
    ]

    $<property-spec> = [
      'g_param_spec_'                     # till prop spec starts
      .*? ');'                            # till the spec ends
    ]
  /;

  my Str $T = ~($<T> // '');
  my Str $property-doc = ~($<property-doc> // '');
  my Str $property-spec = ~($<property-spec> // '');
  return ( '', '') unless (?$property-doc and ?$property-spec);
#note "try-prop-search 2: $property-spec";

  $source-content ~~ s/ $property-doc $T $property-spec//;

  ( $property-doc, $property-spec)
}

#-------------------------------------------------------------------------------
# Try to find g_param_spec_*() when no doc available
sub try-prop-search-last ( $source-content is rw --> List ) {

#note "try-prop-search 2:, ", $source-content ~~ m/'/**' \s+ '*' \s+ <alnum>+ ':' [ <alnum> || '-' ]+ ':'/;

  $source-content ~~ m/
    $<property-spec> = [
      'g_param_spec_'                     # till prop spec starts
      .*? ');'                            # till the spec ends
    ]
  /;

  my Str $property-spec = ~($<property-spec> // '');
  return ( '', '') unless ?$property-spec;

  my List $l = process-prop-args($property-spec);
  my Array $args = $l[6];

  # Create a doc c-wise from the spec. This causes process-prop-args-type()
  # to be called twice unfortunately
  my Str $property-doc = Q:qq:to/EOPROPDOC/;
     /**
      * {$*lib-class-name}:{$args[0]}:
      *
      * $args[2]
      */
    EOPROPDOC

  $source-content ~~ s/$property-spec//;

  ( $property-doc, $property-spec)
}

#-------------------------------------------------------------------------------
# Get property documentation
sub get-prop-doc ( Str $search-prop-doc is rw --> List ) {
  $search-prop-doc ~~ m/
    \s+ '*' \s+ $*lib-class-name ':'
    $<prop-name> = [ [<alnum> || '-']+ ] ':'
  /;
  my Str $property-name = ~($<prop-name> // '');

  # Remove classname and property name
  $search-prop-doc ~~
    s/ \s+ '*' \s+ $*lib-class-name ':' $property-name ':' //;
  $search-prop-doc ~~ s/ \s* \* \s+ Since ':' \s+ \d+ \. \d+ \s*//;

  $search-prop-doc = cleanup-source-doc(
    primary-doc-changes($search-prop-doc)
  );

#note "get-prop-doc $property-name: $search-prop-doc";

  ( $property-name, $search-prop-doc);
}

#-------------------------------------------------------------------------------
# $search-prop-spec could be something like
# pspec = g_param_spec_boolean ("show-other",
#     P_("Show other apps"),
#     P_("Whether the widget should show other applications"),
#     FALSE,
#     G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
#     G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
#
sub process-prop-args ( Str $search-prop-spec is copy --> List ) {
  my Str $prop-spec-type = '';
  my Str $prop-g-type = '';
#  my Str $prop-name = '';

#note "process-prop-args 0: $search-prop-spec";

  # Get the type name from the call to property function. E.g. the call
  # g_param_spec_boolean ( … ) gives a boolean.
  $search-prop-spec ~~
    m/ 'g_param_spec_' $<prop-spec-type> = [ <alnum>+ ] \s* '(' /;
  $prop-spec-type = ~($<prop-spec-type> // '' );

  # And its g-type is al uppercase with some prefix: E.g. G_TYPE_BOOLEAN
  $prop-g-type = 'G_TYPE_' ~ $prop-spec-type.uc;

  # Cleanup up to keep arguments only
  $search-prop-spec ~~ s/ 'g_param_spec_' $prop-spec-type \s* '(' //;
  $search-prop-spec ~~ s/ ');' //;
  $search-prop-spec = $search-prop-spec;

#note "process-prop-args 1: $search-prop-spec";

#note "types: $prop-spec-type, $prop-g-type";

  # Before splitting the arguments on the ',', we must first rename commas
  # found in strings into a string '_COMMA_'.
  my Bool $in-string = False;
  my Str $temp-prop-args = $search-prop-spec;
  $search-prop-spec = '';
  for $temp-prop-args.comb -> $c {
    if $c eq '"' {
      $in-string = !$in-string;
      $search-prop-spec ~= '"';
    }

    elsif $in-string and $c eq ',' {
      $search-prop-spec ~= '_COMMA_';
    }

    else {
      $search-prop-spec ~= $c;
    }
  }
#note 'process-prop-args 2: ', $search-prop-spec;

  # Now we can split on the real commas and substitute the comma back in str
  my Array $args = [];
  for $search-prop-spec.split(/ \s* ',' \s* /) -> $arg is copy {
    # rename '_COMMA_' back to ','
    $arg ~~ s/ '_COMMA_' /,/;

    # Remove some translation calls
    $arg ~~ s/ 'P_(' //;
    $arg ~~ s/ ')' //;

    # Some cleanup
    $arg ~~ s:g/ \" \s+ \" //;
    $arg ~~ s:g/ \" //;

    # Save argument
    $args.push($arg);
  }

#note 'process-prop-args 3: ', @$args.join("\n  ");

  my Array $flags;
  my Str $gtype-string;
  my $prop-default;
  my Str ( $min, $max);
  ( $flags, $gtype-string, $prop-default, $min, $max) =
    process-prop-args-type( $prop-spec-type, $prop-g-type, $args);

#note "Flags: $flags.gist()";
  ( $flags, $gtype-string, $prop-g-type, $prop-default, $min, $max, $args)
}

#-------------------------------------------------------------------------------
# See also https://www.freedesktop.org/software/gstreamer-sdk/data/docs/latest/gobject/gobject-Standard-Parameter-and-Value-Types.html

sub process-prop-args-type (
  Str $prop-spec-type, Str $prop-g-type, Array $args --> List
) {
  # Simple types have the types defined before. Only complex classes
  # like G_TYPE_OBJECT have additional info like a gtype of GtkWindow.
  my Str $gtype-string = $prop-g-type;
  my Str $flags = '';
  my Str $prop-default = '';
  my Str $min = '';
  my Str $max = '';

#note "process-prop-args-type '$prop-spec-type', '$prop-g-type': ", $args.join("\n  ");

  # Get some info depending on the type of this property
  given $prop-spec-type {
    when 'boolean' {
      $prop-default = $args[3].tc;
      $flags = $args[4] // '';
    }

    when any(<char uchar int uint long ulong int64 uint64 float double>) {
      $min = $args[3] // '';
      $max = $args[4] // '';
      $prop-default = $args[5] // '';
      $flags = $args[6] // '';
    }

    when any(<enum flags>) {
      $gtype-string = $args[3] // '';
      $prop-default = $args[4] // '';
      $flags = $args[5] // '';
    }

    when 'string' {
      $prop-default = $args[3] ~~ 'NULL' ?? 'undefined' !! $args[3];
      $flags = $args[4] // '';
    }

    when any(<param pointer>) {
      note "Parameter type '$prop-spec-type' not processed";
      $prop-default = '';
      $flags = '';
    }

    when any(<object boxed>) {
      $gtype-string = $args[3] // '';
      $prop-default = '';
      $flags = $args[4] // '';
    }

    when any(<unichar array override gtype>) {
      note "Parameter type '$prop-spec-type' not processed";
      $prop-default = '';
      $flags = '';
    }

    when 'variant' {
      $prop-default = $args[4]  ~~ 'NULL' ?? 'undefined' !! $args[3];
      $flags = $args[5] // '';
    }

    default {
      note "Unknown parameter type '$prop-spec-type' not processed"
        if ?$prop-spec-type;
      $prop-default = '';
      $flags = '';
    }
  }

  # Flags are ored into one value, a mask. Get the values by splitting the
  # string on the C-or operation and translate into strings for the
  # common ones.
  my Array $f = [];
  for $flags.split(/\s* '|' \s*/) {
#.note;
    when / [GTK || G] '_PARAM_READABLE'/ {
      $f.push: 'Parameter is readable';
    }

    when / [GTK || G] '_PARAM_WRITABLE'/ {
      $f.push: 'Parameter is writable';
    }

    # alias for 'G_PARAM_READABLE | G_PARAM_WRITABLE'
    when / [GTK || G] '_PARAM_READWRITE'/  {
      $f.push: 'Parameter is readable and writable';
    }

    when /[GTK | G] '_PARAM_CONSTRUCT'/ {
      $f.push: 'Parameter is set on construction of object';
    }

    when /[GTK || G] '_PARAM_CONSTRUCT_ONLY'/ {
      $f.push: 'Parameter is only set on construction of object';
    }

    when any(<
      G_PARAM_LAX_VALIDATION G_PARAM_STATIC_NAME G_PARAM_PRIVATE
      GTK_PARAM_LAX_VALIDATION GTK_PARAM_STATIC_NAME GTK_PARAM_PRIVATE
      >) {
    }

    when any(<
      G_PARAM_STATIC_NICK G_PARAM_STATIC_BLURB
      GTK_PARAM_STATIC_NICK GTK_PARAM_STATIC_BLURB
      >) {
    }

    # already filtered out
    when / [GTK || G] '_PARAM_DEPRECATED'/ {
    }

    # G_PARAM_STATIC_STRINGS alias for
    #    'G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB'
    when any(<
      G_PARAM_STATIC_STRINGS G_PARAM_MASK G_PARAM_USER_SHIFT
      GTK_PARAM_STATIC_STRINGS GTK_PARAM_MASK GTK_PARAM_USER_SHIFT
      >) {
    }

    # Comes from somewhere else
    when / [GTK || G] '_PARAM_EXPLICIT_NOTIFY'/ {
    }

    default {
      note "Unknown param_spec flag type '$_'" if ?$_;
    }
  }

#note "process-prop-args-type: $flags -> $f.gist()";
  ( $f, $gtype-string, $prop-default, $min, $max)
}

#-------------------------------------------------------------------------------
sub get-vartypes ( Str:D $include-content ) {

#  my Str $enums-doc = get-enumerations($include-content);
#  my Str $structs-doc = get-structures($include-content);

  get-enumerations($include-content);
  get-structures($include-content);
}

#-------------------------------------------------------------------------------
sub get-enumerations ( Str:D $include-content is copy ) {
#`{{
  my Str $enums-doc = Q:qq:to/EODOC/;

    #-------------------------------------------------------------------------------
    =begin pod
    =head1 Types
    =end pod
    EODOC
}}

  my Bool $found-doc = False;
  my Hash $enum-docs = %();

  print "Find enumerations ";
  loop {
    print ".";
    $*OUT.flush;

    my Str $enum-name = '';
    my Str $items-doc = '';
    my Str $enum-doc = '';
    my Str $enum-spec = '';

    $include-content ~~ m:s/
      $<enum-type-section> = [
         '/**' .*? '*/' 'typedef' 'enum' '{' .*? '}' <-[;]>+ ';'
      ]
    /;
    my Str $enum-type-section = ~($<enum-type-section> // '');
#note $enum-type-section;

    # if no enums are found, clear the string
    if !?$enum-type-section {
#      $enums-doc = '' unless $found-doc;
      last;
    }

    # this match "m:g/'/**' .*? '*/' ...etc... /" can fail. It can gather
    # too many comment blocks to stop at a final enum. So try to find the
    # last doc section.
    my Int $start-doc = 1;  # There is a '/**' string at 0!
    my Int $x;
    while ?($x = $enum-type-section.index( '/**', $start-doc)) {
      $start-doc = $x + 1;
    }

    $start-doc -= 1;
    $enum-type-section = $enum-type-section.substr(
      $start-doc, $enum-type-section.chars - $start-doc
    );

    # remove type info for next search
    $include-content ~~ s/ $enum-type-section //;

    # enums found
    $found-doc = True;

    my Bool ( $get-item-doc, $get-enum-doc, $process-enum) =
            ( False, False, False);

    for $enum-type-section.lines -> $line {
#note "Line: $line";

      next if $line ~~ m/ '/**' /;

      if $line ~~ m/ ^ \s+ '*' \s+ $<enum-name> = [<alnum>+] ':' \s* $ / {
        $enum-name = ~($<enum-name>//'');
#        note "get enumeration $enum-name";
        $get-item-doc = True;
      }

      elsif $line ~~ m/ ^ \s+ '* @' $<item> = [ <alnum>+ ':' .* ] $ / {
        $items-doc ~= "\n=item " ~ ~($<item>//'');
#note "Item: $items-doc";
      }

      # on empty line switch from item to enum doc
      elsif $line ~~ m/ ^ \s+ '*' \s* $ / {
        $get-item-doc = False;
        $get-enum-doc = True;

        $enum-doc ~= "\n";
      }

      # end of type documentation
      elsif $line ~~ m/ ^ \s+ '*'*? '*/' \s* $ / {
        $get-item-doc = False;
        $get-enum-doc = False;
        $process-enum = True;

        $enum-spec = "\n=end pod\n\n#TE:0:$enum-name:\nenum $enum-name is export (\n";
      }

      elsif $line ~~ m/ ^ \s+ '*' \s* 'Since:' .* $ / {
        # ignore
      }

      elsif $line ~~ m/ ^ \s+ '*' \s+ $<doc> = [ \S .* ] $ / {
        if $get-item-doc {
          $items-doc ~= " " ~ ~($<doc>//'');
        }

        elsif $get-enum-doc {
          $enum-doc ~= "\n" ~ ~($<doc>//'');
        }
      }

      elsif $line ~~ m/ ^ \s+ $<item-name> = [<alnum>+ <-[,]>* ','? ] \s* $ / {
        my Str $s = ~($<item-name> // '');
        $s ~~ s/ '=' /=>/;
        $s ~~ s/ '<<' /+</;
        $s ~~ s/ ( <alnum>+ ) /'$/[0]'/;
        $enum-spec ~= "  $s\n";
      }

      elsif $line ~~ m:s/ '}' $enum-name ';' / {
        $enum-spec ~= ");\n";
      }
    }

    # remove first space
    $enum-doc ~~ s/ ^ \s+ //;

    $enum-doc = primary-doc-changes($enum-doc);
    $items-doc = primary-doc-changes($items-doc);
#`{{
    $enums-doc ~= Q:qq:to/EODOC/;
      #-------------------------------------------------------------------------------
      =begin pod
      =head2 enum $enum-name

      $enum-doc

      $items-doc

      $enum-spec
      EODOC
}}
    $enum-docs{$enum-name} = Q:qq:to/EODOC/;
      #-------------------------------------------------------------------------------
      =begin pod
      =head2 enum $enum-name

      $enum-doc

      $items-doc

      $enum-spec
      EODOC
  }

#  $output-file.IO.spurt( $enums-doc, :append);
   $output-file.IO.spurt( Q:qq:to/EODOC/, :append);

    #-------------------------------------------------------------------------------
    =begin pod
    =head1 Types
    =end pod
    EODOC

  print "\n";
  for $enum-docs.keys.sort -> $enum-name {
    note "get enumeration $enum-name";
    $output-file.IO.spurt( $enum-docs{$enum-name}, :append);
  }
#  note "add type information to $output-file";
}

#-------------------------------------------------------------------------------
sub get-structures ( Str:D $include-content is copy ) {

#  my Str $structs-doc = '';
  my Bool $found-doc = False;
  my Hash $struct-docs = %();

  # now we try again to get structs
  print "Find structures ";
  loop {
    print ".";
    $*OUT.flush;

    my Str $struct-name = '';
    my Str $items-doc = '';
    my Str $struct-doc = '';
    my Str $struct-spec = '';
    my Str ( $entry-type, $raku-entry-type);
    my Bool $type-is-class;

    $include-content ~~ m:s/
      $<struct-type-section> = [
          '/**' .*? '*/' struct <-[{]>+ '{' <-[}]>+ '};'
      ]
    /;

    my Str $struct-type-section =