<%doc>
=head1 NAME

/content/transform/apply_transformation - Applies file content transformations

=head1 DESCRIPTION

This module performs several tasks. The primary purpose of this module is to
take generated content in some format A and find a way to transform that data
into format A'. 

The format of the content is given in the $context via $context->original_kind.
The "kind" is just a name for some format (not a MIME-type or anything fancy,
just a name). The "kind" the content should be coerced into is determined by the
request itself and the configuration of Contentment. This module attempts to
find zero or more transformations that will coerce the document of the original
kind into a document of the final kind. These transformations can be file format
converters (e.g. XML -> PDF) or merely thematic formatters, or even just bits of
code that modify nothing, but log some information. Whatever.

The importance is that each transformer accept data from one or more input kinds
and produces a document of a single output kind. Transformers are Mason
components. They must have the @input_kinds and the $output_kind attributes
defined to the names of the input kinds and output kind of the transformer,
respectively. Each transformer must also define a $cost attribute, which
associates a numeric cost to the transformation---the higher the value the more
"costly" the transformation. This module will attempt to use the cheapest
transformation available, but may use any found.

=head1 AUTHOR

Andrew Sterling Hanenkamp, E<lt>hanenkamp@users.sourceforge.netE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright 2005 Andrew Sterling Hanenkamp. All Rights Reserved.

Contentment is distributed under the same license and terms as Perl itself.

=cut
</%doc>
<%args>
$from => undef
$to   => undef
</%args>
<%perl>

# Step 0: Force content to be executed first.
my $input = $m->content;
unless (defined $input) {
	warn "No input to transform, not bothing; possible bug in generator.";
	return;
}

# Step 1: Determine the original kind.
my $original_kind = $from || $context->original_kind;
$log->debug("Transformation #1: original kind is $original_kind");

# Step 2: Determine the final kind.
my $final_kind    = $to   || $m->comp("/content/kind/final/kind");
$log->debug("Transformation #2: final kind is $final_kind");

if ($final_kind eq 'unknown') {
	$log->debug("Transformation #2: treating unknown final kind as original kind $original_kind");
	$final_kind = $original_kind;
}

# Step 3: Do we need to perform any transformations?
if ($original_kind eq $final_kind) {
	$log->debug("Transformation #3: original kind is final kind, no transformation needed");
	print $input;
	return;
}
$log->debug("Transformation #3: original kind is not final kind, transformation needed");

# Step 4: Assess the available transformers.
my @dirs = map $$_[1], @comp_dirs;

use Contentment::Util::Transformation;
my $transform = Contentment::Util::Transformation->new;

my $found = 0;
my @files = $vfs->glob('/content/transform/modules/*');
for my $filename (@files) {
	my $file = $vfs->lookup($filename);
	next if $file->is_directory;
	
	$log->debug("Found transformation module $file");
	my $comp = $m->fetch_comp($file->path);
	my @input_kinds = @{ $comp->attr('input_kinds') };
	my $output_kind = $comp->attr('output_kind');
	my $cost = $comp->attr('cost');

	$transform->add_transformation(
		$comp, \@input_kinds, $output_kind, $cost
	);

	$found++;
}

$log->debug("Transformation #4: discovered $found transformations");

# Step 5: Attempt to find the cheapest transformation available.
my $transforms = $transform->shortest_path($original_kind, $final_kind);

(defined $transforms and @$transforms > 0)
	or die "Oops! No way to get from $original_kind kind to $final_kind kind.";

$log->debug("Transformation #5: transformation from original to final in ",scalar(@$transforms)," moves");

# Step 6: Use the transforms to get us there. We can let Mason do all the nasty
# work of feeding the data around, we just loop through our transformations and
# then spew out the result.
my $output;
for my $transform (@$transforms) {
	$log->debug("Transformation #6: Applying transformation $transform");
</%perl>
<&| { store => \$output }, $transform &><% $input %></&>
<%perl>
	$input = $output;
}
</%perl>
<% $output %>
