use Renard::Incunabula::Common::Setup;
package Renard::Incunabula::MuPDF::mutool::DateObject;
# ABSTRACT: An object to store the date
$Renard::Incunabula::MuPDF::mutool::DateObject::VERSION = '0.004';
use Moo;
use Renard::Incunabula::Common::Types qw(Str HashRef InstanceOf);

use overload '""' => \&stringify, eq => \&_string_eq;

has string => (
	is => 'ro',
	isa => Str,
	required => 1,
);

has data => (
	is => 'lazy',
	isa => HashRef,
);

method _build_data() {
	my $date_string = $self->string;

	# § 3.8.3 Dates (pg. 160)
	# (D:YYYYMMDDHHmmSSOHH'mm')
	# where
	my $date_re = qr/
		(?<Prefix>D:)?
		(?<Year> \d{4} )        # YYYY is the year
		(?<Month> \d{2} )?      # MM is the month
		(?<Day> \d{2} )?        # DD is the day (01–31)
		(?<Hour> \d{2} )?       # HH is the hour (00–23)
		(?<Minute> \d{2} )?     # mm is the minute (00–59)
		(?<Second> \d{2} )?     # SS is the second (00–59)
		(?<TzOffset> [-+Z] )?   # O is the relationship of local time
		                        # to Universal Time (UT), denoted by
		                        # one of the characters +, −,
		                        # or Z (see below)
		(?<TzHourW>
			(?<TzHour> \d{2})
			'
		)? # HH followed by ' is the absolute
		   # value of the offset from UT in hours
		   # (00–23)
		(?<TzMinuteW>
			(?<TzMinute> \d{2})
			'
		)? # mm followed by ' is the absolute
		   # value of the offset from UT in
		   # minutes (00–59)
	/x;

	my $time = {};

	die "Not a date string" unless $date_string =~ $date_re;

	$time->{year} = $+{Year};
	$time->{month} = $+{Month} // '01';
	$time->{day} = $+{Day} // '01';

	$time->{hour} = $+{Hour} // '00';
	$time->{minute} = $+{Minute} // '00';
	$time->{second} = $+{Second} // '00';

	if( exists $+{TzOffset} ) {
		$time->{tz}{offset} = $+{TzOffset};
		$time->{tz}{hour} = $+{TzHour} // '00';
		$time->{tz}{minute} = $+{TzMinute} // '00';
	}

	$time;
}

method as_DateTime() :ReturnType(InstanceOf['DateTime']) {
	eval { require DateTime; 1 } or die "require DateTime";

	my $dt_hash = $self->data;
	my $dt_timezone = 'floating';
	if( exists $dt_hash->{tz} ) {
		if( $dt_hash->{tz}{offset} eq 'Z' ) {
			$dt_timezone = 'UTC'; # UTC
		} else {
			$dt_timezone = join '', (
				$dt_hash->{tz}{offset}, # ±
				$dt_hash->{tz}{hour},
				$dt_hash->{tz}{minute},
			);
		}
	}

	return DateTime->new(
		year => $dt_hash->{year},
		month => $dt_hash->{month},
		day => $dt_hash->{day},

		hour => $dt_hash->{hour},
		minute => $dt_hash->{minute},
		second => $dt_hash->{second},

		time_zone => $dt_timezone,
	);
}

method stringify() {
	my $dt_part = sprintf(
		"%4d-%02d-%02dT%02d:%02d:%02d",
			$self->data->{year},
			$self->data->{month},
			$self->data->{day},

			$self->data->{hour},
			$self->data->{minute},
			$self->data->{second},
	);

	my $tz_part = '';
	if( exists $self->data->{tz} ) {
		if( $self->data->{tz}{offset} eq 'Z' ) {
			$tz_part = 'Z'; # UTC
		} else {
			$tz_part = $self->data->{tz}{offset} # ±
				. $self->data->{tz}{hour}
				. ":"
				.  $self->data->{tz}{minute};
		}
	}


	$dt_part . $tz_part;
}

fun _string_eq($a, $b, $swap) {
	"$a" eq "$b";
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Renard::Incunabula::MuPDF::mutool::DateObject - An object to store the date

=head1 VERSION

version 0.004

=head1 EXTENDS

=over 4

=item * L<Moo::Object>

=back

=head1 ATTRIBUTES

=head2 string

A PDF date string in C<string> which are in the form:

  D:YYYYMMDDHHmmSSOHH'mm'

=head2 data

A C<HashRef> in the form

  Dict[
    year   => Str,   # YYYY
    month  => Str,   # MM: 01-12
    day    => Str,   # DD: 01-31

    hour   => Str,   # HH: 00-23
    minute => Str,   # mm: 00-59
    second => Str,   # SS: 00-59

    tz     => Dict[
      offset => Str, # O: /[-+Z]/
      hour   => Str, # HH': 00-59
      minute => Str, # mm': 00-59
    ],
  ]

=head1 METHODS

=head2 as_DateTime

  method as_DateTime() :ReturnType(InstanceOf['DateTime'])

Returns a L<DateTime> representation of the date.

=head2 stringify

  method stringify()

Returns a C<Str> representation of the date.

This follows the ISO 8601 format of

  YYYY-MM-DDThh:mm:ss±hh:mm

which includes the timezone (either as an offset C<±hh:mm> or as C<Z> for UTC)
and using a C<T> separator for the date and time.

=head1 AUTHOR

Project Renard

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2017 by Project Renard.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut