package Apache::AxKit::Pipeline::LibXSLT;

use strict;
use vars qw/@ISA $VERSION %DEPENDS/;
use XML::LibXSLT 1.30;
use XML::LibXML;
use Apache;
use Apache::Request;
use Apache::AxKit::Pipeline;
use Apache::AxKit::Provider;
use Apache::AxKit::LibXMLSupport;
use File::Basename qw(dirname);

@ISA = 'Apache::AxKit::Pipeline';

$VERSION = 1.0; # this fixes a CPAN.pm bug. Bah!

my %style_cache;

sub new{
	my $class = shift;
	my $self = $class->SUPER::new(@_);
	$self->{'style_depends'} = {};
	$self->{'_stylesheet'} = undef;
	return $self;
}
	
sub add_style_depends {
	my $self = shift;
	foreach ( @_ ) {
		$self->{'style_depends'}->{$_}++
	}
}

sub get_style_depends {
    return keys %{shift->{'style_depends'}};
}


sub get_dom {
    my $self = shift;
    my ( $r ) = @_;
    
    my ($xml_doc);
    my $stage = $self->stage();
            
    my $xml_doc = $self->upstream()->get_dom( $r );        
    
	if( !$xml_doc) { 
		Apache::AxKit::Exception::Error->throw(
			-text => "LibXSLT[$stage] Nothing to process"
		) 
	}

    $xml_doc->process_xinclude();
   
    # get request form/querystring parameters
    my @params = fixup_params($self->get_params($r));

	my $stylesheet = $self->get_stylesheet($r);
	
    AxKit::Debug(7, "LibXSLT[$stage]: Performing transformation");

    my $results = $stylesheet->transform($xml_doc, @params);
    
    AxKit::Debug(7, "LibXSLT[$stage]: Transformation finished");
    
    return $results;
}

sub get_stylesheet{
	my $self = shift;
	my $r = shift;
	my $stage = $self->stage();
    AxKit::Debug(7, "LibXSLT[$stage]: Parsing stylesheet");
	
	return $self->{_stylesheet} if $self->{_stylesheet};
	
	my $style = $self->style_provider( $r );
	
    my $stylesheet;
    my $cache = $style_cache{$style->key()};
    if (ref($cache) eq 'HASH' && !$style->has_changed($cache->{mtime}) && ref($cache->{depends}) eq 'ARRAY') {
        AxKit::Debug(8, "LibXSLT[$stage]: Checking if stylesheet is cached");
        my $changed = 0;
        DEPENDS:
        foreach my $depends (@{ $cache->{depends} }) {
            my $p = Apache::AxKit::Provider->new_style_provider($r, key => $depends);
            if ( $p->has_changed( $cache->{mtime} ) ) {
                $changed = 1;
                last DEPENDS;
            }
        }
        if (!$changed) {
            AxKit::Debug(7, "LibXSLT[$stage]: Stylesheet cached");
            $stylesheet = $cache->{style};
            $self->add_style_depends( @{$cache->{depends}} );
        }
    }
    
    if (!$stylesheet || ref($stylesheet) ne 'XML::LibXSLT::Stylesheet') {
        my $style_uri = $style->apache_request->uri();
        AxKit::Debug(7, "LibXSLT[$stage]: parsing stylesheet $style_uri");
        my $style_doc = $style->get_dom();
        
        Apache::AxKit::LibXMLSupport->reset;
        local $Apache::AxKit::LibXMLSupport::provider_cb = 
            sub {
                my $r = shift;
                my $provider = Apache::AxKit::Provider->new_style_provider($r);
                $self->add_style_depends($provider->key());
                return $provider;
            };
    
        $stylesheet = XML::LibXSLT->parse_stylesheet($style_doc) or 
        	Apache::AxKit::Exception::Error->throw(
        		-text => "LibXSLT[$stage]: Not a stylesheet! ".$style_uri
        	); 
        
        unless ($r->dir_config('AxDisableXSLTStylesheetCache')) {
            $style_cache{$style->key()} = 
                { style => $stylesheet, mtime => time, depends => [ $self->get_style_depends() ] };
        }
    }	
	return $self->{_stylesheet} = $stylesheet;	
}

sub get_strref{
	# This will by-pass any xsl:output method.
	my ($self, $r ) = @_;
	my $str = $self->get_dom( $r )->toString();
	return \$str;
}
	
	
sub get_bytes_ref{
	# This takes into account any xsl:output methods.
	# Since the output might not be xml...
	my ($self, $r ) = @_;
	my $results = $self->get_dom( $r );
	
	my $str;
	
	if( $AxKit::Pipeline->post_styles_pending()) {
		# We're no longer last in chain....
		my $provider = Apache::AxKit::Provider::DOM->new( $r, $results );
		$str = $AxKit::Pipeline->create_post_pipeline( $r, $provider )->get_bytes_ref( $r );	
	
	} else {
	
		my $stylesheet = $self->get_stylesheet($r);
		if ($XML::LibXSLT::VERSION >= 1.03) {
			my $encoding = $stylesheet->output_encoding;
			my $type = $stylesheet->media_type;
			
			$r->content_type("$type; charset=$encoding");	
		}

		$str = $stylesheet->output_string($results);
	}
	return \$str;
}



sub fixup_params {
    my @results;
    while (@_) {
        push @results, XML::LibXSLT::xpath_to_string(
                splice(@_, 0, 2)
                );
    }
    return @results;
}

1;
