package blx::xsdsql::xsd_parser::node::element;
use base(qw(blx::xsdsql::xsd_parser::node));
use strict;
use warnings;
use integer;
use Carp;
use File::Basename;
use blx::xsdsql::ut(qw(nvl));
use blx::xsdsql::xsd_parser::type;


sub trigger_at_start_node {
	my ($self,%params)=@_;
	my $parent_table=$self->_get_parent_table;
	my $isparent_choice=$parent_table->get_attrs_value(qw(CHOICE));
	my ($maxoccurs,$minoccurs) = ($self->_resolve_maxOccurs,$self->_resolve_minOccurs(CHOICE => $isparent_choice)); 

	if (defined (my $name=$self->get_attrs_value(qw(name)))) {
		my $path=$self->_construct_path($name,%params,PARENT => undef);
		if (defined (my $xsd_type=$self->get_attrs_value(qw(type)))) {
			my ($schema,$debug)=($self->get_attrs_value(qw(STACK))->[1],$self->get_attrs_value(qw(DEBUG)));
			my $ty_obj=blx::xsdsql::xsd_parser::type::factory(
					$xsd_type
					,SCHEMA => $schema
					,DEBUG => $debug
			);
			if ($maxoccurs > 1  && ref($ty_obj)=~/::simple$/) {
				$self->_debug(__LINE__,$xsd_type,': simple type with maxOccurs > 1');
				my $column =  $self->get_attrs_value(qw(COLUMN_CLASS))->new(
					PATH					=> $path
					,TYPE					=> $schema->get_attrs_value(qw(ID_SQL_TYPE))
					,MINOCCURS				=> $minoccurs
					,MAXOCCURS				=> $maxoccurs
					,INTERNAL_REFERENCE 	=> 1
					,CHOICE					=> $isparent_choice
					,ELEMENT_FORM 			=> $self->_resolve_form
				);
				if (defined $parent_table->get_xsd_seq) {	   #the table is a sequence or choice
					$column->set_attrs_value(XSD_SEQ => $parent_table->get_xsd_seq); 
					$parent_table->_inc_xsd_seq unless $isparent_choice; #the columns of a choice have the same xsd_seq
				}
				$parent_table->_add_columns($column);
				my $table=$self->get_attrs_value(qw(TABLE_CLASS))->new(
					PATH		    		=> $path
					,INTERNAL_REFERENCE 	=> 1
				);
				$schema->set_table_names($table);
				$table->_add_columns(
					$schema->get_attrs_value(qw(ANONYMOUS_COLUMN))->_factory_column(qw(ID))
					,$schema->get_attrs_value(qw(ANONYMOUS_COLUMN))->_factory_column(qw(SEQ))
				);
				my $value_col=$schema->get_attrs_value(qw(ANONYMOUS_COLUMN))->_factory_column(qw(VALUE));
				$value_col->set_attrs_value(TYPE => $ty_obj,PATH => $path,CHOICE => $isparent_choice);
				$table->_add_columns($value_col);
				$column->set_attrs_value(TABLE_REFERENCE => $table,PATH_REFERENCE => $table->get_path);
				$parent_table->_add_child_tables($table);
				$self->set_attrs_value(TABLE => $table);
			}
			else {
				$self->_debug(__LINE__,$path,': type with maxOccurs <= 1');
				my $column = $self->get_attrs_value(qw(COLUMN_CLASS))->new(
					PATH					=> $path
					,TYPE					=> $ty_obj
					,MINOCCURS				=> $minoccurs
					,MAXOCCURS				=> $maxoccurs
					,CHOICE 				=> $isparent_choice
					,ELEMENT_FORM 			=> $self->_resolve_form
				);
				if (defined $parent_table->get_xsd_seq) {	   #the table is a sequence or choice
					$column->set_attrs_value(XSD_SEQ => $parent_table->get_xsd_seq); 
					$parent_table->_inc_xsd_seq unless $isparent_choice; #the columns of a choice have the same xsd_seq
				}
				$parent_table->_add_columns($column);
			}
		}
		else {   #anonymous type - converted into a table
			$self->_debug(__LINE__,$path,': anonymous type - converted into table');
			my $schema=$self->get_attrs_value(qw(STACK))->[1];
			my $table = $self->get_attrs_value(qw(TABLE_CLASS))->new(
					PATH				=> $path
					,ANONYMOUS_TYPE		=> 1 
			);
			$schema->set_table_names($table);
			my $maxocc=nvl($params{MAXOCCURS},1);
			$table->set_attrs_value(MAXOCCURS => $maxocc) 	if $maxocc > 1;
			$table->set_attrs_value(MAXOCCURS => $maxoccurs) 	if $maxoccurs > 1;
			$table->_add_columns($schema->get_attrs_value(qw(ANONYMOUS_COLUMN))->_factory_column(qw(ID)));
			$parent_table->_add_child_tables($table);

			my $column = $schema->get_attrs_value(qw(COLUMN_CLASS))->new(	 #hoock to the parent the column 
					NAME				=> $name
					,PATH				=> undef
					,TYPE				=> $schema->get_attrs_value(qw(ID_SQL_TYPE))
					,MINOCCURS			=> $minoccurs
					,MAXOCCURS			=> $maxoccurs
					,PATH_REFERENCE		=> $path
					,TABLE_REFERENCE 	=> $table
					,CHOICE				=> $isparent_choice
					,ELEMENT_FORM 		=> $self->_resolve_form
				);
			if (defined $parent_table->get_xsd_seq) {	   #the table is a xs:sequence or a xs:choice 
				$column->set_attrs_value(XSD_SEQ => $parent_table->get_xsd_seq); 
				$parent_table->_inc_xsd_seq unless $isparent_choice; 
			}	
			$parent_table->_add_columns($column);
			my $cols=$parent_table->get_columns;
			my $child_tables=$parent_table->get_child_tables;
			$self->set_attrs_value(
				TABLE 			=> $table
				,TABLE_INDEX 	=> scalar(@$child_tables) - 1
				,PARENT_TABLE 	=> $parent_table
				,COLUMN_INDEX   => scalar(@$cols) - 1
			);
		}
	}
	elsif (defined (my $ref=$self->get_attrs_value(qw(ref)))) {
		$self->_debug(__LINE__,$ref,': element without name and with ref');
		my $path=$self->_construct_path($ref,%params,PARENT => undef);
		my $schema=$self->get_attrs_value(qw(STACK))->[1];
		my $column = $schema->get_attrs_value(qw(COLUMN_CLASS))->new(
			REF					=> 1
			,PATH				=> $path
			,MINOCCURS			=> $minoccurs
			,MAXOCCURS			=> $maxoccurs
			,CHOICE 			=> $isparent_choice
			,ELEMENT_FORM 		=> $self->_resolve_form
		);
		if (defined $parent_table->get_xsd_seq) {	   #the table is a sequence or choice
			$column->set_attrs_value(XSD_SEQ => $parent_table->get_xsd_seq); 
			$parent_table->_inc_xsd_seq unless  $isparent_choice; #the columns of a choice have the same xsd_seq
		}
		$parent_table->_add_columns($column);
		$self->set_attrs_value(TABLE => $parent_table);
	}
	else {
		$self->_debug(__LINE__,Dumper($self));
		confess "node without name or ref is not supported\n";
	}

	return $self;
}


sub _get_type {
	my ($self,%params)=@_;
	my $childs=delete $params{CHILD};
	$childs=$self->get_attrs_value(qw(CHILD)) unless defined $childs;
	confess "CHILD not set\n" unless defined $childs;
	confess "CHILD not array\n" unless ref($childs) eq 'ARRAY';
	confess "not CHILDS element\n" if scalar(@$childs)==0;
	confess "multiply CHILDS element\n" if scalar(@$childs) > 1;
	my $child=$childs->[0];
	my $out={};
	my ($schema,$debug)=($self->get_attrs_value(qw(STACK))->[1],$self->get_attrs_value(qw(DEBUG)));
	$self->_resolve_simple_type($child,undef,$out,%params,SCHEMA => $schema);
	my $ty_obj=ref($out->{base}) eq '' 
			? blx::xsdsql::xsd_parser::type::simple->_new(NAME => $out,SCHEMA => $schema,DEBUG => $debug) 
			: $out->{base};
	return $ty_obj;
}

sub _get_last_node_column { 
	my ($self,$table,%params)=@_;
	my @cols=$table->get_columns;
	my $col=undef;
	while (1) {
		$col=pop @cols;
		last unless defined $col;
		last unless $col->is_sys_attributes or $col->is_attribute or $col->is_pk;
	}	
	return $col;
}

sub trigger_at_end_node {
	my ($self,%params)=@_;
	my $table=$self->get_attrs_value(qw(TABLE));

	if (defined $table && $table->get_attrs_value(qw(ANONYMOUS_TYPE))) {
		if (defined (my $childs=$self->get_attrs_value(qw(CHILD)))) {
			my $parent_table=$self->get_attrs_value(qw(PARENT_TABLE));
			my $col=$self->_get_last_node_column($parent_table);
			confess "link column not found for table ".$table->get_sql_name."\n" unless defined $col;
			my ($schema,$debug)=($self->get_attrs_value(qw(STACK))->[1],$self->get_attrs_value(qw(DEBUG)));
			my $ty_obj=$self->_get_type(CHILD => $childs);
			if ($col->get_max_occurs <= 1) {
				$col->set_attrs_value(TYPE => $ty_obj,TABLE_REFERENCE => undef,PATH_REFERENCE => undef,PATH => $table->get_path);
				$parent_table->_delete_child_tables($self->get_attrs_value(qw(TABLE_INDEX)));
			}
			else {
				my $path=$table->get_path;
				$col->set_attrs_value(INTERNAL_REFERENCE => 1,PATH => $path);
				my $column = $schema->get_attrs_value(qw(COLUMN_CLASS))->new(
					NAME			=> basename($path)
					,PATH			=> $path
					,CHOICE 		=> $table->is_choice
					,TYPE			=> $ty_obj
					,ELEMENT_FORM 	=> $self->_resolve_form
				);
				$table->set_attrs_value(INTERNAL_REFERENCE => 1);
				$table->_add_columns(
					$schema->get_attrs_value(qw(ANONYMOUS_COLUMN))->_factory_column(qw(SEQ))
					,$column
				);
			}
		}
	}
	return $self;
}

1;


__END__


=head1  NAME

blx::xsdsql::xsd_parser::node::element - internal class for parsing schema 

=cut
