#!/usr/bin/perl

package Catalyst::Plugin::Images;

use strict;
use warnings;

use Image::Size ();
use HTML::Entities ();
use Path::Class ();
use MRO::Compat;

our $VERSION = "0.02";

sub setup {
    my $app = shift;
    my $ret = $app->next::method( @_ );

    $app->config->{images}{paths} ||= [
        $app->path_to(qw/root static/),
        $app->path_to(qw/root images/),
        $app->path_to(qw/root static images/),
    ];

    $app->config->{images}{uri_base} ||= $app->path_to("root");

    $ret;
}

sub image_tag {
    my ( $c, $basename, @attrs ) = @_;
    my %attrs = (@attrs == 1) ? %{ $attrs[0] } : @attrs;

    my $info = $c->get_image_info( $basename );
    
    foreach my $attr (qw/width height alt/) {
        next if exists $attrs{$attr};
        next unless exists $info->{$attr};
        $attrs{$attr} = $info->{$attr};
    }

    $attrs{src} = $info->{uri}->as_string;

    foreach my $tag ( keys %attrs ) {
        $attrs{$tag} = HTML::Entities::encode_entities( $attrs{$tag} )
            if $attrs{$tag} =~ /\D/;
    }

    return join(" ",
        '<img',
        (map { sprintf '%s="%s"', $_, $attrs{$_} } keys %attrs ),
        '/>'
    );
}

sub get_image_info {
    my ( $c, $basename ) = @_;

    if ( my $cached = $c->get_cached_image_info( $basename ) ) {
        return $cached;
    }
    
    my $path = $c->find_image_file( $basename );

    my $info = { ( $path ? $c->read_image_info( $path ) : () ) };

    $info->{path} = $path || Path::Class::file( $basename );
    $info->{uri}  = $c->image_path_to_uri( $path, $basename );

    $c->set_cached_image_info( $basename, $info );

    return $info;
}

sub get_cached_image_info {
    my ( $c, $basename ) = @_;
    return;
}

sub set_cached_image_info {
    my ( $c, $basename, $info ) = @_;
    return;
}

sub image_path_to_uri {
    my ( $c, $path, $basename ) = @_;
    $c->uri_for( "/" . ( $path ? $path->relative( $c->config->{images}{uri_base} ) : $basename ) );
}

sub find_image_file {
    my ( $c, $basename ) = @_;
   
    foreach my $path ( map { Path::Class::dir($_) } @{ $c->config->{images}{paths} } ) {
        $path = $c->path_to( $path ) unless $path->is_absolute;
        my $file = $path->file( $basename );
        return $file if -f $file->stringify;
    }

    $c->log->debug("Couldn't find an image by the name of '$basename' in any of the search paths")
        if $c->debug;

    return;
}

sub read_image_info {
    my ( $self, $path ) = @_;
    my ( $width, $height ) = eval { Image::Size::imgsize( $path->stringify ) };
    return ( width => $width || '', height => $height || '' );
}

__PACKAGE__;

__END__

=pod

=head1 NAME

Catalyst::Plugin::Images - Generate image tags for static files.

=head1 SYNOPSIS

	use Catalyst qw/
        Images
    /;

    # ... somewhere in your templates

    [% c.image_tag("foo.png", alt => "alt text") %];

=head1 DESCRIPTION

This plugin provides a quick and easy way to include your images on the page,
automatically extracting and caching image metadata. It's automatically 
extendable, just pass whatever attribute you require as a key/value pair, and
it will be added to the image tag. It will also look through a preset of folders
so that you don't have to specify the full address to your image.

=head1 METHODS

=over 4

=item image_tag $basename, %attrs

This method generates an image tag for the image named $basename, with the
extra tags %attr automatically added to the resulting HTML tag. If you don't
specify height/width, it will be autodetected from the image.

=item get_image_info $basename

Retrieve the information about the image either from the cache or by searching
for it.

=item find_image_file $basename

Look inside all the search paths (see L</CONFIGURATION>) for an image named
$basename, and return the full path to it, as a <Path::Class::File> object..

=item read_image_info $path

Given the full path, as a L<Path::Class::File> object, return the attributes to
be added to the image. This returns a list with C<width> and C<height>, using
C<Image::Size>.

=item image_path_to_uri $path, $basename

Generates a URI using L<Catalyst/uri_for>, with the absolute path C<$path>
relativized to C<uri_base>. See L</CONFIGURATION>.

=item get_cached_image_info

=item set_cached_image_info

see L</CACHING IMAGE DATA> below.

=item setup

Overridden to seed configuration defaults.

=back

=head1 CONFIGURATION

All configuration information is stored under the C<images> key.

=over 4

=item paths

This should be an array reference of L<Path::Class::Dir> objects (easily
generated with L<Catalyst/path_to>) in which to search for images.

It defaults to C<root/static>, C<root/images>, C<root/static/images> by
default.

=item uri_base

This is the "base" prefix path for URI generation. For example, if an image was
found at C</www/static/images/foo.png> and C<uri_base> is C</www> then the
L<URI> generated with C<Catalyst/uri_for> will be for
C</static/images/foo.png>.

=back

=head1 CACHING IMAGE DATA

The code will call C<get_cached_image_info> and C<set_cached_image_info> when
appropriate. Currently both these operations are no op. You should override
this if you care.

C<get_cached_image_info> receives the base name, and should return the info
hash.

C<set_cached_image_info> receives the base name, and the info hash. It can use
this data to expire the cache based on mtime, etc. The info hash contains the
keys C<width>, C<height>, C<uri>, and C<path>.

=head1 SEE ALSO

L<Catalyst>, L<Catalyst::Plugin::Static::Simple>, L<Image::Size>

=head1 AUTHOR

Yuval Kogman, C<nothingmuch@woobling.org>

Last released by Tomas Doran, C<bobtfish@bobtfish.net>

=head1 LICENSE

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

=cut