#!/usr/perl

use strict;
use warnings;

my @propositions;
my %terms;
my $verbose;

my %prop_desc = (
  A => {
         quantity => 'universal',
         quality  => 'affirmative',
         form     => 'All S are P.',
         distributed_terms => [ 'S' ],
       },
  E => {
         quantity => 'universal',
         quality  => 'negative',
         form     => 'No S are P.',
         distributed_terms => [ 'S', 'P' ],
       },
  I => {
         quantity => 'particular',
         quality  => 'affirmative',
         form     => 'Some S are P.',
         distributed_terms => [ ],
       },
  O => {
         quantity => 'particular',
         quality  => 'negative',
         form     => 'Some S are not P.',
         distributed_terms => [ 'P' ],
       },
);

sub is_valid {
    my( $mood, $figure, $conditional ) = @_;

    return undef unless $mood =~ /^[AEIO]{3,3}$/;
    return undef unless $figure =~ /^[1234]$/;

    my %unconditionally_valid = ( # boolean
      AAA => {
        1 => 1
      },
      AEE => {
        2 => 1,
        4 => 1,
      },
      AII => {
        1 => 1,
        3 => 1,
      },
      AOO => {
        2 => 1
      },
      EAE => {
        1 => 1,
        2 => 1,
      },
      EIO => {
        1 => 1,
        2 => 1,
        3 => 1,
        4 => 1,
      },
      IAI => {
        3 => 1,
        4 => 1,
      },
      OAO => {
        3 => 1
      },
    );

    my %conditionally_valid = ( # aristotle
      AAI => {
        1 => 1,
        3 => 1,
        4 => 1,
      },
      EAO => {
        1 => 1,
        2 => 1,
        3 => 1,
        4 => 1,
      },
      AEO => {
        2 => 1,
        4 => 1,
      },
    );

    unless ( $conditional ) {
        return $unconditionally_valid{$mood}{$figure} ? 1 : 0;
    } else {
	return $unconditionally_valid{$mood}{$figure}
	    || $conditionally_valid{$mood}{$figure} ? 1 : 0;
    }
}


my $tobe_verbs = qr/are not|aren't|are|is not|isn't|is|have not|haven't|have|had not|hadn't|had|was not|wasn't|was|were not|weren't|were|cannot|can't|can|do not|don't|do/;

sub parse_proposition {
    my $prop = shift;
    my( $quantifier, $subject, $copula, $predicate );

    ($quantifier, $prop) = split( /\s+/, $prop, 2 );
    die "Unknown quantifier '$quantifier'.\n"
	unless ( $quantifier =~ /^(all|every|no|some)$/i );

    ($subject, $copula, $predicate) =
	split( /\s+($tobe_verbs)\s+/i, $prop, 2 );

    return ( $quantifier, $subject, $copula, $predicate );
}

sub is_query {
    my $prop = lc( shift );
    if ( $prop =~ /^(some|all|none|no|every)/ ) { return 0 }
    if ( $prop =~ /^($tobe_verbs)/ ) { return 1 }
    die "Can't determine if this is a query or a proposition.\n";
}

sub parse_query {
    my $prop = shift;
    my( $quantifier, $subject, $copula, $predicate );

    ($copula) = $prop =~ /^($tobe_verbs)/;
    $prop =~ s/^$copula\s+//;
    $prop =~ s/^(there)\s+//; # are there any, is there any...

    ($quantifier) = $prop =~ /^(all|no|some|any)/;
    $prop =~ s/^$quantifier\s+//;

    my @candidates;
    my $negative;
    foreach my $term1 (keys %terms) {
	if ( $prop =~ /^($term1)\s+/ ) {
	    (my $temp = $prop) =~ s/^$1\s+//;
	    if ( $temp =~ /^not\s+/ ) {
		$negative = 1;
		$temp =~ s/^not\s+//;
		# Is this the right place for this
		$copula .= ' not';
	    }
	    foreach my $term2 (keys %terms) {
		if ( $temp eq $term2 ) {
		    push @candidates, {
		      subject   => $term1,
                      predicate => $term2,
                    };
		}
	    }
	}
    }

    if ( @candidates == 1 ) {
	$subject = $candidates[0]{subject};
	$predicate = $candidates[0]{predicate};
	return ( $quantifier, $subject, $copula, $predicate );
    } elsif ( @candidates > 1 ) {
	die "Ambiguous query.\n";
	# Actually, don't really know at this point if it's ambiguous.
    } else {
	die "Failed to translate query.\n";
    }
}

sub translate_to_equivalent {
  # might as well handle conversion, obversion, and contraposition
}

sub determine_mood {
    my $quantifier = lc( shift );
    my $copula    = lc( shift );

    if ( $quantifier eq 'all' || $quantifier eq 'every' ) {
	return 'A';
    } elsif ( $quantifier eq 'none' || $quantifier eq 'no' ) {
	return 'E';
    } elsif ( $quantifier eq 'some' || $quantifier eq 'any' ) {
	if ( $copula =~ /not|n't/ ) {
	    return 'O';
	} else {
	    return 'I';
	}
    } else {
	die "Unknown mood.\n";
    }
}

sub try_command {
    my $cmd = shift;
    if ( $cmd =~ /^(quit|q)$/ ) {
	exit 1;
    } elsif ( $cmd eq 'verbose' ) {
	$verbose = !$verbose;
	print 'verbosity ' . ($verbose ? "on." : "off.") . "\n\n";
	print '> ';
	return 1;
    } elsif ( $cmd =~ /^(clear|clr|reset)$/ ) {
	@propositions = ();
	%terms = ();
	print "Cleared.\n\n";
	print '> ';
	return 1;
    } elsif ( $cmd eq 'dump' ) {
	use Data::Dumper;
	print Data::Dumper->Dump( [\%terms, \@propositions],
				  [qw(terms propositions)] );
	print "\n> ";
	return 1;
    } elsif ( $cmd =~ /^(help|h|\?)$/ ) {
	print <<EOM;
help | h | ?         Display this help
verbose              Toggle verbosity on and off
clear | clr | reset  Clear internal state
dump                 Display internal state
quit | q             Quit

EOM
	print '> ';
        return 1;
    } else {
	return undef;
    }
}


print '> ';
while (defined( my $line = <STDIN> )) {
    chomp( $line );
    $line =~ s/^\s+//;
    $line =~ s/\s+$//;

    try_command($line) && next;

    my $is_query = is_query( $line );
    $line =~ s/[.?!]\s*$//; # strip off punctuation

    my( $quantifier, $subject, $copula, $predicate );
    if ( $is_query ) {
	($quantifier, $subject, $copula, $predicate) =
	    parse_query( $line );
    } else {
	($quantifier, $subject, $copula, $predicate) =
	    parse_proposition( $line );
	push @propositions, {
	  quantifier => $quantifier,
          subject    => $subject,
          copula     => $copula,
          predicate  => $predicate,
        };
	push @{$terms{$subject}}, $#propositions;
	push @{$terms{$predicate}}, $#propositions;
    }


    my $mood = determine_mood( $quantifier, $copula );


print <<"EOF" if $verbose;
quantifier : $quantifier
subject    : $subject
copula     : $copula
predicate  : $predicate
mood       : $mood
quality    : $prop_desc{$mood}{quality}
quantity   : $prop_desc{$mood}{quantity}
form       : $prop_desc{$mood}{form}
distributed terms  : @{$prop_desc{$mood}{distributed_terms}}

EOF

    if ( $is_query ) {
	is_valid_conclusion($subject, $predicate);
    }

  print '> ';
}

sub is_valid_conclusion {
    my $major = shift;
    my $minor = shift;

# * parse conclusions
# * use subject & predicate of conclusion to find candidate propositions
# * if two (& only two) candidates are found which share a common middle term,
#   determine the positions of each of the terms in order to determine figure.

}
