use strict;
use warnings;

package Opts;

our ($OPTS_TX, @BOOL_OPTS, @DEC_OPTS, @STRING_OPTS, %OPTS,
     $BOOL_REGEXP, $DEC_REGEXP, $DEC_REGEXP_COMPACT, $STRING_REGEXP_COMPACT);

BEGIN {
    $OPTS_TX = '';
    @BOOL_OPTS = ();
    @DEC_OPTS = ();
    @STRING_OPTS = ();
    %OPTS = ();
}

sub new
{
    my $class = shift;
    my $this = shift || {};

    $this->{'_DONE'} = 0;
    $this = bless( $this, ref( $class ) || $class );
    $this->set_defaults( @_ );

    if( !defined( $BOOL_REGEXP ) ) {
        $BOOL_REGEXP = $#BOOL_OPTS >= 0 ?
            '^([+-])(' . join('|', @BOOL_OPTS) . ")\$" : '';
        
        $DEC_REGEXP_COMPACT = $#DEC_OPTS >= 0 ?
            '^-(' . join('|', @DEC_OPTS) . ")=(\\d+)\$" : '';
        
        $DEC_REGEXP = $#DEC_OPTS >= 0 ?
            '^-(' . join('|', @DEC_OPTS) . ")\$" : '';

        $STRING_REGEXP_COMPACT = $#STRING_OPTS >= 0 ?
            '^-(' . join('|', @STRING_OPTS) . ")=(.+)\$" : '';
    }

    return $this;
}

sub set_defaults {
    my $this = shift;

    for( my $i = 0; $i < $#_; $i += 2 ) # default values
    {
        defined( $this->{ $_[$i] } ) or $this->{ $_[$i] } = $_[$i+1];
    }
}

sub add_opts {
    defined( $BOOL_REGEXP ) and die '???';

    my ($opt, $handler);

    $OPTS_TX .= (shift || '' );

    while( $#_ > 0 ) {
        $opt = shift;
        $handler = shift;

        $handler eq 'b' and do {
            if( defined( $OPTS{$opt} ) ) {
                $OPTS{$opt} eq 'b' or 
                    die "Duplicite inconsistent option name '$opt'";
            } else {
                push @BOOL_OPTS, $opt;
                $OPTS{$opt} = 'b';
            }
            next;
        };

        $handler eq 'd' and do {
            if( defined( $OPTS{$opt} ) ) {
                $OPTS{$opt} eq 'd' or
                    die "Duplicite inconsistent option name '$opt'";
            } else {
                push @DEC_OPTS, $opt;
            }
            next;
        };

        $handler eq 's' and do {
            if( defined( $OPTS{$opt} ) ) {
                $OPTS{$opt} eq 's' or
                    die "Duplicite inconsistent option name '$opt'";
            } else {
                push @STRING_OPTS, $opt;
            }
            next;
        };

        die "???";
    }

    die "$_[0]???" if( $#_ >= 0 );
}

sub try_handle {
    my ($this, $ARGV )  = @_;

    $this->{'_DONE'} and return 0;

    $ARGV->[0] eq '--' and do {
        $this->{'_DONE'} = 1;
        shift @{$ARGV};
        return 1;
    };

    $BOOL_REGEXP and 
        $ARGV->[0] =~ /$BOOL_REGEXP/o and do {
        # $1 +/-, $2 name
        $this->{$2} = ( $1 eq '-' );
        shift @{$ARGV};
        return 1;
    };
        
    $DEC_REGEXP_COMPACT and
        $ARGV->[0] =~ /$DEC_REGEXP_COMPACT/o and do {
        # $1 name, $2 value
        $this->{$1} = $2;
        shift @{$ARGV};
        return 1;
    };

    $DEC_REGEXP and
        $ARGV->[0] =~ /$DEC_REGEXP/o and do {
        # $1 name, $ARGV->[1] value
        $#{$ARGV} > 0 or die "Missing value for $1 option\n";
        $this->{$1} = $ARGV->[1];
        shift @{$ARGV};
        shift @{$ARGV};
        return 1;
    };

    $STRING_REGEXP_COMPACT and
        $ARGV->[0] =~ /$STRING_REGEXP_COMPACT/o and do {
        print ">>$STRING_REGEXP_COMPACT\n";
        # $1 name, $2 value
        $this->{$1} = $2;
        shift @{$ARGV};
        return 1;
    };


    return 0;
}

1;


