=head1 NAME

HTML::Microformats::Format::hProduct - the hProduct microformat

=head1 SYNOPSIS

 use HTML::Microformats::DocumentContext;
 use HTML::Microformats::Format::hProduct;

 my $context = HTML::Microformats::DocumentContext->new($dom, $uri);
 my @objects = HTML::Microformats::Format::hProduct->extract_all(
                   $dom->documentElement, $context);
 foreach my $p (@objects)
 {
   printf("%s\n", $m->get_fn);
   if ($p->get_review)
   {
     foreach my $r ($p->get_review)
     {
       printf("  - reviewed by %s\n", $r->get_reviewer->get_fn);
     }
   }
   else
   {
     print "    (no reviews yet)\n";
   }
 }

=head1 DESCRIPTION

HTML::Microformats::Format::hProduct inherits from HTML::Microformats::Format. See the
base class definition for a description of property getter/setter methods,
constructors, etc.

=cut

package HTML::Microformats::Format::hProduct;

use base qw(HTML::Microformats::Format HTML::Microformats::Mixin::Parser);
use strict qw(subs vars); no warnings;
use 5.010;

use Object::AUTHORITY;

BEGIN {
	$HTML::Microformats::Format::hProduct::AUTHORITY = 'cpan:TOBYINK';
	$HTML::Microformats::Format::hProduct::VERSION   = '0.105';
}

sub new
{
	my ($class, $element, $context) = @_;
	my $cache = $context->cache;
	
	return $cache->get($context, $element, $class)
		if defined $cache && $cache->get($context, $element, $class);
	
	my $self = {
		'element'    => $element ,
		'context'    => $context ,
		'cache'      => $cache ,
		'id'         => $context->make_bnode($element) ,
		};
	
	bless $self, $class;
	
	my $clone = $element->cloneNode(1);	
	$self->_expand_patterns($clone);
	$self->_simple_parse($clone);
	
	foreach my $review (@{ $self->{'DATA'}->{'review'} })
	{
		$review->{'DATA'}->{'item'} = $self
			unless $review->{'DATA'}->{'item'};
	}

	foreach my $listing (@{ $self->{'DATA'}->{'listing'} })
	{
		$listing->{'DATA'}->{'item'} = $self
			unless $listing->{'DATA'}->{'item'};
	}

	##TODO: class=identifier (type+value)    post-0.001

	$cache->set($context, $element, $class, $self)
		if defined $cache;

	return $self;
}

sub format_signature
{
	my $gr   = 'http://purl.org/goodrelations/v1#';
	my $hl   = 'http://ontologi.es/hlisting-hproduct#';
	my $rdfs = 'http://www.w3.org/2000/01/rdf-schema#';
	my $foaf = 'http://xmlns.com/foaf/0.1/';
	my $dc   = 'http://purl.org/dc/terms/';

	return {
		'root' => 'hproduct',
		'classes' => [
			['brand',            'M?', {'embedded'=>'hCard'}],
			['category',         '*'],
			['price',            '?',  {'value-title'=>'allow'}],
			['description',      '?'],
			['fn',               '1'],
			['photo',            'u*'],
			['url',              'u?'],
			['review',           'm*', {'embedded'=>'hReview hReviewAggregate'}],
			['listing',          'm*', {'embedded'=>'hListing'}],
		],
		'options' => {
			'rel-tag' => 'category',
		},
		'rdf:type' => ["${gr}ProductOrService"] ,
		'rdf:property' => {
			'brand'           => { literal =>["${hl}brand"] },
			'category'        => { resource=>['http://www.holygoat.co.uk/owl/redwood/0.1/tags/taggedWithTag'] },
			'description'     => { literal =>["${dc}description"] },
			'fn'              => { literal =>["${rdfs}label"] },
			'photo'           => { resource=>["${foaf}depiction"] },
			'url'             => { resource=>["${foaf}page", "${rdfs}seeAlso"] },
			'review'          => { resource=>['http://purl.org/stuff/rev#hasReview'] },
			'listing'         => { rev     =>["${hl}listing"] },
		},
	};
}

sub add_to_model
{
	my $self  = shift;
	my $model = shift;

	my $gr   = 'http://purl.org/goodrelations/v1#';
	my $hl   = 'http://ontologi.es/hlisting-hproduct#';
	my $rdfs = 'http://www.w3.org/2000/01/rdf-schema#';
	my $foaf = 'http://xmlns.com/foaf/0.1/';
	my $rdf  = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
	
	$self->_simple_rdf($model);

	if ($self->_isa($self->{'DATA'}->{'brand'}, 'HTML::Microformats::Format::hCard'))
	{
		$model->add_statement(RDF::Trine::Statement->new(
			$self->id(1),
			RDF::Trine::Node::Resource->new("${hl}brand"),
			$self->{'DATA'}->{'brand'}->id(1, 'holder'),
			));
	}

	if ($self->{'DATA'}->{'price'})
	{
		$model->add_statement(RDF::Trine::Statement->new(
			$self->id(1),
			RDF::Trine::Node::Resource->new("${hl}price"),
			$self->id(1, 'price'),
			));
		$model->add_statement(RDF::Trine::Statement->new(
			$self->id(1, 'price'),
			RDF::Trine::Node::Resource->new("${rdf}type"),
			RDF::Trine::Node::Resource->new("${gr}PriceSpecification"),
			));
		$model->add_statement(RDF::Trine::Statement->new(
			$self->id(1, 'price'),
			RDF::Trine::Node::Resource->new("${rdfs}comment"),
			$self->_make_literal($self->{'DATA'}->{'price'}),
			));

		my ($curr, $val);
		if ($self->{'DATA'}->{'price'} =~ /^\s*([a-z]{3})\s*(\d*(?:[\,\.]\d\d))\s*$/i)
		{
			($curr, $val) = ($1, $2);
		}
		elsif ($self->{'DATA'}->{'price'} =~ /^\s*(\d*(?:[\,\.]\d\d))\s*([a-z]{3})\s*$/i)
		{
			($curr, $val) = ($2, $1);
		}
		
		if (defined $curr && defined $val)
		{
			$model->add_statement(RDF::Trine::Statement->new(
				$self->id(1, 'price'),
				RDF::Trine::Node::Resource->new("${gr}hasCurrency"),
				$self->_make_literal($curr, 'string'),
				));
			$model->add_statement(RDF::Trine::Statement->new(
				$self->id(1, 'price'),
				RDF::Trine::Node::Resource->new("${gr}hasCurrencyValue"),
				$self->_make_literal($val, 'float'),
				));
		}
	}

	return $self;
}

sub profiles
{
	my $class = shift;
	return qw(http://purl.org/uF/hProduct/0.3/);
}

1;

=head1 MICROFORMAT

HTML::Microformats::Format::hProduct supports hProduct 0.3 as described at
L<http://microformats.org/wiki/hProduct>, with the following additions:

=over 4

=item * 'item' propagation.

If 'review' and 'listing' objects don't have an 'item' set, then their
'item' property is set to this object.

=back

=head1 RDF OUTPUT

Product data is primarily output using GoodRelations v1
(L<http://purl.org/goodrelations/v1#>).

=head1 BUGS

Please report any bugs to L<http://rt.cpan.org/>.

=head1 SEE ALSO

L<HTML::Microformats::Format>,
L<HTML::Microformats>.

=head1 AUTHOR

Toby Inkster E<lt>tobyink@cpan.orgE<gt>.

=head1 COPYRIGHT AND LICENCE

Copyright 2008-2012 Toby Inkster

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

=head1 DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.


=cut