# This module is part of da_reference, and is released under the terms
# of the GPL version 2, or any later version, at your option. See the
# file README and COPYING for more information.

# Copyright 2003, 2004 by Don Armstrong <don@donarmstrong.com>.
# $Id: Author.pm 42 2009-03-20 06:29:46Z don $

package Reference::Field::Author;

=head1 NAME

Reference::Field::Author --

=head1 SYNOPSIS


=head1 DESCRIPTION


=head1 TODO

XXX Allow the corresponding author to be set explicitely

XXX To do this, we need to break away from using the author field as
an arrayref, and instead use a hashref with the author fields, and a
specific corresponding author setting. [This should probaly be de
riguer for other fields as well.]

=head1 BUGS

None known.

=cut


use strict;
use vars qw($REVISION $DEBUG);

use NEXT;
use Params::Validate qw(:types validate_with);

BEGIN{
     ($REVISION) = q$LastChangedRevision: 42 $ =~ /\$LastChangedRevision:\s+([^\s+])/;
     $DEBUG = 0 unless defined $DEBUG;
}


=head2 author

=head3 Usage



=head3 Function

=head3 Returns

=head3 Args

=cut

sub author{
     my $self = shift;
     my %params;
     if (scalar(@_) == 1) {
	  $params{author} = shift;
	  $params{output} = 'scalar';
	  $params{add_author} = 0;
	  $params{del_author} = 0;
     }
     else {
	  %params = validate_with(params => \@_,
				  spec   => {author => {type     => ARRAYREF|SCALAR|HASHREF,
							optional => 1,
						       },
					     add_author => {type    => BOOLEAN,
							    default => 0,
							   },
					     del_author => {type    => BOOLEAN,
							    default => 0,
							   },
					     output => {default => 'scalar',
							type    => SCALAR,
						       },
					    },
				 );
     }
     # Update author according to the passed information
     if (defined $params{author}) {
	       $self->{reference}->{author} = {authors              => [],
					       first_author         => 0,
					       corresponding_author => -1,
					      } unless $params{add_author};
	  # We can't handle things like Smith, Jones, Paul, Rue; for
	  # obvious reasons. If you must do something so broken, you
	  # have to go Smith, Jones; Paul, Rue; or Smith, Jones and
	  # Paul, Rue.
	  if (not ref($params{author}) and ($params{author} =~ /\sand\,?\s/ or $params{author} =~ /\w\;\s/)) {
	       $params{author} = [grep /\w/, split (/\s*and,?\s*|\;\s*/,$params{author})];
	  }
	  $params{author} = [$params{author}] unless ref($params{author}) eq 'ARRAY';
	  foreach my $author (@{$params{author}}) {
	       my $author_info = _parse_author($author);
	       if (not $params{del_author}) {
		    push @{$self->{reference}{author}{authors}},$author_info;
	       }
	       else {
		    _delete_author($author_info,$author->{reference}{author}{authors});
	       }
	  }
     }

     local $_ = $params{output};
     if (/bibtex/) {
	  return join(' and ',map {$_->{full}} @{$self->{reference}{author}{authors}});
     }
      else {
	   return join(', ',map {$_->{full}} @{$self->{reference}{author}{authors}});
     }

}

=head2 corresponding_author

     my $corresponding_author = $ref->corresponding_author;

Returns the corresponding author (the last author listed.)

=cut

sub corresponding_author{
     my $self = shift;

     my %params = validate_with(params => \@_,
				spec   => {output => {default => 'scalar',
						      type    => SCALAR,
						     },
					  },
			       );
     local $_ = $params{output};
     if (/bibtex/) {
	  return $self->{reference}{author}{authors}[$self->{reference}{author}{corresponding_author}]{full};
     }
     elsif (/last/) {
	 return $self->{reference}{author}{authors}[$self->{reference}{author}{corresponding_author}]{last};
     }
     else {
	  return $self->{reference}{author}{authors}[$self->{reference}{author}{corresponding_author}]{full};
     }
}

=head2 first_author

     my $first_author = $ref->first_author;

Returns the first author (primary author.)

=cut

sub first_author{
     my $self = shift;
     my %params = validate_with(params => \@_,
				spec   => {output => {default => 'scalar',
						      type    => SCALAR,
						     },
					  },
			       );
     local $_ = $params{output};
     if (/bibtex/) {
	  return $self->{reference}{author}{authors}[$self->{reference}{author}{first_author}]{full} || '';
     }
     elsif (/last/) {
	 return $self->{reference}{author}{authors}[$self->{reference}{author}{first_author}]{last};
     }
     else {
	  return $self->{reference}{author}{authors}[$self->{reference}{author}{first_author}]{full} || '';
     }
}


=head2 _parse_author

     my $author_info = _parse_author($author);

Parses the author and returns an author record.

Author record

The author can be specified in a few different ways:

=over

=item SCALAR Author Name

=over

=item SMITH John W.

=item Smith JW

=item John W. Smith

=item John Wilkenson Smith

=item HASHREF Author structure

=item ARRAYREF Author Name

=back

In these cases, the author's name should be parsed appropriately. [XXX
Needs to be extended to handle Smith, John W. appropriately.]


=cut

sub _parse_author($){
     my ($author) = @_;

     warn "Undefined author" and return undef if not defined $author;

     # the author information
     my %au = ();
     if (not ref($author)) {
	  # UGH. Try to figure out the author.
	  if ($author =~ /^\s*([A-Z]+)\s+([\w\s]+?)(?:\s*([A-Z])\.)?\s*$/) { # SMITH John W.
	       $au{first} = ucfirst(lc($2)) || '';
	       $au{last} = ucfirst(lc($1)) || '';
	       $au{middle} = $3 || '';
	       $au{initials} = (($au{first} ne '')?uc(substr($au{first},0,1)):'') . 
		    (($au{middle} ne '')?uc(substr($au{middle},0,1)):'');
	       $au{full} = join(' ',grep /\w/,($au{first},$au{middle},$au{last}));
	  }
	  elsif ($author =~ /^\s*([A-Z][\w\s]*?)\s+([A-Z]+)\s*$/) { # Smith JW
	       $au{first} = $2 || '';
	       $au{middle} = '';
	       if (length $au{first} > 1) {
		    $au{middle} = substr($au{first},1);
		    $au{first} = substr($au{first},0,1);
	       }
	       $au{last} = $1;
	       $au{initials} = $2;
	       $au{full} = join(' ',grep /\w/, ($au{first},$au{middle},$au{last}))
	  }
	  elsif ($author =~ /^\s*([A-Z][\w]*?)\s+(?:([A-Z])\.|([A-Z][\w\s]*?)\s+)?\s*([A-Z][\w]*)\s*$/){ # John W. Smith or John Wilkinson Smith
	       $au{first} = $1;
	       $au{middle} = $2 || $3 || '';
	       $au{last} = $4;
	       $au{full} = join(' ',grep /\w/, ($au{first},$au{middle},$au{last}));
	  }
	  # XXX Handle Smith, John Wilkinson; Smith, JW; and Smith, John W.
	  else {
	       warn "Couldn't handle author $author";
	       $au{full} = $author;
	  }
     }
     elsif (ref $author eq 'ARRAY') {
	  warn "Author was empty" unless scalar @{$author};
	  $au{full} = join(' ',grep /\w/, @{$author});
	  $au{last} = $author->[-1];
	  $au{first} = $author->[0] if scalar @{$author} > 1;
	  $au{middle} = join(' ',grep /\w/,@{$author}[1..($#{$author}-1)]) if scalar @{$author} > 2;
	  $au{initials} = (($au{first} ne '')?uc(substr($au{first},0,1)):'') .
	       (($au{middle} ne '')?uc(substr($au{middle},0,1)):'');
     }
     elsif (ref $author eq 'HASH') {
	  foreach my $key (qw(full last middle first initials)) {
	       $au{$key} = '';
	       $au{$key} = $author->{$key} if exists $author->{$key} and defined $author->{$key};
	  }
	  $au{full} = join(' ',grep /\w/, ($au{first},$au{middle},$au{last})) if $au{full} eq '';
	  $au{initials} = (($au{first} ne '')?uc(substr($au{first},0,1)):'') .
	       (($au{middle} ne '')?uc(substr($au{middle},0,1)):'') if $au{initials} eq '';
     }
     else {
	  warn "Unknown reference: $author";
	  return undef;
     }
     return \%au;
}

=head2 _delete_author

     


XXX NOT IMPLEMENTED

=cut

sub _delete_author($$){
     my ($author_info,$author_list) = @_;

     die "NOT IMPLEMENTED";
}


=head2 _init

Called by Reference's new function

Call superclass's _init function [C<$self->NEXT::_init>], sets up the
author list reference.

=cut

sub _init{
     my $self = shift;

     $self->{reference}->{author} = {authors => [],
				     first_author => 0,
				     corresponding_author => -1,
				    };

     $self->NEXT::_init;

}



1;


__END__






