use XML::Validator::Schema;
use XML::NamespaceSupport;
use XML::SAX::Base;

package W3C::SPDL::CMParser;
@W3C::SPDL::CMParser::ISA = qw(XML::SAX::Base);

sub TNS {
    my ($self, $value) = @_;
    return "{$self->{TNS}}$value";
}

sub NS {
    my ($self, $value) = @_;
    my ($ns, $prefix, $lname) = $self->{NSHelper}->process_element_name($value);
    return "{$ns}$lname";
}

sub start_element {
    my ($self, $data) = @_;
    my $name = $data->{LocalName};
    my $namespace = $data->{NamespaceURI};
    my $qualname = "{$namespace}$name";

    if (!exists $self->{Stack}[-1]{$qualname}) {
	my $stackStr = join("\n", (map {"<$_->{_name}>"} @{$self->{Stack}}), "<$qualname>");
	die "unexpected:\n$stackStr";
    }
    my $goto = $self->{Stack}[-1]{$qualname};
    push (@{$self->{Stack}}, $goto);
    if (my $code = $goto->{_start}) {
	&$code($self, $data);
    }
}

sub end_element {
    my ($self, $data) = @_;
    my $name = $data->{LocalName};
    my $namespace = $data->{NamespaceURI};
    my $qualname = "{$namespace}$name";

    my $gotFrom = pop (@{$self->{Stack}});
    if (my $code = $gotFrom->{_end}) {
	&$code($self, $data);
    }
}


package W3C::SPDL::WSDL::Parser;
@W3C::SPDL::WSDL::Parser::ISA = qw(W3C::SPDL::CMParser);

use vars qw($wsdl $xsd $soap $CM);
$wsdl = 'http://schemas.xmlsoap.org/wsdl/';
$xsd = 'http://www.w3.org/2001/XMLSchema';
$soap = 'http://schemas.xmlsoap.org/wsdl/soap/';
$CM = { _name => '--root--', 
    "{$wsdl}definitions" => { _name => "{$wsdl}definitions", 
	_start => sub {
	    my ($self, $data) = @_;
	    $self->{TNS} = $data->{Attributes}{'{}targetNamespace'}{Value};
	}, 
	"{$wsdl}types" => { _name => "{$wsdl}types", 
	    _start => sub {
		my ($self, $data) = @_;
		$self->{HandlerStack}->set_handler($self->{SchemaValidator}->getXSDhandler());
	    }, 
	    _end => sub {
		my ($self, $data) = @_;
		$self->{SchemaValidator}->finish_parser($self->{URI});
	    }
	}, 

	"{$wsdl}message" => { _name => "{$wsdl}message", 
	    _start => sub {
		my ($self, $data) = @_;
		$self->{MESSAGENAME} = $data->{Attributes}{'{}name'}{Value};
	    }, 
	    "{$wsdl}part" => { _name => "{$wsdl}part", 
		_start => sub {
		    my ($self, $data) = @_;
		    $self->{SPAT}->addMessage($self->TNS($self->{MESSAGENAME}), $self->NS($data->{Attributes}{'{}element'}{Value}));
		}, 
	    },
	}, 
	"{$wsdl}portType" => { _name => "{$wsdl}portType", 
	    _start => sub {
		my ($self, $data) = @_;
		$self->{PORTTYPE} = $data->{Attributes}{'{}name'}{Value};
	    }, 
	    "{$wsdl}operation" => { _name => "{$wsdl}operation", 
		_start => sub {
		    my ($self, $data) = @_;
		    $self->{OPERATIONNAME} = $data->{Attributes}{'{}name'}{Value};
		}, 
		"{$wsdl}input" => { _name => "{$wsdl}input", 
		    _start => sub {
			my ($self, $data) = @_;
			$self->{INPUTMESSAGE} = $data->{Attributes}{'{}message'}{Value};
		    }, 
		},
		"{$wsdl}output" => { _name => "{$wsdl}output",
		    _start => sub {
			my ($self, $data) = @_;
			$self->{OUTPUTMESSAGE} = $data->{Attributes}{'{}message'}{Value};
		    }, 
		}, 
		_end => sub {
		    my ($self, $data) = @_;
		    $self->{SPAT}->addPorttypeOperation($self->TNS($self->{PORTTYPE}), 
							$self->TNS($self->{OPERATIONNAME}), 
							$self->NS($self->{INPUTMESSAGE}), 
							$self->NS($self->{OUTPUTMESSAGE}));
		}, 
	    }
	}, 
	"{$wsdl}binding" => { _name => "{$wsdl}binding", 
	    _start => sub {
		my ($self, $data) = @_;
		$self->{BINDINGNAME} = $data->{Attributes}{'{}name'}{Value};
		$self->{BINDINGTYPE} = $data->{Attributes}{'{}type'}{Value};
	    }, 
	    "{$soap}binding" => { _name => "{$soap}binding", 
		_start => sub {
		    my ($self, $data) = @_;
		    $self->{SPAT}->setBindingTransport($self->TNS($self->{BINDINGNAME}), 
						       $self->NS($self->{BINDINGTYPE}), 
						       $data->{Attributes}{'{}transport'}{Value});
		}, 
	    }, 
	    "{$wsdl}operation" => { _name => "{$wsdl}operation", 
		_start => sub {
		    my ($self, $data) = @_;
		    $self->{SPAT}->addBindingOperation($self->TNS($self->{BINDINGNAME}), 
						       $self->NS($self->{BINDINGTYPE}), 
						       $self->NS($data->{Attributes}{'{}name'}{Value}));
		}, 
		"{$soap}operation" => { _name => "{$soap}operation", }, 
		"{$wsdl}input" => { _name => "{$wsdl}input", 
		    "{$soap}body" => { _name => "{$soap}body", }, 
		}, 
		"{$wsdl}output" => { _name => "{$wsdl}output", 
		    "{$soap}body" => { _name => "{$soap}body", }, 
		}, 
	    }
	},
	"{$wsdl}service" => { _name => "{$wsdl}service", 
	    _start => sub {
		my ($self, $data) = @_;
		$self->{SERVICNAME} = $data->{Attributes}{'{}name'}{Value};
	    }, 
	    "{$wsdl}port" => { _name => "{$wsdl}port", 
		_start => sub {
		    my ($self, $data) = @_;
		    $self->{PORTNAME} = $data->{Attributes}{'{}name'}{Value};
		    $self->{SERVICEBINDINGNAME} = $data->{Attributes}{'{}binding'}{Value};
		}, 
		"{$soap}address" => { _name => "{$soap}address", 
		    _start => sub {
			my ($self, $data) = @_;
			$self->{SPAT}->addServicePort($self->TNS($self->{SERVICNAME}), 
						      $self->TNS($self->{PORTNAME}), 
						      $self->NS($self->{SERVICEBINDINGNAME}), 
						      $data->{Attributes}{'{}location'}{Value});
		    }, 
		}, 
	    }
	}

    } 
};


sub new {
    my ($proto, $spat, $handlerStack, $schemaValidator) = @_;
    my $class = ref($proto) || $proto;
    my $self = {SPAT => $spat, 
		HandlerStack => $handlerStack, 
		Stack => [$CM], 
		SchemaValidator => $schemaValidator, 
	        NSHelper => $handlerStack->get_namespace_helper()};
#	        NSHelper => XML::NamespaceSupport->new({xmlns => 1})};
    bless ($self, $class);
    return $self;
}


package W3C::SPDL::SOAP::Parser;
@W3C::SPDL::SOAP::Parser::ISA = qw(W3C::SPDL::CMParser);

use vars qw($wsdl $xsd $soap $CM);
$wsdl = 'http://schemas.xmlsoap.org/wsdl/';
$xsd = 'http://www.w3.org/2001/XMLSchema';
$soap = 'http://schemas.xmlsoap.org/wsdl/soap/';
$SOAP_ENV = 'http://schemas.xmlsoap.org/soap/envelope/';
$CM = { _name => '--root--', 
    "{$SOAP_ENV}Envelope" => { _name => "{$SOAP_ENV}Envelope", 
	"{$SOAP_ENV}Body" => { _name => "{$SOAP_ENV}Body", 
	    _start => sub {
		my ($self, $data) = @_;
		$self->{HandlerStack}->set_handler($self->{SchemaValidator});
	    }, 
	}, 
    } 
};


sub new {
    my ($proto, $spat, $handlerStack, $schemaValidator) = @_;
    my $class = ref($proto) || $proto;
    my $self = {SPAT => $spat, 
		HandlerStack => $handlerStack, 
		Stack => [$CM], 
		SchemaValidator => $schemaValidator, 
	        NSHelper => $handlerStack->get_namespace_helper()};
#	        NSHelper => XML::NamespaceSupport->new({xmlns => 1})};
    bless ($self, $class);
    return $self;
}

package W3C::SPDL::WSDL;

1;

