use strict;
use warnings;
package Time::C;
$Time::C::VERSION = '0.001'; # TRIAL
# ABSTRACT: Convenient time manipulation.
use Time::Moment;
use Time::C::Val;
use Scalar::Util qw/ weaken /;

sub _build {
    my $meth = shift;
    my $class = shift;
    my $tm = Time::Moment->$meth(@_);

    bless { tm => $tm }, $class;
}

my @accessors = (qw/ year month week hour minute second millisecond microsecond nanosecond /);
my @new_accs = (qw/ epoch jd mjd rd /);
my @day_accs = (qw/ day_of_month day_of_week day_of_year day_of_quarter /);

sub _acc :lvalue {
    my $val = shift;
    my $self = shift;
    $self->_apply();

    $self->{$val} //= Time::C::Val->new(val => $self->{tm}->$val(), self => $self, acc => $val);
    $self->{$val};
}

sub _new_acc :lvalue {
    my $val = shift;
    my $self = shift;
    $self->_apply();

    $self->{$val} = $self->{tm}->$val();
    $self->{$val};
}

sub _day_acc :lvalue {
    my $val = shift;
    my $self = shift;
    $self->_apply();

    $self->{$val} //= Time::C::Val->new(val => $self->{tm}->$val(), self => $self, acc => $val, meth => 'day');
    $self->{$val};
}

sub _apply {
    my $self = shift;
    foreach my $acc (grep { exists $self->{$_} } @accessors, @day_accs, 'offset') {
        my $meth = "with_$acc";
        $self->{tm} = $self->{tm}->$meth(delete $self->{$acc});
    }
    foreach my $acc (grep { exists $self->{$_} } @new_accs, 'string') {
        my $meth = "from_$acc";
        $self->{tm} = Time::Moment->$meth($self->{$_});
    }
}

# constructors

for my $const (qw/ new now now_utc from_epoch from_object from_string from_rd from_jd from_mjd /) {
    no strict 'refs';
    *$const = sub { _build($const, @_); };
}

# accessors

for my $acc (@accessors) {
    no strict 'refs';
    *$acc = sub :lvalue { _acc($acc, @_); };
}

for my $acc (@new_accs) {
    no strict 'refs';
    *$acc = sub :lvalue { _new_acc($acc, @_); };
}

for my $acc (@day_accs) {
    no strict 'refs';
    *$acc = sub :lvalue { _day_acc($acc, @_); };
}

sub string :lvalue {
    my $self = shift;
    $self->_apply();

    $self->{string} //= "$self->{tm}";
    $self->{string};
}

sub tm :lvalue {
    my $self = shift;
    $self->_apply();

    $self->{tm};
}

sub day :lvalue {
    shift->day_of_month();
}

sub offset :lvalue {
    my $self = shift;
    $self->_apply();

    $self->{offset} //= $self->{tm}->offset();
    $self->{offset};
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Time::C - Convenient time manipulation.

=head1 VERSION

version 0.001

=head1 SYNOPSIS

  use Time::C;

  my $t = Time::C->from_string('2016-09-23T04:28:30Z');

  # 2016-01-01T04:28:30Z
  $t->month = $t->day = 1;

  # 2016-01-01T00:00:00Z
  $t->hour = $t->minute = $t->second = 0;

  # 2016-02-04T00:00:00Z
  $t->month += 1; $t->day += 3;

  # 2016-03-03T00:00:00Z
  $t->day += 28;

=head1 DESCRIPTION

Makes manipulating time structures more convenient. Internally uses L<Time::Moment>.

=head1 CONSTRUCTORS

All the constructors simply pass along their arguments to the constructor with the same name from L<Time::Moment>.

=over

=item new

See L<Time::Moment/new>.

=item now

See L<Time::Moment/now>.

=item now_utc

See L<Time::Moment/now_utc>.

=item from_epoch

See L<Time::Moment/from_epoch>.

=item from_object

See L<Time::Moment/from_object>.

=item from_string

See L<Time::Moment/from_string>.

=item from_rd

See L<Time::Moment/from_rd>.

=item from_jd

See L<Time::Moment/from_jd>.

=item from_mjd

See L<Time::Moment/from_mjd>.

=back

=head1 ACCESSORS

These accessors will work as C<LVALUE>s, meaning you can assign to them to change the time being represented. The actual underlying L<Time::Moment> object won't be replaced until the next time you try to access a value.

=over

=item year

See L<Time::Moment/year>, L<Time::Moment/with_year>, L<Time::Moment/plus_years>, L<Time::Moment/minus_years>.

=item month

See L<Time::Moment/month>, L<Time::Moment/with_month>, L<Time::Moment/plus_months>, L<Time::Moment/minus_months>.

=item week

See L<Time::Moment/week>, L<Time::Moment/with_week>, L<Time::Moment/plus_weeks>, L<Time::Moment/minus_weeks>.

=item day

=item day_of_month

See L<Time::Moment/day_of_month>, L<Time::Moment/with_day_of_month>, L<Time::Moment/plus_days>, L<Time::Moment/minus_days>.

=item day_of_year

See L<Time::Moment/day_of_year>, L<Time::Moment/with_day_of_year>, L<Time::Moment/plus_days>, L<Time::Moment/minus_days>.

=item day_of_quarter

See L<Time::Moment/day_of_quarter>, L<Time::Moment/with_day_of_quarter>, L<Time::Moment/plus_days>, L<Time::Moment/minus_days>.

=item day_of_week

See L<Time::Moment/day_of_week>, L<Time::Moment/with_day_of_week>, L<Time::Moment/plus_days>, L<Time::Moment/minus_days>.

=item hour

See L<Time::Moment/hour>, L<Time::Moment/with_hour>, L<Time::Moment/plus_hours>, L<Time::Moment/minus_hours>.

=item minute

See L<Time::Moment/minute>, L<Time::Moment/with_minute>, L<Time::Moment/plus_minutes>, L<Time::Moment/minus_minutes>.

=item second

See L<Time::Moment/second>, L<Time::Moment/with_second>, L<Time::Moment/plus_seconds>, L<Time::Moment/minus_seconds>.

=item millisecond

See L<Time::Moment/millisecond>, L<Time::Moment/with_millisecond>, L<Time::Moment/plus_milliseconds>, L<Time::Moment/minus_milliseconds>.

=item microsecond

See L<Time::Moment/microsecond>, L<Time::Moment/with_microsecond>, L<Time::Moment/plus_microseconds>, L<Time::Moment/minus_microseconds>.

=item nanosecond

See L<Time::Moment/nanosecond>, L<Time::Moment/with_nanosecond>, L<Time::Moment/plus_nanoseconds>, L<Time::Moment/minus_nanoseconds>.

=item offset

See L<Time::Moment/offset>, L<Time::Moment/with_offset>.

=item string

See L<Time::Moment/stringification>, L<Time::Moment/from_string>.

=item epoch

See L<Time::Moment/epoch>, L<Time::Moment/from_epoch>.

=item rd

See L<Time::Moment/rd>, L<Time::Moment/from_rd>.

=item jd

See L<Time::Moment/jd>, L<Time::Moment/from_jd>.

=item mjd

See L<Time::Moment/mjd>, L<Time::Moment/from_mjd>.

=item tm

This returns the current internal Time::Moment object. This becomes a new object every time something is changed.

=back

=head1 SEE ALSO

=over

=item L<Time::Moment>

=back

=head1 AUTHOR

Andreas Guldstrand <andreas.guldstrand@gmail.com>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2016 by Andreas Guldstrand.

This is free software, licensed under:

  The MIT (X11) License

=cut
