use v6-alpha;

class Unit {
    has $.q   is rw; 

    method set    ($self: Num $value) { $.q = $value; $self; };
    multi method string ($self:) {$self.q ~ $self.abbreviation; } 
    multi method string ($self: Num $prec) {
        return $self.q.fmt('%.' ~ $prec ~ 'f') ~ $self.abbreviation;
    }

    method toBase($self:)     { $.q * $self.baseFactor }
    method fromBase($self:)   { $.q / $self.baseFactor }
   
    method setFromBase($self: Num $value) { $.q = $value / $self.baseFactor; }    
    method baseFactor() { 1; }       
}

multi sub *infix:<+>   (Unit $a, Unit $b) {
   die unless $a.type eq $b.type;
   my $new = $a.clone();
   # wierd hack around a wierd error.
   my $aVal = $a.toBase();
   my $bVal = $b.toBase();
   $new.setFromBase( $aVal + $bVal);
   return $new;
}

multi sub *infix:<->   (Unit $a, Unit $b) {
   my $new = $a.clone();
   # wierd hack around a wierd error.
   my $aVal = $a.toBase();
   my $bVal = $b.toBase();
   $new.setFromBase( $aVal - $bVal);
   return $new;
} 

class Distance is Unit { method type () { 'distance'} }

multi sub postfix:<ft> (Num $value) { Feet.new().set($value)  }
class Feet is Distance {
    method abbreviation () { "ft" };
    method baseFactor   () { 0.3048 };
}

# not m, as that's taken by m//
multi sub postfix:<m> (Num $value) { Meter.new().set($value)  }
class Meter is Distance {
    method abbreviation () { "m" };
    # no base factor because it IS the base Unit
}

multi sub postfix:<km> (Num $value) { Kilometer.new().set($value)  }
class Kilometer is Distance {
    method abbreviation () { "km" };
    method baseFactor    () { 1000 };
}

multi sub prefix:<~> (Unit $unit)   { $unit.string; }
#multi sub infix:<`>  (Int $value, Unit $unit) { $unit.set($value); };

my $feet  = 5ft;
my $meter = 6m;
say $feet.string();
say $meter.string();
# my $z = $x.to($y);
# say $z.string;

my $add = $feet + $meter;
say $add.string(2);

my $add2 = $meter + $feet;
say ~$add2;

my $sub = $meter - $feet;
say ~$sub;

my $kilometer = 1000km;
$add = $kilometer + 90ft; 
say $add.string(2);

my $add = 1ft + 1m;
say $add.string(2);

