# Copyright (c) 2023 Yuki Kimoto
# MIT License

class Time::Seconds {
  version_from Time::Piece;
  
  allow Time::Piece;
  
  use StringBuffer;
  
  # Interfaces
  interface Cloneable;
  
  # Fields
  has seconds : ro long;
  
  # Class Methods
  static method new : Time::Seconds ($second : long = 0) {
    
    my $self = new Time::Seconds;
    
    $self->{seconds} = $second;
    
    return $self;
  }
  
  static method ONE_MINUTE : Time::Seconds() { return &new(60); }
  
  static method ONE_HOUR : Time::Seconds () { return &new(3_600); }
  
  static method ONE_DAY : Time::Seconds () { return &new(86_400); }
  
  static method ONE_WEEK : Time::Seconds () { return &new(604_800); }
  
  static method ONE_MONTH : Time::Seconds () { return &new(2_629_744); } # ONE_YEAR / 12
  
  static method ONE_YEAR : Time::Seconds () { return &new(31_556_930); } # 365.24225 days
  
  static method ONE_FINANCIAL_MONTH : Time::Seconds () { return &new(2_592_000); } # 30 days
  
  static method LEAP_YEAR : Time::Seconds () { return &new(31_622_400); } # 366 * ONE_DAY
  
  static method NON_LEAP_YEAR : Time::Seconds () { return &new(31_536_000); } # 365 * ONE_DAY
  
  # Instance Methods
  method add : Time::Seconds ($seconds : long) {
    
    my $new_seconds = $self->{seconds} + $seconds;
    
    return Time::Seconds->new($new_seconds);
  }
  
  method subtract : Time::Seconds ($seconds : long) {
    
    my $new_seconds = $self->{seconds} - $seconds;
    
    return Time::Seconds->new($new_seconds);
  }
  
  method minutes : double () {
    return $self->{seconds} / 60.0;
  }
  
  method hours : double () {
    return $self->minutes / 60.0;
  }
   
  method days : double () {
    return $self->hours / 24.0;
  }
   
  method weeks : double () {
    return $self->days / 7.0;
  }
   
  method months : double () {
    return $self->days / 30.4368541;
  }
   
  method financial_months : double () {
    return $self->days / 30.0;
  }
  
  method years : double () {
    return $self->days / 365.24225;
  }
  
  method clone : Time::Seconds () {
    return Time::Seconds->new($self->{seconds});
  }
  
  method pretty : string () {
    my $tsec_clone = $self->clone;
    
    my $buffer = StringBuffer->new;
    
    if ($tsec_clone->{seconds} < 0) {
      $tsec_clone->{seconds} = -$tsec_clone->{seconds};
      $buffer->push("minus ");
    }
    if ($tsec_clone->{seconds} >= &ONE_MINUTE->{seconds}) {
      if ($tsec_clone->{seconds} >= &ONE_HOUR->{seconds}) {
        if ($tsec_clone->{seconds} >= &ONE_DAY->{seconds}) {
          my $days_floor = (int)$tsec_clone->days;
          $buffer->push($days_floor);
          
          $buffer->push(" day");
          
          unless ($days_floor == 1) {
            $buffer->push("s");
          }
          $buffer->push(", ");
          
          $tsec_clone->{seconds} -= ($days_floor * &ONE_DAY->{seconds});
        }
        my $hours_floor = (int)$tsec_clone->hours;
        $buffer->push($hours_floor);
        
        $buffer->push(" hour");
        
        unless ($hours_floor == 1) {
          $buffer->push("s");
        }
        $buffer->push(", ");
        $tsec_clone->{seconds} -= ($hours_floor * &ONE_HOUR->{seconds});
      }
      my $mins_floor = (int)$tsec_clone->minutes;
      $buffer->push($mins_floor);
      
      $buffer->push(" minute");
      
      unless ($mins_floor == 1) {
        $buffer->push("s");
      }
      $buffer->push(", ");
      $tsec_clone->{seconds} -= ($mins_floor * &ONE_MINUTE->{seconds});
    }
    
    $buffer->push($tsec_clone->{seconds});
    
    $buffer->push(" second");
    
    unless ($tsec_clone->{seconds} == 1) {
      $buffer->push("s");
    }
    
    my $string = $buffer->to_string;
    
    return $string;
  }
  
}