#!/usr/athena/bin/perl

use strict;
use warnings;

######################################################################
package Shape;

sub new {
# Subclasses should override this abstract method.
# Creates a new shape.
    die "Cannot call abstract method Shape->new()";
}

sub get_color {
# Returns the color of the shape.
# my $color = $shape->get_color();

    # Get object
    my $self = shift;

    # Return attribute
    return $self->{Color};
}

sub set_color {
# Sets the color of the shape.
# $shape->set_color($color);

    # Get object
    my $self = shift;

    # Get attributes
    my ($color) = @_;

    # Set attributes
    $self->{Color} = $color;
}


######################################################################
package Polygon;

# use base qw( Shape ); # if this package were in a different file
our @ISA = qw( Shape );

sub area {
# Subclasses should override this abstract method.
# Returns the area of the polygon.
# my $area = $polygon->area();
    die "Cannot call abstract method Polygon->area()";
}


######################################################################
package Triangle;

our @ISA = qw( Polygon );

sub new {
# Creates a new triangle.
# my $tri = Triangle->new($A);              # equilateral
# my $tri = Triangle->new($A, $B);          # isosceles; $C = $B
# my $tri = Triangle->new($A, $B, $C);      # scalene

    # Get class
    my $proto = shift;
    my $class = ref($proto) || $proto;            # Allow for object method

    # Get sides
    my ($A, $B, $C) = @_;
    $A = $proto->get_A() if ref($proto) and not defined $A;
    $B = $proto->get_B() if ref($proto) and not defined $B;
    $C = $proto->get_C() if ref($proto) and not defined $C;

    # Check sides and set defaults
    die "Must provide at least one side" unless defined $A and $A > 0;
    $B = $A unless defined $B and $B > 0;
    $C = $B unless defined $C and $C > 0;
    my ($x, $y, $max) = sort {$a <=> $b} ($A, $B, $C);
    die "Impossible triangle ($A, $B, $C)"
	if ($x + $y < $max);

    # Create object
    my $self = {
		A => $A,
		B => $B,
		C => $C,
	       };
    bless $self, $class;

    return $self;
}

sub get_A {
# Returns the appropriate side of the triangle.
# my $side = $shape->get_A();
    # Get object
    my $self = shift;
    # Return attribute
    return $self->{A};
}

sub get_B {
# Returns the appropriate side of the triangle.
# my $side = $shape->get_B();
    # Get object
    my $self = shift;
    # Return attribute
    return $self->{B};
}

sub get_C {
# Returns the appropriate side of the triangle.
# my $side = $shape->get_C();
    # Get object
    my $self = shift;
    # Return attribute
    return $self->{C};
}

sub perimeter {
# Returns the perimeter of the triangle.
# my $area = $tri->perimeter();
    # Get object
    my $self = shift;
    # Calculate value
    my $perimeter = $self->get_A() + $self->get_B() + $self->get_C();
    # Return value
    return $perimeter;
}

sub area {
# Returns the area of the triangle.
# my $area = $tri->area();
    # Get object
    my $self = shift;
    # Calculate value (using Heron's Method)
    my $s = $self->perimeter() / 2;
    my $area = sqrt($s *
		    ($s - $self->get_A()) *
		    ($s - $self->get_B()) *
		    ($s - $self->get_C()));
    # Return value
    return $area;
}


######################################################################
package Quadrilateral;

our @ISA = qw( Polygon );

sub new {
# Creates a new quadrilateral.
# my $quad = Quadrilateral->new($A);              # equilateral
# my $quad = Quadrilateral->new($A, $B);          # $C = $A; $D = $B
# my $quad = Quadrilateral->new($A, $B, $C);      # $D = $B
# my $quad = Quadrilateral->new($A, $B, $C, $D);

    # Get class
    my $proto = shift;
    my $class = ref($proto) || $proto;            # Allow for object method

    # Get sides
    my ($A, $B, $C, $D) = @_;
    $A = $proto->get_A() if ref($proto) and not defined $A;
    $B = $proto->get_B() if ref($proto) and not defined $B;
    $C = $proto->get_C() if ref($proto) and not defined $C;
    $D = $proto->get_D() if ref($proto) and not defined $D;

    # Check sides and set defaults
    die "Must provide at least one side" unless defined $A and $A > 0;
    $B = $A unless defined $B and $B > 0;
    $C = $A unless defined $C and $C > 0;
    $D = $B unless defined $D and $D > 0;
    my ($x, $y, $z, $max) = sort {$a <=> $b} ($A, $B, $C, $D);
    die "Impossible quadrilateral ($A, $B, $C, $D)"
	if ($x + $y + $z < $max);

    # Create object
    my $self = {
		A => $A,
		B => $B,
		C => $C,
		D => $D,
	       };
    bless $self, $class;

    return $self;
}

sub get_A {
# Returns the appropriate side of the quadrilateral.
# my $side = $shape->get_A();
    # Get object
    my $self = shift;
    # Return attribute
    return $self->{A};
}

sub get_B {
# Returns the appropriate side of the quadrilateral.
# my $side = $shape->get_B();
    # Get object
    my $self = shift;
    # Return attribute
    return $self->{B};
}

sub get_C {
# Returns the appropriate side of the quadrilateral.
# my $side = $shape->get_C();
    # Get object
    my $self = shift;
    # Return attribute
    return $self->{C};
}

sub get_D {
# Returns the appropriate side of the quadrilateral.
# my $side = $shape->get_D();
    # Get object
    my $self = shift;
    # Return attribute
    return $self->{D};
}

sub perimeter {
# Returns the perimeter of the quadrilateral.
# my $area = $quad->perimeter();
    # Get object
    my $self = shift;
    # Calculate value
    my $perimeter = $self->get_A() + $self->get_B() +
	            $self->get_C() + $self->get_D();
    # Return value
    return $perimeter;
}

sub area {
# Returns the area of the quadrilateral.
# my $area = $quad->area();
    die "I didn't want to do the math for this";
}


######################################################################
package Parallelogram;

our @ISA = qw( Quadrilateral );

sub area {
# Returns the area of the parallelogram.
# my $area = $para->area();
    die "I didn't want to do the math for this";
}


######################################################################

package Rectangle;

our @ISA = qw( Parallelogram );

sub new {
# Creates a new rectangle.
# my $rect = Rectangle->new($A);                  # equilateral
# my $rect = Rectangle->new($A, $B);

    # Get class
    my $proto = shift;
    my $class = ref($proto) || $proto;            # Allow for object method

    # Get sides
    my ($A, $B) = @_;

    # Create object
    my $self = $proto->SUPER::new($A, $B);
    bless $self, $class;

    return $self;
}

sub area {
# Returns the area of the rectangle.
# my $area = $rect->area();

    # Get object
    my $self = shift;

    # Get area
    my $area = $self->get_A() * $self->get_B();

    # Return attribute
    return ($area);
}


######################################################################

package Rhombus;

our @ISA = qw( Parallelogram );

sub new {
# Creates a new rhombus.
# my $rhombus = Rhombus->new($a);                  # equilateral

    # Get class
    my $proto = shift;
    my $class = ref($proto) || $proto;            # Allow for object method

    # Get sides
    my ($A) = @_;

    # Create object
    my $self = $proto->SUPER::new($A);
    bless $self, $class;

    return $self;
}

sub area {
# Returns the area of the rectangle.
# my $area = $rect->area();

    # Get object
    my $self = shift;

    # Get area
    die "Don't know angle yet";
    my $area;

    # Return attribute
    return ($area);
}


######################################################################

package Square;

our @ISA = qw( Rectangle Rhombus );

sub new {
# Creates a new square.
# my $square = Square->new($a);

    # Get class
    my $proto = shift;
    my $class = ref($proto) || $proto;            # Allow for object method

    # Get sides
    my ($A) = @_;

    # Create object
    my $self = $proto->SUPER::new($A);
    bless $self, $class;

    return $self;
}

sub area {
# Returns the area of the square.
# my $area = $square->area();

    # Get object
    my $self = shift;

    # Get area
    my $area = $self->get_A() ** 2;

    # Return attribute
    return ($area);
}


######################################################################

1;
