package Cmdsel;
use warnings;
use strict;
use integer;
use FindBin;
use lib $FindBin::RealBin;
use Def;

# This module provides the function parse() to parse a cmdsel.txt file.

our $shell_sect  = '0';
our $x_div       = '8';
our $x_ext       = 'x';
our $shsect_dflt = '(shell)';

our $pat_line    ;
our $pat_cmdsect ;
our $pat_cmdsect0;
our $pat_title   ;
our $xpat_interm ;
our $pat_shortbar;
our $pat_longbar ;
{
  my $w1        = $Def::width_shortbar    ;
  my $w2        = $Def::width_shortbar + 1;
  my $n0        = $Def::ndig              ;
  my $n2        = $Def::ndig           - 2;
  $pat_line     = qr/^(.{${Def::w_whatis}})- (\S(?:.*?\S)??)\s*$/o;
  $pat_cmdsect  =
    qr/^(\S(?:.*?\S)??)(\s+)(\([^()]+?\)|\[[^\[\]]+?\])$/o;
  $pat_cmdsect0 = qr/^(\S(?:.*?\S)??)$/o;
  $pat_title    = qr/^((\d)(\d)\d{$n2}) (\S(?:.*?\S)??)\s*$/o;
  $xpat_interm  = qr/^\d{$n0} \S/o;
  $pat_shortbar = qr /^-{$w1}$/o;
  $pat_longbar  = qr /^-{$w2}/o ;
}

sub parse {

  for ( 1, 2 ) { 1 while shift ne $Def::mark_commands }
  shift;

  my $intitle      = '';
  my $intermission = '';
  my $ram;
  my %parse        = ();

  for (@_) {

    /^.{0,${Def::width_fmt}}$/
      or die "$0: line $. too long\n$_\n";
    /$pat_longbar/o and $intitle = !$intitle, next;

    if ( $intitle ) {

      last unless /\S/;
      my( $ramno, $sect, $div, $title ) = /$pat_title/o;
      $intermission = !defined $ramno;
      next if $intermission;
      my $isx = $div eq $x_div ? $x_ext : '';
      defined $ram
        and die "$0: confused: new ram $ramno starts "
        . "before old ram $ram->{ram} is stored\n";
      $ram = {
        ram   => $ramno,
        sect  => $sect ,
        div   => $div  ,
        isx   => $isx  ,
        title => $title,
        line  => $_    ,
        x     => {}    ,
      };

    }

    elsif ( !$intermission ) {

      if ( /\S/ ) {
        # Continue the ram.
        my( $cmdsect, $desc ) = /$pat_line/o
          or die "$0: badly formatted Guide line $.\n$_\n";
        $cmdsect =~ s/\s*$//;
        my( $cmds, $spaces, $sect0 );
        unless (
          ( $cmds, $spaces, $sect0 ) = $cmdsect =~ /$pat_cmdsect/o
        ) {
          ( $cmds ) = $cmdsect =~ /$pat_cmdsect0/o
            or die "$0: command and section missing in\n$_\n";
          $sect0 = $ram->{sect} eq $shell_sect ? "[$shsect_dflt]"
            : '(' . $ram->{sect} . ( $ram->{isx} ? $x_ext : '' ) . ')';
        }
        my @cmd = split /\s*,\s*/, $cmds;
        my( $shsect, $sect );
        my $isx = '';
        if ( $sect0 =~ /^\[/ ) {
          ( $shsect ) = $sect0 =~ /^\[(.+)\]$/
            or die "$0: bad shell section $sect0\n";
          $sect = $shell_sect;
        }
        else {
          my $x0;
          ( $sect, $isx ) = $sect0 =~ /^\((\d)(${x_ext}?)\)$/
            or die "$0: bad man section $sect0\n";
          !defined($spaces) || length($spaces) == 1
            or warn "$0: \"$cmdsect\" misspaced\n";
        }
        my $x    = $ram->{x};
        my $line = $_       ;
        for ( @cmd ) {
          exists $x->{$_}
            and die
            "$0: command $_ listed twice (both in ram $ram->{ram})\n";
          $x->{$_} = {
            cmdsect =>  $cmdsect,
            cmds    =>  $cmds   ,
            sect0   =>  $sect0  ,
            shsect  =>  $shsect ,
            sect    =>  $sect   ,
            isx     =>  $isx    ,
            desc    =>  $desc   ,
            line    => \$line   ,
          };
        }
        (
          !defined($shsect) && (
            $ram->{sect} eq $shell_sect ||
            $sect ne $ram->{sect}       ||
            !$isx ne !$ram->{isx}
          )
        ) || (
          defined ($shsect) && $ram->{sect} ne $shell_sect
        ) and warn "$0: $cmdsect is mislisted "
          . "in section $ram->{ram}$ram->{isx}\n";
      }

      else {
        # End the ram.
        exists( $parse{ $ram->{ram} } )
          and die "$0: ram $ram->{ram} defined twice\n";
        $parse{ $ram->{ram} } = $ram;
        $ram = undef;
      }

    }

  }

  defined $ram
    and die "$0: confused: last ram $ram->{ram} "
    . "does not end properly, not stored\n";

  return \%parse;

}

1;

