package Rose::HTML::Form::Field;

use strict;

use Carp();
use Scalar::Util();

use Rose::HTML::Util();

use Rose::HTML::Label;

use Rose::HTML::Object;
our @ISA = qw(Rose::HTML::Object);

use constant HTML_ERROR_SEP  => "<br>\n";
use constant XHTML_ERROR_SEP => "<br />\n";

our $VERSION = '0.013';

#our $Debug = 0;

use Rose::Object::MakeMethods::Generic
(
  scalar => 
  [
    qw(label description)
  ],

  boolean => [ qw(required is_cleared) ],
  boolean => [ trim_spaces => { default => 1 } ],

  'scalar --get_set_init' => 
  [
    qw(html_prefix html_suffix html_error_separator xhtml_error_separator) 
  ],
);

__PACKAGE__->add_valid_html_attrs(qw(
  name
  value
  onblur
  onfocus
  accesskey
  tabindex
));

sub parent_field
{
  my($self) = shift; 
  return Scalar::Util::weaken($self->{'parent_field'} = shift)  if(@_);
  return $self->{'parent_field'};
}

sub init_html_prefix { '' }
sub init_html_suffix { '' }

sub init_html_error_separator  { HTML_ERROR_SEP  }
sub init_xhtml_error_separator { XHTML_ERROR_SEP }

sub value
{
  my($self) = shift;

  if(@_)
  {
    return $self->input_value($self->html_attr('value', shift));
  }
  else
  { 
    return $self->html_attr('value');
  }
}

sub name
{
  my($self) = shift;

  if(@_)
  {
    return $self->html_attr('name', shift);
  }
  else
  {
    unless(defined $self->html_attr('name'))
    {
      return $self->field_name;
    }

    return $self->html_attr('name');
  }
}

sub field_name
{
  my($self) = shift;

  if(@_)
  {
    $self->{'field_name'} = shift;

    unless(defined $self->name)
    {
      $self->html_attr(name => $self->{'field_name'})
    }

    return $self->{'field_name'};
  }
  else
  {
    unless(defined $self->{'field_name'})
    {
      return $self->{'field_name'} = $self->html_attr('name');
    }

    return $self->{'field_name'};
  }
}

sub default_value
{
  my($self) = shift;

  if(@_)
  {
    $self->{'internal_value'} = undef;
    $self->{'output_value'} = undef;
    return $self->{'default_value'} = shift;
  }

  return $self->{'default_value'};
}

sub default { shift->default_value(@_) }

sub inflate_value { $_[1] }
sub deflate_value { $_[1] }

sub input_value
{
  my($self) = shift;

  if(@_)
  {
    $self->{'is_cleared'} = 0;
    $self->{'internal_value'} = undef;
    $self->{'output_value'} = undef;
    $self->{'error'} = undef;
    $self->{'input_value'} = shift;

    return $self->{'input_value'};
  }

  return undef  if($self->is_cleared);

  my $value = 
    (defined $self->{'input_value'}) ? $self->{'input_value'} :  
    $self->default_value;

  if(wantarray && ref $value eq 'ARRAY')
  {
    return @$value;
  }

  return $value;
}

sub input_value_filtered
{
  my($self) = shift;

  my $value = $self->input_value;

  $value = $self->input_prefilter($value);

  if(my $input_filter = $self->input_filter)
  {
    local $_ = $value;
    $value = $input_filter->($self, $value);
  }

  return $value;
}

sub internal_value
{
  my($self) = shift;

  Carp::croak "Cannot set the internal value.  Use input_value() instead."  if(@_);

  return undef  if($self->is_cleared);

  return $self->{'internal_value'}  if(defined $self->{'internal_value'});

  my $value = $self->input_value;

  my($using_default, $final_value);

  unless(defined $value)
  {
    $value = $self->default_value;
    $using_default++;
  }

  $value = $self->input_prefilter($value);

  if(my $input_filter = $self->input_filter)
  {
    local $_ = $value;
    $final_value = $input_filter->($self, $value);
  }
  else { $final_value = $value }

  $final_value = $self->inflate_value($final_value);

  $self->{'internal_value'} = $final_value  unless($using_default);

  if(wantarray && ref $final_value eq 'ARRAY')
  {
    return @$final_value;
  }

  return $final_value;
}

sub output_value
{
  my($self) = shift;

  Carp::croak "Cannot set the output value.  Use input_value() instead."  if(@_);

  return undef  if($self->is_cleared);

  return $self->{'output_value'}  if(defined $self->{'output_value'});

  my $value = $self->deflate_value(scalar $self->internal_value);

  if(my $output_filter = $self->output_filter)
  {
    local $_ = $value;
    $self->{'output_value'} = $output_filter->($self, $value);
  }
  else { $self->{'output_value'} = $value }

  if(wantarray && ref $self->{'output_value'} eq 'ARRAY')
  {
    return @{$self->{'output_value'}};
  }

  return $self->{'output_value'};
}

sub is_empty
{
  no warnings;
  return (shift->internal_value =~ /\S/) ? 0 : 1;
}

sub input_prefilter
{
  my($self, $value) = @_;

  return undef  unless(defined $value);

  for($value)
  {
    no warnings;

    if($self->trim_spaces)
    {
      s/^\s+//;
      s/\s+$//;
    }
  }

  return $value;
}

sub input_filter
{
  my($self) = shift;

  if(@_)
  {
    $self->{'internal_value'} = undef;
    $self->{'output_value'} = undef;
    return $self->{'input_filter'} = shift;
  }

  return $self->{'input_filter'};
}

sub output_filter
{
  my($self) = shift;

  if(@_)
  {
    $self->{'output_value'} = undef;
    return $self->{'output_filter'} = shift;
  }

  return $self->{'output_filter'};
}

sub filter
{
  my($self) = shift;

  if(@_)
  {
    my $filter = shift;
    $self->{'input_filter'}  = $filter;
    $self->{'output_filter'} = $filter;
  }

  my $input_filter = $self->{'input_filter'};

  if(ref $input_filter && $input_filter eq $self->output_filter)
  {
    return $input_filter;
  }

  return;
}

sub clear
{
  my($self) = shift;

  $self->value(undef);
  $self->error(undef);
  $self->is_cleared(1);
}

sub reset
{
  my($self) = shift;

  $self->input_value(undef);
  $self->error(undef);
  $self->is_cleared(0);
  return 1;
}

sub hidden_fields
{
  my($self) = shift;

  require Rose::HTML::Form::Field::Hidden; # Circular dependency... :-/

  return Rose::HTML::Form::Field::Hidden->new(
      name  => $self->html_attr('name'),
      value => $self->output_value);
}

sub hidden_field { shift->hidden_fields(@_) }

sub html_hidden_fields
{
  my($self) = shift;

  my @html;

  foreach my $field ($self->hidden_fields)
  {
    push(@html, $field->html_field);
  }

  return (wantarray) ? @html : join("\n", @html);
}

sub html_hidden_field { shift->html_hidden_fields(@_) }

sub xhtml_hidden_fields
{
  my($self) = shift;

  my @xhtml;

  foreach my $field ($self->hidden_fields)
  {
    push(@xhtml, $field->xhtml_field);
  }

  return (wantarray) ? @xhtml : join("\n", @xhtml);
}

sub xhtml_hidden_field { shift->xhtml_hidden_fields(@_) }

*html_field  = \&Rose::HTML::Object::html_tag;
*xhtml_field = \&Rose::HTML::Object::xhtml_tag;

sub html_tag  { shift->html_field(@_) }
sub xhtml_tag { shift->xhtml_field(@_) }

sub html
{
  my($self) = shift;

  my($field, $error);

  $field = $self->html_field;
  $error = $self->html_error;

  if($error)
  {
    return $field . $self->html_error_separator . $error;
  }

  return $field;
}

sub xhtml
{
  my($self) = shift;

  my($field, $error);

  $field = $self->xhtml_field;
  $error = $self->xhtml_error;

  if($error)
  {
    return $field . $self->xhtml_error_separator . $error;
  }

  return $field;
}

sub label_object
{
  my($self) = shift;
  my $label = Rose::HTML::Label->new();

  $label->contents($self->escape_html ? __escape_html($self->label) : 
                                        $self->label);

  if($self->html_attr_exists('id'))
  {
    $label->for($self->html_attr('id'));
  }

  if(@_)
  {
    my %args = @_;

    while(my($k, $v) = each(%args))
    {
      $label->html_attr($k => $v);
    }
  }

  return $label;
}

sub html_label
{
  my($self) = shift;
  return ''  unless(length $self->label);
  return $self->label_object(($self->required ? (class => 'required') : ()), @_)->html_tag;
}

sub xhtml_label
{
  my($self) = shift;
  return ''  unless(length $self->label);
  return $self->label_object(($self->required ? (class => 'required') : ()), @_)->xhtml_tag;
}

sub validate
{
  my($self) = shift;

  $self->error(undef);

  my $value = $self->internal_value;

  if($self->required && 
     ((!ref $value && (!defined $value || ($self->trim_spaces && $value !~ /\S/))) ||
      (ref $value eq 'ARRAY' && !@$value)))
  {
    my $label = $self->label;
    $label = 'This'  unless(defined $label);
    $self->error("$label is a required field");
    return 0;
  }

  my $code = $self->validator;

  if($code)
  {
    local $_ = $value;
    #$Debug && warn "running $code->($self)\n";
    my $ok = $code->($self);

    if(!$ok && !defined $self->error)
    {
      my $label = $self->label;
      $label = 'Value'  unless(defined $label);
      $self->error("$label is invalid");
    }

    return $ok;
  }

  return 1;
}

sub validator
{
  my($self) = shift;

  if(@_)
  {
    my $code = shift;

    if(ref $code eq 'CODE')
    {
      return $self->{'validator'} = $code;
    }
    else
    {
      Carp::croak ref($self), "::validator() - argument must be a code reference";
    }
  }

  return $self->{'validator'};
}

*__escape_html = \&Rose::HTML::Util::escape_html;

1;

__END__

=head1 NAME

Rose::HTML::Form::Field - HTML form field base class.

=head1 SYNOPSIS

    package MyField;

    use Rose::HTML::Form::Field;
    our @ISA = qw(Rose::HTML::Form::Field);
    ...

    my $f = MyField->new(name => 'test', label => 'Test');

    print $f->html_field;
    print $f->xhtml_field;

    $f->input_value('hello world');

    $i = $f->internal_value;

    print $f->output_value;
    ...

=head1 DESCRIPTION

C<Rose::HTML::Form::Field> is the base class for field objects used in an HTML
form.  It defines a generic interface for field input, output, validation, and
filtering.

This class inherits from, and follows the conventions of,
C<Rose::HTML::Object>. Inherited methods that are not overridden will not be
documented a second time here.  See the C<Rose::HTML::Object> documentation
for more information.

=head1 OVERVIEW

A field object provides an interface for a logical field in an HTML form. 
Although it may serialize to multiple HTML tags, the field object itself is a
single, logical entity.

C<Rose::HTML::Form::Field> is the base class for field objects.   Since the
field object will eventually be asked to serialize itself as HTML,
C<Rose::HTML::Form::Field> inherits from C<Rose::HTML::Object>.  That defines
a lot of a field object's interface, leaving only the field-specific functions
to C<Rose::HTML::Form::Field> itself.

The most important function of a field object is to accept and return user
input.  C<Rose::HTML::Form::Field> defines a data flow for field values with
several different hooks and callbacks along the way:

                        +------------+
                       / user input /
                      +------------+
                             |
                             V
                    +------------------+
             set -->.                  .
                    .   input_value    .   input_value()
             get <--.                  .
                    +------------------+
                             |
                             V
                    +------------------+
          toggle -->| input_prefilter  |   trim_spaces()
                    +------------------+
                             |
                             V
                    +------------------+
         define <-->|   input_filter   |   input_filter()
                    +------------------+
                             |
                             V
                  +----------------------+
                  .                      .
           get <--. input_value_filtered . input_value_filtered()
                  .                      .
                  +----------------------+
                             |
                             V
                    +------------------+
                    |   inflate_value  |   (override in subclass)
                    +------------------+
                             |
                             V
                    +------------------+
                    .                  .
             get <--.  internal_value  .   internal_value()
                    .                  .
                    +------------------+                      
                             |
                             V
                    +------------------+
                    |   deflate_value  |   (override in subclass)
                    +------------------+
                             |
                             V
                    +------------------+
         define <-->|   output_filter  |   output_filter()
                    +------------------+
                             |
                             V
                    +------------------+
                    .                  .
             get <--.   output_value   .   output_value()
                    .                  .
                    +------------------+


Input must be done "at the top", by calling C<input_value()>. The value as
it exists at various stages of the flow can be retrieved, but it can only be
set at the top.  Input and output filters can be defined, but none exist by
default.

The purposes of the various stages of the data flow are as follows:

=over 4

=item B<input value>

The value as it was passed to the field.

=item B<input value filtered>

The input value after being passed through all input filters, but
before being inflated.

=item B<internal value>

The most useful representation of the value as far as the user of the
C<Rose::HTML::Form::Field>-derived class is concerned.  It has been filtered
and optionally "inflated" into a richer representation (i.e., an object).
The internal value must also be a valid input value.

=item B<output value>

The value as it will be used in the serialized HTML representation of the
field, as well as in the equivalent URI query string.  This is the internal
value after being optionally "deflated" and then passed through an output
filter. This value should be a string or a reference to an arry of strings. If
passed back into the field as the input value, it should result in the same
output value.

=back

Only subclasses can define class-wide "inflate" and "deflate" methods (by
overriding the no-op implementations in this class), but users can define
input and output filters on a per-object basis by passing code references to
the appropriate object methods.

The prefilter exists to handle common filtering tasks without hogging
the lone input filter spot (or requiring users to constantly set
input filters for every field).  The C<Rose::HTML::Form::Field> prefilter
optionally trims leading and trailing whitespace based on the value of
the C<trim_spaces()> boolean attribute.  This is part of the public
API for field objects, so subclasses that override C<input_prefilter()>
must preserve this functionality.

In addition to the various kinds of field values, each field also has a name,
which may or may not be the same as the value of the "name" HTML attribute.

Fields also have associated labels, error strings, default values, and various
methods for testing, clearing, and reseting the field value.  See the list of
object methods below for the details.

=head1 CUSTOM FIELDS

This module distribution contains classes for most simple HTML fields, as well
as examples of several more complex field types.  These "custom" fields do
things like accept only valid email addresses or dates, coerce input and
output into fixed formats, and provide rich internal representations (e.g.,
C<DateTime> objects).  Compound fields are made up of more than one field, and
this construction can be nested: compound fields can contain other compound
fields.  So long as each custom field class complies with the API outlined
here, it doesn't matter how complex it is internally (or externally, in its
HTML serialization).

(There are, however, certain rules that compound fields must follow in order to
work correctly inside C<Rose::HTML::Form> objects.  See the
C<Rose::HTML::Form::Field::Compound> documentation for more information.)

All of these classes are meant to be a starting point for your own custom
fields.  The custom fields included in this module distribution are mostly
meant as examples of what can be done.  I will accept any useful, interesting
custom field classes into the C<Rose::HTML::Form::Field::*> namespace, but I'd
also like to encourage suites of custom field classes in other namespaces
entirely.  Remember, subclassing doesn't necessarily dictate namespace.

Building up a library of custom fields is almost always a big win in the long
run.  Reuse, reuse, reuse!

=head1 HTML ATTRIBUTES

C<Rose::HTML::Form::Field> has the following set of valid HTML attributes.

    accesskey
    class
    dir
    id
    lang
    name
    onblur
    onclick
    ondblclick
    onfocus
    onkeydown
    onkeypress
    onkeyup
    onmousedown
    onmousemove
    onmouseout
    onmouseover
    onmouseup
    style
    tabindex
    title
    value
    xml:lang

=head1 CONSTRUCTOR

=over 4

=item B<new PARAMS>

Constructs a new C<Rose::HTML::Form::Field> object based on PARAMS, where 
PARAMS are name/value pairs.  Any object method is a valid parameter name.

=back

=head1 OBJECT METHODS

=over 4

=item B<clear>

Clears the field by setting both the "value" HTML attribute and the input
value to undef.  Also sets the C<is_cleared()> flag.

=item B<default VALUE>

Convenience wrapper for C<default_value()>

=item B<default_value VALUE>

Set the default value for the field.  In the absence of a defined input value,
the default value is used as the input value.

=item B<deflate_value VALUE>

This method is meant to be overridden by a subclass.  It should take VALUE and
"deflate" it to a form that is a suitable for the output value: a string or
reference to an array of strings.  The implementation in
C<Rose::HTML::Form::Field> simply returns VALUE unmodified.

=item B<description [TEXT]>

Get or set a text description of the field.  This text is not currently used
anywhere, but may be in the future.  It may be useful as help text, but keep
in mind that any such text should stay true to its intended purpose: a
description of the field.

Going too far off into the realm of generic help text is not a good idea
since this text may be used elsewhere by this class or subclasses, and there
it will be expected to be a description of the field rather than a description
of how to fill out the field (e.g. "Command-click to make multiple
selections") or any other sort of help text.

It may also be useful for debugging.

=item B<field_name [NAME]>

When passed a NAME argument, it sets the name of the field.  If the "name"
HTML attribute is not defined, its value is also set to NAME.  NAME is then
returned.

If no arguments are passed, and if the field name is not defined, then the
field name is set to the value of the "name" HTML attribute and then returned.
If the field name was already defined, then it is simply returned.

Note that this means that the field name and the "name" HTML attribute do not
necessarily have to be the same (but usually are).

=item B<filter [CODE]>

Sets both the input filter and output filter to CODE.

=item B<hidden_field>

Convenience wrapper for C<hidden_fields()>

=item B<hidden_fields>

Returns one or more C<Rose::HTML::Form::Field::Hidden> objects that represent
the hidden fields needed to encode this field's value.

=item B<html>

Returns the HTML serialization of the field, along with the HTML error
message, if any. The field and error HTML are joined by
C<html_error_separator()>, which is "E<lt>brE<gt>\n" by default.

=item B<html_error_separator [STRING]>

Get or set the string used to join the HTML field and HTML error message
in the output of the C<html()> method.  The default value is "E<lt>brE<gt>\n"

=item B<html_field>

Returns the HTML serialization of the field.

=item B<html_hidden_field>

Convenience wrapper for C<html_hidden_fields()>

=item B<html_hidden_fields>

Returns the HTML serialization of the fields returned by C<hidden_fields()>,
joined by newlines.

=item B<html_label [ARGS]>

Returns the HTML serialization of the label object, or the empty
string if the field's C<label> is undefined or zero in length.
Any ARGS are passed to the call to C<label_object()>.

If C<required()>is true for this field, then the name/value pair "class =>
'required'" is passed to the call to C<label_object()> I<before> any
arguments that you pass.  This allows you to override the "class" value
with one of your own.

=item B<html_prefix [STRING]>

Get or set an HTML prefix that may be displayed before the HTML field.
C<Rose::HTML::Form::Field> does not use this prefix, but subclasses might.
The default value is an empty string.

=item B<html_suffix [STRING]>

Get or set an HTML suffix that may be appended to the HTML field.
C<Rose::HTML::Form::Field> does not use this suffix, but subclasses might.
The default value is an empty string.

=item B<html_tag>

This method is part of the C<Rose::HTML::Object> API.  In this case, it simply
calls C<html_field()>.

=item B<inflate_value VALUE>

This method is meant to be overridden by subclasses.  It should take VALUE and
"inflate" it to a form that is a suitable internal value.  (See the
L<OVERVIEW> for more on internal values.)  The default implementation simply
returns its first argument unmodified.

=item B<input_filter [CODE]>

Get or set the input filter.

=item B<input_prefilter VALUE>

Runs VALUE through the input prefilter.  This method is called automatically
when needed and is not meant to be called by users of this module.  Subclasses
may want to override it, however. 

The default implementation optionally trims leading and trailing spaces based
on the value of the C<trim_spaces()> boolean attribute.  This is part of the
public API for field objects, so subclasses that override C<input_prefilter()>
must preserve this functionality.

=item B<input_value [VALUE]>

Get or set the input value.

=item B<input_value_filtered>

Returns the input value after passing it through the input prefilter
and input filter (if any).

=item B<internal_value>

Returns the internal value.

=item B<is_cleared>

Returns true if the field is cleared (i.e., if C<clear()> has been called on
it and it has not subsequently been C<reset()> or given a new input value),
false otherwise.

=item B<is_empty>

Returns true if the internal value contains any non-whitespace characters.
Subclasses should be sure to override this if they use internal values
other than strings.

=item B<label [STRING]>

Get or set the field label.  This label is used by the various label printing
methods as well as in some default error messages.  Even if you don't plan to
use any of the former, it might be a good idea to set it to a sensible value
for use in the latter.

=item B<label_object [ARGS]>

Returns a C<Rose::HTML::Label> object with its C<for> HTML attribute set to
the calling field's C<id> attribute and any other HTML attributes specified by
the name/value pairs in ARGS.  The HTML contents of the label object are set
to the field's C<label()>, which has its HTML escaped if C<escape_html()> is
true (which is the default).

=item B<name [NAME]>

Get or set the "name" HTML attribute, but return the C<field_name()>
if the "name" HTML attribute is undefined.

=item B<output_filter [CODE]>

Get or set the output filter.

=item B<output_value>

Returns the output value.

=item B<parent_field [FIELD]>

Get or set the parent field.  This method is provided for the benefit of
subclasses that might want to have a hierarchy of field objects.  The
reference to the parent field is "weakened" using C<Scalar::Util::weaken()> in
order to avoid memory leaks caused by circular references.

=item B<required [BOOL]>

Get to set a boolean flag that indicates whether or not a field is "required."
See C<validate()> for more on what "required" means.

=item B<reset>

Reset the field to its default state: the input value and C<error()> are set
to undef and the C<is_cleared()> flag is set to false.

=item B<trim_spaces [BOOL]>

Get or set the boolean flag that indicates whether or not leading and trailing
spaces should be removed from the field value in the input prefilter.
The default is true.

=item B<validate>

Validate the field and return a true value if it is valid, false otherwise. If
the field is C<required>, then its internal value is tested according to the
following rules.

* If the internal value is undefined, then return false.

* If the internal value is a reference to an array, and the array is empty,
then return false.

* If C<trim_spaces()> is true (the default) and if the internal value does not
contain any non-whitespace characters, return false.

If false is returned due to one of the conditions above, then C<error()> is
set to the string:

    $label is a required field

where C<$label> is either the field's C<label()> or, if C<label()> is not
defined, the string "This".

If a custom C<validator()> is set, then C<$_> is localized and set to the
internal value and the validator subroutine is called with the field object
as the first and only argument.

If the validator subroutine returns false and did not set C<error()> to a
defined value, then C<error()> is set to the string:

    $label is invalid

where C<$label> is is either the field's C<label()> or, if C<label()> is not
defined, the string "Value".

The return value of the validator subroutine is then returned.

If none of the above tests caused a value to be returned, then true is
returned.

=item B<validator [CODE]>

Get or set a validator subroutine.  If defined, this subroutine is called by
C<validate()>.

=item B<value [VALUE]>

If a VALUE argument is passed, it sets both the input value and the "value"
HTML attribute to VALUE.  Returns the value of the "value" HTML attribute.

=item B<xhtml>

Returns the XHTML serialization of the field, along with the HTML error
message, if any. The field and error HTML are joined by
C<xhtml_error_separator()>, which is "E<lt>br /E<gt>\n" by default.

=item B<xhtml_error_separator [STRING]>

Get or set the string used to join the XHTML field and HTML error message
in the output of the C<xhtml()> method.  The default value is "E<lt>br /E<gt>\n"

=item B<xhtml_field>

Returns the XHTML serialization of the field.

=item B<xhtml_hidden_field>

Convenience wrapper for C<xhtml_hidden_fields()>

=item B<xhtml_hidden_fields>

Returns the XHTML serialization of the fields returned by C<hidden_fields()>,
joined by newlines.

=item B<xhtml_label [ARGS]>

Returns the XHTML serialization of the label object, or the empty
string if the field's C<label> is undefined or zero in length.
Any ARGS are passed to the call to C<label_object()>.

If C<required()>is true for this field, then the name/value pair "class =>
'required'" is passed to the call to C<label_object()> I<before> any
arguments that you pass.  This allows you to override the "class" value
with one of your own.

=item B<xhtml_tag>

This method is part of the C<Rose::HTML::Object> API.  In this case, it simply
calls C<xhtml_field()>.

=back

=head1 AUTHOR

John C. Siracusa (siracusa@mindspring.com)

=head1 COPYRIGHT

Copyright (c) 2004 by John C. Siracusa.  All rights reserved.  This program is
free software; you can redistribute it and/or modify it under the same terms
as Perl itself.
