### ----------------------------------------------------
### -- CSS::Module
### -- Licenses: Artistic-2.0
### -- Authors: David Warring
### -- File: make-modules.raku
### ----------------------------------------------------
use v6;

use CSS::Specification::Build;
use CSS::Module::CSS3;
use CSS::Module::CSS3::Fonts::AtFontFace;
use CSS::Module::SVG;

class Build {

    method build($where) {

        indir $where, {

            my %props;
            my CSS::Module::CSS3::Actions $actions .= new;

            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>],
                 :SVG[<src svg-properties.txt> => <SVG>],
                 'CSS3::Fonts::AtFontFace' => [<src css3x-font-@fontface-properties.txt> => <CSS3 Fonts AtFontFace>],

                ) {
                my $meta = .key;

                for .value.list {
                    my ($input-spec, $class-isa) = .kv;
                    my $grammar = CSS::Module::CSS3;
                    if $class-isa.tail eq 'AtFontFace' {
                        $grammar = CSS::Module::CSS3::Fonts::AtFontFace;
                        %props = ();
                    }
                    elsif $class-isa.tail eq 'SVG' {
                        $grammar = CSS::Module::SVG;
                    }

                    for (:interface<Interface>,
                         :actions<Actions> ,
                         :grammar<Grammar>) {
                        my ($type, $subclass) = .kv;
                        my $name = (<CSS Module>, @$class-isa, <Gen>, $subclass).flat.join('::');

                        my $class-dir = $*SPEC.catdir(<lib CSS Module>, @$class-isa, <Gen>);
                        mkdir $class-dir;

                        my $class-path = $*SPEC.catfile( $class-dir, $subclass~'.rakumod' );

                        my $input-path = $*SPEC.catfile( |@$input-spec );
                        say "Building $input-path => $name";
                        {
                            my $*IN = open $input-path, :r;
                            my $*OUT = open $class-path, :w;

                            CSS::Specification::Build::generate( $type, $name );
                        }

                        my @summary = CSS::Specification::Build::summary( :$input-path );
                        for @summary.sort(*.<name>) {
                            my %prop = %$_;
                            my $prop-name = %prop<name>:delete;

                            with %prop<default> -> $default {
                                my @d = $default;
                                # either a description or concrete term
                                if $default !~~ /agent/ {
                                    @d.push: .ast<property><expr>
                                        with $grammar.parse("$prop-name:$default", :$actions, :rule<declaration>);
                                }
                                %prop<default> = @d;
                            }
                            %props{$prop-name} = %prop;
                        }
                    }
                }
                my $class-dir = $*SPEC.catdir(flat(<lib CSS Module>, $meta.split('::')));
                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 'use v6;';
                    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.perl};";
                    # 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);
}


