package Catalyst::Controller::AutoAssets::Handler::IconSet;
use strict;
use warnings;

# VERSION

use Moose;
use namespace::autoclean;

extends 'Catalyst::Controller::AutoAssets::Handler::ImageSet';

has 'flatten_paths', is => 'ro', isa => 'Bool', default => 1;
has 'icon_name_prefix', is => 'ro', isa => 'Str', default => 'icon-';
has 'lowercase_icon_names', is => 'ro', isa => 'Bool', default => 1;
has 'strip_ext_icon_names', is => 'ro', isa => 'Bool', default => 1;
has 'replace_nonwords_icon_names', is => 'ro', isa => 'Bool', default => 1;
has 'css_file_name', is => 'ro', isa => 'Str', default => 'icons.css';

around asset_request => sub {
  my ( $orig, $self, $c, $sha1, @args ) = @_;
  
  # Request for the generated CSS
  if($args[0] eq $self->css_file_name) {
    $self->prepare_asset;
    return $self->unknown_asset($c) unless ($sha1 eq $self->asset_name);
    return $self->_set_file_response($c,$self->css_file,'text/css');
  }
  
  return $self->$orig($c, $sha1, @args);
};

has 'icon_name_generator', is => 'ro', isa => 'CodeRef', lazy => 1, default => sub {
  return sub {
    my ($self, $name) = @_;
    $name =~ s/\.\w+$// if ($self->strip_ext_icon_names);
    $name =~ s/[\_\W]/\-/g if($self->replace_nonwords_icon_names);
    $name = lc($name) if ($self->lowercase_icon_names);
    return $self->icon_name_prefix . $name;
  };
};

has 'css_rule_generator', is => 'ro', isa => 'CodeRef', lazy => 1, default => sub {
  return sub {
    my ($self, $icon, $path) = @_;
    return '.' . $icon . ' { background-image: url(' . $path . ') !important; }';
  };
};


has 'css_file', is => 'ro', isa => 'Path::Class::File', lazy => 1, default => sub {
  my $self = shift;
  return $self->work_dir->file($self->css_file_name);
};

has 'icon_manifest', is => 'rw', isa => 'HashRef', default => sub {{}};
after rebuild_manifest => sub {
  my $self = shift;
  my $manifest = $self->manifest;
  
  my %icon_map = ();
  for my $name (keys %{$self->manifest}) {
    my $icon = $self->icon_name_generator->($self,$name);
    die "Duplicate icon name '$icon' ($name)" if (exists $icon_map{$icon});
    
    # The path in the CSS can be relative because the css file itself
    # is served from the root of the asset path. That is good because
    # we would have a deep recursion problem if we wanted to get the
    # asset path here (i.e. $self->asset_path($name) ) because our caller
    # might already be: asset_path -> prepare_asset -> ... -> us
    $self->manifest->{$name}->{css_rule} = $self->css_rule_generator->($self,$icon,$name);
    $self->manifest->{$name}->{name} = $name;
    $self->manifest->{$name}->{icon_name} = $icon;
    
    $icon_map{$icon} = $self->manifest->{$name};
  }
  
  $self->icon_manifest(\%icon_map);
  $self->rebuild_css_file;
};

sub rebuild_css_file {
  my $self = shift;
  
  my $css = join("\n",map {
    $self->manifest->{$_}->{css_rule}
  } (sort keys %{$self->manifest}) );
  
  return $self->css_file->spew($css);
}

sub html_head_tags {
  my $self = shift;
  return
		"<!--   AUTO GENERATED BY " . ref($self->Controller) . " (/" .
    $self->action_namespace($self->_app) . ")   -->\r\n" .
		'<link rel="stylesheet" type="text/css" href="' . 
    join('/',$self->asset_path,$self->css_file_name) .
    '" />' .
		"\r\n<!--  ---- END AUTO GENERATED ASSETS ----  -->\r\n";
}

sub icon_class {
  my ($self, @path) = @_;
  die "icon_class_name() requires subfile path argument" 
    unless (scalar @path > 0);
  
  # Will prepare the asset if needed:
  $self->asset_path(@path);
  
  my $name = join('/',@path);
  my $data = $self->manifest->{$name} or die "No such image/icon '$name'";
  return $data->{icon_name};
}

has '_persist_attrs', is => 'ro', isa => 'ArrayRef', default => sub{[qw(
 built_mtime
 inc_mtimes
 last_fingerprint_calculated
 subfile_meta
 _excluded_paths
 manifest
 icon_manifest
)]};



1;

__END__

=pod

=head1 NAME

Catalyst::Controller::AutoAssets::Handler::IconSet - IconSet type handler

=head1 SYNOPSIS

In your controller:

  package MyApp::Controller::Assets::Icons;
  use parent 'Catalyst::Controller::AutoAssets';
  
  1;

Then, in your .conf:

  <Controller::Assets::Icons>
    include        root/static/icons/
    type           IconSet
  </Controller::Assets::Icons>

And in your .tt files:

  <head>
    <!-- include the auto generated icon CSS -->
    [% c.controller('Assets::Icons').html_head_tags %]
    
    <!-- or, in static HTML -->
    <link rel="stylesheet" type="text/css" href="/assets/icons/current/icons.css" />
  </head>
  
  ...
  
  <!-- access individual icons by name -->
  [% c.controller('Assets::Icons').img_tag('foo.png') %]
  <img src="[% c.controller('Assets::Icons').asset_path('apple.jpg') %]">
  
  ...
  
  <!-- use icon class names in markup -->
  <div class="mycls [% c.controller('Assets::Icons').icon_class('apple.jpg') %]"></div>
  
  <!-- or, in static HTML -->
  <div class="mycls icon-apple"></div>

=head1 DESCRIPTION

Like the 'ImageSet' asset type but also generates/includes CSS classes for accessing/using icons in HTML
markup (i.e. with the css style C<'background-image'>). These icon class names can then be used in HTML tags
or directly in JavaScript frameworks like ExtJS.

This class extends L<Catalyst::Controller::AutoAssets::Handler::ImageSet>. Only differences are shown below.

=head1 CONFIG PARAMS

=head2 flatten_paths

Inherited from ImageSet but defaults to true (1).

=head2 icon_name_prefix

String to prepend to the generated css icon class name. Defaults to C<'icon-'>.

=head2 lowercase_icon_names

Whether or not to use all lowercase characters in the css icon class name. Defaults to true (1).

=head2 strip_ext_icon_names

Whether or not to strip the file extension from the filename when generating the css class name. 
Defaults to true (1).

=head2 replace_nonwords_icon_names

Whether or not to convert special characters and _ in filename to '-' when generating the css class name. 
Defaults to true (1).

=head2 icon_name_generator

CodeRef used to generate the css icon class name. Receives ($self, $image_name) as arguments and returns
the css class name. If this is overridden it will supersede all the above "_icon_names" options. With all 
the default options, the $image_name C<'Brick_House.png'> would return the CSS class name C<'icon-brick-house'>.

=head2 css_rule_generator

CodeRef used to generate the actual css rule definition for a given icon class name. 
Receives ($self, $icon_class, $path) as arguments and returns the css rule string. By default, args C<'icon-brick-house'>
and C<'Brick_House.png'> would return:

  .icon-brick-house { background-image: url(Brick_House.png) !important; }

=head2 css_file_name

The name of the automatically generated CSS file which will contain the CSS rules for all the icons included in
the asset. This file is accessible as an ordinary subfile in the asset.

Defaults to C<'icons.css'>.

=head1 METHODS

=head2 html_head_tags

Returns a pre-built link tag to include the generated CSS suitable for use in an HTML <head>
section. By default this will be:

  <link rel="stylesheet" type="text/css" href="<CONTROLLER_PATH>/<SHA1>/icons.css" />

=head2 icon_class

Returns the generated icon css class name for the supplied icon path/filename.

  [% c.controller('Assets::Icons').icon_class("Brick_House.png") %] # 'icon-brick-house'

=head1 SEE ALSO

=over

=item L<Catalyst::Controller::AutoAssets>

=item L<Catalyst::Controller::AutoAssets::Handler::ImageSet>

=item L<Catalyst::Controller::AutoAssets::Handler>

=back

=head1 AUTHOR

Henry Van Styn <vanstyn@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2013 by IntelliTree Solutions llc.

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

=cut
