The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

use strict;

use FindBin qw($Bin);

use Test::More tests => 179;
#use Test::More 'no_plan';

use_ok('Rose::HTML::Objects');

use File::Spec;
use File::Path;

#
# In-memory
#

my %code =
(
  'My::HTML::Object::Message::Localizer' =>
  {
    filter => sub { s/__FOO__//g },
    code   =><<'EOF',
sub get_localized_message_text__FOO____BAR__
{
  my($self) = shift;
  no warnings 'uninitialized';
  return uc $self->SUPER::get_localized_message_text(@_);
}
EOF
  },

  'My::HTML::Form::Field::File' =><<'EOF',
our $JCS = 123;
EOF
);

#$Rose::HTML::Objects::Debug = 3;
my($packages, $perl) = 
  Rose::HTML::Objects->make_private_library(in_memory    => 1, 
                                            prefix       => 'My::',
                                            class_filter => sub { !/Email/ },
                                            code_filter  => sub {  s/__BAR__//g },
                                            code         => \%code);

is(scalar @My::HTML::Form::Field::Email::ISA, 0, 'class_filter');

QUIET:
{
  no warnings;
  is($My::HTML::Form::Field::File::JCS, 123, 'code additions');
}

my $field     = My::HTML::Form::Field::Text->new(name => 'x', required => 1);
my $std_field = Rose::HTML::Form::Field::Text->new(name => 'x', required => 1);

$field->validate;
$std_field->validate;

is($field->error, uc $std_field->error, 'upcase error 1');

$field = My::HTML::Form::Field::PopUpMenu->new(name => 'x');

$field->options(a => 'Apple', b => 'Pear');

is(ref $field->option('a'), 'My::HTML::Form::Field::Option', 'option 1');

my $object = My::HTML::Object->new('xyz');

is($object->validate_html_attrs, 0, 'generic object 1');

eval { $object->html_attr(foo => 'bar') };
ok(!$@, 'generic object 2');

foreach my $type (My::HTML::Object->object_type_names)
{
  my $std_class = Rose::HTML::Object->object_type_class($type);
  (my $new_class = $std_class) =~ s/^Rose::/My::/;

  is(My::HTML::Object->object_type_class($type), $new_class, "object type class: $type");
}

GENERIC_FIELD:
{
  package My::Field;
  our @ISA = ('My::HTML::Form::Field');

  package main;
  $field = My::Field->new;
  ok($field->isa('Rose::HTML::Form::Field'), 'generic field 1');
}

#
# Module files
#

my $lib_dir = File::Spec->catfile($Bin, 'tmplib');

mkdir($lib_dir)  unless(-d $lib_dir);
die "Could not mkdir($lib_dir) - $!"  unless(-d $lib_dir);

%code =
(
  'My2::HTML::Object::Message::Localizer' =><<'EOF',
our $TwiddleCase = 1;

sub get_localized_message_text
{
  my($self) = shift;

  if($TwiddleCase)
  {
    no warnings 'uninitialized';
    return rand > 0.5 ? uc $self->SUPER::get_localized_message_text(@_) :
                        lc $self->SUPER::get_localized_message_text(@_);
  }
  else
  {
    return $self->SUPER::get_localized_message_text(@_);
  }
}
EOF

  'My2::HTML::Object::Messages' =><<'EOF',
use constant FIELD_ERROR_BAD_NICKNAME    => 100_000;
use constant FIELD_ERROR_BAD_NICKNAME_AR => 100_001;
use constant FIELD_ERROR_TOO_MANY_DAYS   => 100_002;

use constant FIELD_LABEL_NICKNAME        => 200_000;
EOF

  'My2::HTML::Object::Errors' =><<'EOF',
use constant FIELD_ERROR_BAD_NICKNAME    => 100_000;
use constant FIELD_ERROR_BAD_NICKNAME_AR => 100_001;
use constant FIELD_ERROR_TOO_MANY_DAYS   => 100_002;
EOF
);

$packages =
  Rose::HTML::Objects->make_private_library(modules_dir => $lib_dir, 
                                            overwrite   => 1,
                                            prefix      => 'My2::',
                                            code        => \%code);

unshift(@INC, $lib_dir);

GENERIC_FIELD:
{
  package My2::Field;
  require My2::HTML::Form::Field;
  our @ISA = ('My2::HTML::Form::Field');

  package main;
  $field = My2::Field->new;
  ok($field->isa('Rose::HTML::Form::Field'), 'generic field 2');
}

require My2::HTML::Form::Field::Text;

$field     = My2::HTML::Form::Field::Text->new(name => 'x', required => 1);
$std_field = Rose::HTML::Form::Field::Text->new(name => 'x', required => 1);

$field->validate;
$std_field->validate;

my @errors;

for(1 .. 10)
{
  push(@errors, $field->error . '');
}

ok((scalar grep { lc $std_field->error } @errors), 'lowercase error 1');
ok((scalar grep { lc $std_field->error } @errors), 'uppercase error 1');

require My2::HTML::Form::Field::PopUpMenu;

$field = My2::HTML::Form::Field::PopUpMenu->new(name => 'x');

$field->options(a => 'Apple', b => 'Pear');

is(ref $field->option('a'), 'My2::HTML::Form::Field::Option', 'option 1');

require My2::HTML::Object;

$object = My2::HTML::Object->new('xyz');

is($object->validate_html_attrs, 0, 'generic object 1');

eval { $object->html_attr(foo => 'bar') };
ok(!$@, 'generic object 2');

foreach my $type (My2::HTML::Object->object_type_names)
{
  my $std_class = Rose::HTML::Object->object_type_class($type);
  (my $new_class = $std_class) =~ s/^Rose::/My2::/;

  is(My2::HTML::Object->object_type_class($type), $new_class, "object type class: $type");
}

my @parts = split('::', 'My2::HTML::Form::Field::Nickname');
$parts[-1] .= '.pm';

my $nick_pm = File::Spec->catfile($lib_dir, @parts);
#warn "# WRITE: $nick_pm\n";

open(my $fh, '>', $nick_pm) or die "Could not create '$nick_pm' - $!";

my $code=<<'EOF';
package My2::HTML::Form::Field::Nickname;

use My2::HTML::Object::Errors qw(FIELD_ERROR_BAD_NICKNAME);
use My2::HTML::Object::Messages qw(FIELD_LABEL_NICKNAME);

use base qw(My2::HTML::Form::Field::Text);

sub init
{
  my($self) = shift;
  $self->label_id(FIELD_LABEL_NICKNAME);
  $self->SUPER::init(@_);
}

sub validate
{
  my($self) = shift;

  my $ret = $self->SUPER::validate(@_);
  return $ret  unless($ret);

  my $nick  = $self->internal_value;
  my $class = $self->html_attr('class') || 'default';

  if($nick =~ /bob/)
  {
    $self->error_id(FIELD_ERROR_BAD_NICKNAME, { nickname => $nick, class => $class, list => [ 'a' .. 'e' ] });
    return 0;
  }
  elsif($nick =~ /lob/)
  {
    $self->error_id(FIELD_ERROR_BAD_NICKNAME_AR, [ $class, [ 'a' .. 'e' ], $nick ]);
    return 0;
  }

  return 1;
}

if(__PACKAGE__->localizer->auto_load_messages)
{
  __PACKAGE__->localizer->load_all_messages;
}

1;

__DATA__

[% LOCALE en %]

FIELD_ERROR_BAD_NICKNAME = "[class] - \\\[\]Invalid nickname: [@list]:[nickname]"
FIELD_ERROR_BAD_NICKNAME_AR = "[1] - \\\[\]Invalid nickname: [@2]:[3]"

FIELD_LABEL_NICKNAME = "Nickname"

[% LOCALE fr %]

FIELD_ERROR_BAD_NICKNAME = "[class] - \\\[\]Le nickname est mal: [@list]:[nickname]"

FIELD_LABEL_NICKNAME = "Le Nickname"

EOF

print $fh $code;
close($fh) or die "Could not write '$nick_pm' - $!";

require My2::HTML::Form::Field::Nickname;

$field = My2::HTML::Form::Field::Nickname->new;

is(ref($field->localizer), 'My2::HTML::Object::Message::Localizer', 'custom field localizer');

my $label = $field->label . '';

ok($label eq 'nickname' || $label eq 'NICKNAME', 'custom field label (en)');

$field->locale('fr');

$label = $field->label . '';

ok($label eq 'le nickname' || $label eq 'LE NICKNAME', 'custom field label (fr)');

BLAH:
{
  no warnings;
  $My2::HTML::Object::Message::Localizer::TwiddleCase = 0;
}

$field->input_value('lob');
$field->validate;
is($field->error . '', 'default - \[]Invalid nickname: a, b, c, d, e:lob', 'placeholders (arrayref, fallback) 1');

$field->html_attr(class => 'c');
$field->validate;
is($field->error. '', 'c - \[]Invalid nickname: a, b, c, d, e:lob', 'placeholders (arrayref, fallback) 2');

$field->input_value('bob');
$field->validate;

$field->locale('en');
is($field->error. '', 'c - \[]Invalid nickname: a, b, c, d, e:bob', 'placeholders (hashref) 1');

$field->locale('fr');
is($field->error. '', 'c - \[]Le nickname est mal: a, b, c, d, e:bob', 'placeholders (hashref) 2');

require My2::HTML::Form;

My2::HTML::Form->field_type_class
(
  nick => 'My2::HTML::Form::Field::Nickname',
);

# My2::HTML::Form->field_type_class
# (
#   nickname => 'My2::HTML::Form::Field::Nickname',
# );

My2::HTML::Form->add_field_type_classes
(
  #nick     => 'My2::HTML::Form::Field::Nickname',
  nickname => 'My2::HTML::Form::Field::Nickname',
);

my $form = My2::HTML::Form->new;
$field = My2::HTML::Form::Field::Nickname->new;
$field->html_attr(class => 'c');

$form->add_field(nick => $field);

$form->params(nick => 'bob');
$form->init_fields;
$form->validate;

is($form->field('nick')->error. '', 'c - \[]Invalid nickname: a, b, c, d, e:bob', 'form locale 1');

$form->locale('fr');

is($form->field('nick')->error. '', 'c - \[]Le nickname est mal: a, b, c, d, e:bob', 'form locale 2');

$form = My2::HTML::Form->new;

$form->add_field(nick => { type => 'nick', class => 'c' });

$form->params({nick => 'bob'});
$form->init_fields;
$form->validate;

is($form->field('nick')->error. '', 'c - \[]Invalid nickname: a, b, c, d, e:bob', 'localizer locale 1');

My2::HTML::Object->localizer->locale('fr');

is($form->field('nick')->error. '', 'c - \[]Le nickname est mal: a, b, c, d, e:bob', 'localizer locale 2');

My2::HTML::Object->localizer->locale('en');

#
# Days field
#

@parts = split('::', 'My2::HTML::Form::Field::Days');
$parts[-1] .= '.pm';

my $days_pm = File::Spec->catfile($lib_dir, @parts);
#warn "# WRITE: $days_pm\n";

open($fh, '>', $days_pm) or die "Could not create '$days_pm' - $!";

$code=<<'EOF';
package My2::HTML::Form::Field::Days;

use My2::HTML::Object::Errors qw(FIELD_ERROR_TOO_MANY_DAYS);

use base qw(My2::HTML::Form::Field::Integer);


if(__PACKAGE__->localizer->auto_load_messages)
{
  __PACKAGE__->localizer->load_all_messages;
}

1;

__DATA__

[% LOCALE en %]

FIELD_ERROR_TOO_MANY_DAYS = "Too many days."
FIELD_ERROR_TOO_MANY_DAYS(one) = "One day is too many."
FIELD_ERROR_TOO_MANY_DAYS(two) = "Two days is too many."
FIELD_ERROR_TOO_MANY_DAYS(few) = "[count] days is too many (few)."

[% LOCALE fr %]

FIELD_ERROR_TOO_MANY_DAYS = "Trop de jours."

EOF

print $fh $code;
close($fh) or die "Could not write '$days_pm' - $!";

require My2::HTML::Form::Field::Days;

require My2::HTML::Object::Errors;

My2::HTML::Form::Field::Days->localizer->load_messages_from_string(<<"EOF");
[% LOCALE en %]

FIELD_ERROR_TOO_MANY_DAYS(few) = "[count] days is too many (few)."
FIELD_ERROR_TOO_MANY_DAYS(many) = "[count] days is too many (many)."
FIELD_ERROR_TOO_MANY_DAYS(plural) = "[count] days is too many."

[% LOCALE fr %]

FIELD_ERROR_TOO_MANY_DAYS(one) = "Un jour est un trop grand nombre."
FIELD_ERROR_TOO_MANY_DAYS(plural) = "[count] jours est un trop grand nombre."

[% LOCALE xx %]

FIELD_ERROR_TOO_MANY_DAYS = "[count] [variant] xx too many days."

EOF

$field = My2::HTML::Form::Field::Days->new(name => 'days');

my $error_id = My2::HTML::Object::Errors::FIELD_ERROR_TOO_MANY_DAYS();

$field->error_id($error_id, { count => 0 });
is($field->error, '0 days is too many.', 'zero variant (en)');

$field->error_id($error_id, { count => 1 });

is($field->error, 'One day is too many.', 'one variant (en)');

$field->error_id($error_id, { count => 2 });

is($field->error, 'Two days is too many.', 'two variant (en)');

$field->error_id($error_id, { count => 3 });
is($field->error, '3 days is too many.', 'plural fallback variant (en)');

$field->error_id($error_id, { count => 3, variant => 'few' });
is($field->error, '3 days is too many (few).', 'few explicit variant (en)');

$field->error_id($error_id, { count => 3, variant => 'many' });
is($field->error, '3 days is too many (many).', 'many explicit variant (en)');

$field->locale('fr');

$field->error_id($error_id, { count => 0 });
is($field->error, '0 jours est un trop grand nombre.', 'zero variant (fr)');

$field->error_id($error_id, { count => 1 });

is($field->error, 'Un jour est un trop grand nombre.', 'one variant (fr)');

$field->error_id($error_id, { count => 2 });

is($field->error, '2 jours est un trop grand nombre.', 'two variant (fr)');

$field->error_id($error_id, { count => 3 });
is($field->error, '3 jours est un trop grand nombre.', 'plural fallback variant (fr)');

$field->error_id($error_id, { count => 3, variant => 'few' });
is($field->error, '3 jours est un trop grand nombre.', 'few explicit variant (fr)');

$field->error_id($error_id, { count => 3, variant => 'many' });
is($field->error, '3 jours est un trop grand nombre.', 'many explicit variant (fr)');

$field->locale('xx');

ok(!$field->localizer->localized_message_exists('FIELD_ERROR_TOO_MANY_DAYS', 'xx', 'zero'), 'localized_message_exists 1');

$field->error_id($error_id, { count => 0 });

is($field->error, '0  xx too many days.', 'zero variant (xx)');

$field->error_id($error_id, { count => 1 });

is($field->error, '1  xx too many days.', 'one variant (xx)');

$field->error_id($error_id, { count => 2 });

is($field->error, '2  xx too many days.', 'two variant (xx)');

$field->error_id($error_id, { count => 3 });
is($field->error, '3  xx too many days.', 'plural fallback variant (xx)');

$field->error_id($error_id, { count => 3, variant => 'few' });
is($field->error, '3 few xx too many days.', 'few explicit variant (xx)');

$field->error_id($error_id, { count => 3, variant => 'many' });
is($field->error, '3 many xx too many days.', 'many explicit variant (xx)');

($packages, $perl) = 
  Rose::HTML::Objects->make_private_library(in_memory => 1, 
                                            rename    => sub { s/Rose::H/MyH/ });

ok(MyHTML::Object->isa('Rose::HTML::Object'), 'rename sub 1');

END
{
  if($lib_dir && -d $lib_dir)
  {
    #rmtree($lib_dir);
  }
}