package Apache::AxKit::Pipeline;

use strict;
use AxKit;
use Apache::Constants;
use Apache::Request;
use Apache::AxKit::Provider;
use Apache::AxKit::Provider::Scalar;

sub new{
	my ($class) = shift;
	my %args = @_;
	my $self = {
		upstream		=> $args{'upstream'},
		href			=> $args{'href'},
		stage			=> $args{'stage'},
	};
	bless $self, $class;
	return $self;
}

sub pipeline_init{
	# $self, $r
	shift->upstream()->pipeline_init( @_ );
}	

sub stage{	
	my $self = shift;
	if(@_) {
		$self->{'stage'} = shift;
		return $self;
	} else {
		if($self->{'stage'}) {
			return $self->{'stage'} 
		} else {
			return $self->upstream()->stage();
		}
	}
}

sub href{		return shift->{'href'} }

sub style_provider{ 	
	my $self = shift; 
	my $r = shift;
	unless( $self->{'style_provider'} ) {
	
		my $styleprovider = Apache::AxKit::Provider->new_style_provider( $r, uri => $self->href());
		
		if(!$styleprovider->exists()) {
			throw Apache::AxKit::Exception::Error(
				-text => sprintf("stylesheet '%s' could not be found or is not readable", $self->href())
			);
		}
		
		$self->{'style_provider'}=  $styleprovider ;

	}
	return $self->{'style_provider'};
}


sub upstream{ 	
	my $self = shift; 
	if(@_) { 
		if($self->{'upstream'}) {
			# Allow pipelines to be treated as one object
			
			$self->{'upstream'}->upstream( @_ )
		} else {
			$self->{'upstream'} = shift; 
		}
		return $self;
	} 
	return $self->{'upstream'} 
}

sub output_binary{ 0 };

sub no_cache{ 0; }

sub key{ 
	my $self = shift;
	my $r = shift;
	
	# Cache keys for a pipeline should ideally take into account the paramaters used
	# by each transform at each stage in the pipeline.
	# At the end of the pipeline all parameters should be taken into account.
	# At the start, the input is only the provider (which should add the correct things
	# into it's key), so we can ignore all parameters.....
	# In the middle ideally we should only take into account parameters which are actually used.
	
	return join('', $self->upstream()->key( $r ), $self->stage(), $self->href());

    # use href() over style()->key() as it amounts to the same thing,
    # but href() avoids a SLOW call to $r->lookup_uri()
    # $key .= $self->style()->key() if $self->stylesheet_exists();

}

sub has_changed{ 
	return shift->upstream()->has_changed( @_ ) 
};

sub get_ext_ent_handler{ 
	return shift->upstream()->get_ext_ent_handler( @_ ); 
}

sub cache_required{
	return shift->upstream->cache_required();
}

sub get_dom {
	my $self = shift;
	my $r = shift;

	my $parser = XML::LibXML->new();
	$parser->expand_entities(1);
    Apache::AxKit::LibXMLSupport->reset;
    return $parser->parse_string(${ $self->get_strref( $r )}, $r->uri());
}



sub get_strref{
	# $self, $r
	my $str = shift->get_dom( @_ )->toString();
	return \$str;
}

sub get_bytes_ref{
	my ($self, $r ) = @_;
	my $str_ref = $self->get_strref( $r );	
	if($AxKit::Pipeline->post_styles_pending()){
		my $provider = Apache::AxKit::Provider::Scalar->new( $r, ${$str_ref} );
		$str_ref =  $AxKit::Pipeline->create_post_pipeline( $r, $provider )->get_bytes_ref( $r );
	}
	return $str_ref;
}

sub get_provider{
	my $self = shift;
	my $r = shift;
	my $str_ref = $self->get_strref( $r );
	my $provider = Apache::Axkit::Provider::Scalar->new($r, ${$str_ref} );
	return $provider;
}


sub get_sax{
	my ($self, $r, $parser, $pipeline ) = @_;
	
	$parser->set_handler( Handler => $pipeline );
	
	my $result;
	eval {		
		$result = $parser->parse_string( ${$self->get_strref( $r )} );
	};
	if($@) {
		my $error = $@;
		throw Apache::AxKit::Exception::Error(
    		-text => "SAX Error: $error"
		);
	}
	
	return $result;
}

sub send{
    my ($self, $r) = @_;
    
    # This rebless 'cruft' is still here to enable backwards compatibility
    # with old A::A::Language::* modules which still require it.
    # Let's call this feature deprecated. (sp?), ie, it ain't going
    # to be here for long. 
    
    my $bytes;
    my $result_code; 

	$bytes = $self->get_bytes_ref( $r );
   	$result_code = $r->status();
 
    if (ref($r) eq 'AxKit::Apache') {
        bless $r, 'Apache';
    }
    
    if( $AxKit::Cfg->DoGzip() ) {
		AxKit::Debug(4, "Gzipping output.");    
    	${$bytes} = Compress::Zlib::memGzip( ${$bytes} );
    }

    AxKit::Debug(4, "delivering to browser");
	$r->header_out('Content-Length' => length(${$bytes}));
	$r->send_http_header();
	return DONE if $r->method eq 'HEAD';
	$r->print($$bytes);
   	return OK;
}	

sub enable_dynamic_styles{ 0 };

sub get_params {
    my $class = shift;
    my $r = shift;

    my @xslt_params;
    if (!$r->notes('disable_xslt_params')) {
        my $cgi = Apache::Request->instance($r);
        @xslt_params = map { $_ => ($cgi->param($_))[0] } $cgi->param;
        if (ref($r->pnotes('extra_xslt_params')) eq 'ARRAY') {
            push @xslt_params, @{$r->pnotes('extra_xslt_params')};
        }
        elsif (ref($r->pnotes('extra_xslt_params')) eq 'CODE') {
            $r->pnotes('extra_xslt_params')->($r, $cgi, \@xslt_params);
        }
        elsif (ref($r->pnotes('extra_xslt_params')) eq 'HASH') {
            push @xslt_params, %{$r->pnotes('extra_xslt_params')};
        }
    }
    return @xslt_params;
}

1;
__END__

=head1 NAME

Apache::AxKit::Pipeline - base class for pipeline processors.

=cut
