#!/usr/bin/perl -CSD

use utf8;
use strict;
use Cwd;
use File::Basename;
use v5.16;
use feature 'switch';

open DEP, ">&=3";

sub weave
{
    my ( $fn ) = @_;
    say DEP "dep " . getcwd . "/$fn";

    if ( -d $fn )
    {
        my $cwd = getcwd();
        chdir( $fn ) or die "error changing into $fn";
        weave( "readme.txt" );
        chdir( $cwd );
    }

    elsif ( -f $fn )
    {
        open my $fh, $fn;
        $fn =~ m,^(.*)\.(.*?)$|^([^.]*)$,;
        my ( $name, $suff ) = ( $1 . $3, $2 );
        say STDERR "processing $fn [$suff]";
        $suff eq 'txt' ? weave_text( $fh ) : weave_source( $fh, $name );
        close $fh;
        print "\n";
    }

    elsif ( $fn !~ m,\.[a-z]+$, )
    {
        weave( $_ ) for ( "$fn.txt", "$fn.hpp", "$fn.cpp" );
    }
}

sub weave_text
{
    my ( $fh ) = @_;

    my @append;
    my %append;

    while ( <$fh> )
    {
        if ( m,‹[▷▶](.*?)›, && !defined $append{$1} )
        {
            push @append, $1;
            $append{$1} = 1;
        }

        print;
    }

    print "\n";

    weave( $_ ) for ( @append );
}

sub uncomment
{
    my ( $txt ) = @_;
    return $txt =~ s,^\s+\* ?,,r;
}

sub weave_source
{
    my ( $fh, $name ) = @_;
    use constant { MORE => 0, START => 1, HCOM_BODY => 2, HCOM_COPYING => 3,
                   HCOM_POST => 4, COM_BODY => 5, COM_POST => 6, CODE => 7, SKIP_NS => 8 };
    my $state = START;

    print "### ‹$name›\n\n";

    my $keep;
    my $in_ns = 0;
    my $blanks = 0;

    while ( <$fh> )
    {
        my $com_start = m,^\s*/\*,;
        my $com_end   = m,\*/$,;
        my $cpp       = m,^#,;
        my $code_rx   = qr,(/[^*]|\w|[#{}()\[\]]),;
        my $code      = m,^\s*$code_rx,;

        my $next = MORE;

        if ( $state == START )
        {
            $next = HCOM_POST;
            $next = HCOM_BODY if $com_start;
            $next = CODE      if $code && !$cpp;
            $next = SKIP_NS   if $code && m,^namespace ,;
        }

        elsif ( $state == HCOM_BODY )
        {
            $keep .= uncomment( $_ );
            $next = HCOM_COPYING if m,^ \* \(c\) [0-9]+ .*? <[a-z0-9.]+@[.a-z0-9+]+>,;

            if ( $com_end )
            {
                say STDERR "no copying";
                print $keep;
                $keep = "";
                $next = HCOM_POST;
            }
        }

        elsif ( $state == HCOM_POST )
        {
            $next = CODE     if $code && !$cpp;
            $next = SKIP_NS  if $code && m,^namespace ,;
            $next = COM_BODY if $com_start;
        }

        elsif ( $state == COM_POST )
        {
            $next = CODE     if $code;
            $next = SKIP_NS  if $code && m,^namespace ,;
        }

        elsif ( $state == SKIP_NS )
        {
            $next = CODE     if $code;
            $next = MORE     if m,^\{,;
        }

        elsif ( $state == HCOM_COPYING ) { $next = HCOM_POST if $com_end; }
        elsif ( $state == CODE )
        {
            $next = COM_BODY  if $com_start;
            $next = COM_POST  if $com_start && $com_end;
        }
        elsif ( $state == COM_BODY )     { $next = COM_POST  if $com_end; }

        if ( $in_ns && $state == CODE && m,^}, )
        {
            $in_ns = 0;
            next;
        }

        $in_ns = 1 if $next == SKIP_NS;

        s,^$code_rx,    $1,        if $state == CODE || $next == CODE;
        s,^(.*)$,\n$1 /* C++ */,   if $next  == CODE && $state != CODE;
        s,$, /* C++ */,            if $state == CODE && $next == MORE && $blanks;
        s,\*/$,,                   if $next  == COM_POST;
        s,^\s*/\* ?,,              if $next  == COM_BODY || $next == COM_POST;

        $_ = uncomment( $_ ) if $state == COM_BODY;

        print "\n" if  $next == COM_BODY and !$blanks;
        print "$_" if ( $state == CODE || $state == COM_BODY ) && $next == MORE;
        print "$_" if ( $next == CODE || $next == COM_BODY || $next == COM_POST );

        $state = $next unless $next == MORE;
        m,^$, ? ++ $blanks : ( $blanks = 0 );
    }
}

chdir( dirname $ARGV[0] );
weave( basename $ARGV[0] );
