# Calculator.pm
#
# Copyright (c) 2002 Cunningham & Cunningham, Inc.
# Released under the terms of the GNU General Public License version 2 or later.
#
# Perl translation by Dave W. Smith <dws@postcognitive.com>

package Calculator;

use base qw(ColumnFixture);

sub new {
    my $pkg = shift;

    return $pkg->SUPER::new(hp => new HP35(),
                            key => undef,
                            @_);
}

sub key { return $_[0]->{'key'} }

sub hp { return $_[0]->{'hp'} }

sub points { return 0 }

sub flash { return 0 }

sub watts { return 0.5 }

sub reset {
    my $self = shift;

    $self->{'key'} = undef;
}

sub execute {
    my $self = shift;

    if ( defined($self->key()) ) {
	$self->hp()->key($self->key());
    }
}

sub x {
    my $self = shift;

    return $self->hp()->{'rh'}->[0];
}


sub y {
    my $self = shift;

    return $self->hp()->{'rh'}->[1];
}

sub z {
    my $self = shift;

    return $self->hp()->{'rh'}->[2];
}

sub t {
    my $self = shift;

    return $self->hp()->{'rh'}->[3];
}




package HP35;

my $D2R = (4.0 * CORE::atan2(1, 1)) / 180.0;


sub new {
    my $pkg = shift;

    bless { rh => [0.0, 0.0, 0.0, 0.0], s => 0.0 }, $pkg;
}

sub key {
    my $self = shift;
    my($key) = @_;

    if ( $key =~ m/^-?\d+(?:\.\d+)?$/ ) {
	$self->_push($key);
    }
    elsif ( $key eq "enter" ) {
	$self->_push();
    }
    elsif ( $key eq "+" ) {
	$self->_push($self->_pop() + $self->_pop());
    }
    elsif ( $key eq "-" ) {
	my $t = $self->_pop();
	$self->_push($self->_pop() - $t);
    }
    elsif ( $key eq "*" ) {
	$self->_push($self->_pop() * $self->_pop());
    }
    elsif ( $key eq "/" ) {			# HACK divide-by-zero catch
	my $t = $self->_pop();
        eval {
	    $self->_push($self->_pop() / $t);
        };
        $self->_push("Infinity") if $@;
    }
    elsif ( $key eq "x^y" ) {
	$self->_push(exp(log($self->_pop()) * $self->_pop()));
    }
    elsif ( $key eq "clx" ) {
	$self->{'rh'}->[0] = 0.0;
    }
    elsif ( $key eq "clr" ) {
	$self->{'rh'} = [0.0, 0.0, 0.0, 0.0];
    }
    elsif ( $key eq "chs" ) {
	$self->{'rh'}->[0] *= -1.0;
    }
    elsif ( $key eq "x<>y" ) {
	my $t = $self->{'rh'}->[0];
	$self->{'rh'}->[0] = $self->{'rh'}->[1];
	$self->{'rh'}->[1] = $t;
    }
    elsif ( $key eq "r!" ) {
	$self->{'rh'}->[3] = $self->_pop();
    }
    elsif ( $key eq "sto" ) {
	$self->{'s'} = $self->{'rh'}->[0];
    }
    elsif ( $key eq "rcl" ) {
	$self->_push($self->{'s'});
    }
    elsif ( $key eq "sqrt" ) {
	$self->_push(sqrt($self->_pop()));
    }
    elsif ( $key eq "ln" ) {
	$self->_push(log($self->_pop()));
    }
    elsif ( $key eq "sin" ) {
	$self->_push(sin($self->_pop() * $D2R));
    }
    elsif ( $key eq "cos" ) {
	$self->_push(cos($self->_pop() * $D2R));
    }
    elsif ( $key eq "tan" ) {
	my $t = $self->_pop();
	$self->_push(sin($t * $D2R)/cos($t * $D2R));
    }
    else {
	die "can't do key: $key\n";
    }
}

sub _push {
    my $self = shift;
    my($value) = @_;

    for ( my $i = 3; $i > 0 ; --$i ) {
	$self->{'rh'}->[$i] = $self->{'rh'}->[$i - 1];
    }
    $self->{'rh'}->[0] = $value if defined($value);
}

sub _pop {
    my $self = shift;

    my $result = $self->{'rh'}->[0];
    for ( my $i = 0 ; $i < 3 ; ++$i ) {
	$self->{'rh'}->[$i] = $self->{'rh'}->[$i + 1];
    }
    return $result;
}

1;
