use v6;

use experimental :rakuast;
use CSS::Specification::Compiler :&build-metadata;
use NativeCall;

sub path(RakuAST::Package $p) {
    $p.name.parts>>.name.join: '/';
}

class Build {

    method build($where) {

        indir $where, {
            my %props;
            my Pair @metadata;

            for (:CSS1[<src css1-properties.txt> => <CSS1>],
                 :CSS21[<src css21-properties.txt> => <CSS21>],
                 :CSS3[<src css3x-font-properties.txt> => <CSS3 Fonts>,
                       <src css3x-paged-media.txt> => <CSS3 PagedMedia>,
                       <src css-values-3-20240322.txt> => <CSS3 Values_and_Units>,
                      ],
                 :SVG[<src svg-properties.txt> => <SVG>],
                 'CSS3::Fonts::AtFontFace' => [<src css3x-font-@fontface-properties.txt> => <CSS3 Fonts AtFontFace>],

                ) {
                my $meta-root = .key;
                %props = () if $meta-root eq 'CSS3::Fonts::AtFontFace';
                for .value.list {
                    my ($input-spec, $class-isa) = .kv;
                    my @base-id = flat <CSS Module>, @$class-isa, <Gen>;
                    my @grammar-id = @base-id.Slip, 'Grammar';
                    my $scope := 'unit';
                    my CSS::Specification::Compiler $compiler .= new;
                    my $file = $input-spec.join: '/';
                    my @defs = $compiler.load-defs: :$file;
                    my %child-props = $compiler.child-props;

                    my RakuAST::Package $grammar-ast = $compiler.build-grammar(@grammar-id, :$scope);
                    "lib/{$grammar-ast.&path}.rakumod".IO.spurt: $grammar-ast.DEPARSE;

                    my @actions-id = @base-id.Slip, 'Actions';
                    my RakuAST::Package $actions-ast = $compiler.build-actions(@actions-id, :$scope);
                    "lib/{$actions-ast.&path}.rakumod".IO.spurt: $actions-ast.DEPARSE;

                    my @external-id = @base-id.Slip, 'External';
                    my RakuAST::Package $external-ast = $compiler.build-external(@external-id, :$scope);
                    "lib/{$external-ast.&path}.rakumod".IO.spurt: $external-ast.DEPARSE;

                    my %meta = @defs.&build-metadata(:%child-props);
                    %props ,= %meta;
                }
                
                %props.&write-metadata($meta-root);
            }
        }
    }
}

sub write-metadata(%props, $meta) {
    my $class-dir = $*SPEC.catdir(<lib CSS Module>, $meta.split('::').Slip);
    my $class-path = $*SPEC.catfile( $class-dir, 'Metadata.rakumod' );
    my $class-name = "CSS::Module::{$meta}::Metadata";
    say "Building $class-name";
    {
        my $*OUT = open $class-path, :w;
        say "#  -- DO NOT EDIT --";
        say "# generated by: $*PROGRAM-NAME {@*ARGS}".trim;
        say 'use NativeCall;';
        say 'use CSS::Module::Property;';
        say '';
        say "module $class-name \{";
        say "    BEGIN our \$property = {%props.item.raku};";
        # todo: BEGIN our \$index = ... ; ## Missing serialize REPR function for REPR
        say '    our enum prop-names <' ~ %props.keys.sort.join(' ') ~ '>;';
        say q:to<END>;
            our sub index {
                state $ //= do {
                    my $enums := prop-names.enums;
                    CArray[CSS::Module::Property].new: |$property.sort.map({CSS::Module::Property.new(:$enums, name => .key, |.value)});
                }
            }
        }
        END
        $*OUT.close;
    }
}

sub MAIN(Str $working-directory = '.' ) {
    Build.new.build($working-directory);
}

