require GetWeb::FormatText;
use URI::URL;
use GetWeb::Util;
use HTML::TreeBuilder;
$HTML::TreeBuilder::isBodyElement{'frame'} = 1;

package GetWeb::FormatAnnotated;
@ISA = qw( GetWeb::FormatText );

use Carp;
use strict;

=head1 NAME

GetWeb::FormatAnnotated - Format HTML as annotated text

=head1 SYNOPSIS

 require GetWeb::FormatAnnotated;
 $html = parse_htmlfile("test.html");
 $formatter = new GetWeb::FormatAnnotated;
 print $formatter->format($html);

=head1 DESCRIPTION

GetWeb::FormatAnnotated is a formatter that outputs plain latin1 text,
with annotations identifiying the hypertext links.  It extends
HTML::FormatText.

=head1 SEE ALSO

L<HTML::Formatter>, L<HTML::FormatText>

=head1 COPYRIGHT

Copyright (c) 1997 SatelLife.  All rights reserved.

This library is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

=head1 AUTHOR

Rolf Nelson <rolf@usa.healthnet.org>

=cut

# jfj center <Hn> tags, remove ======= underlining
# jfj always have exactly one space under <Hn> tags
# jfj indent <address> blocks
# jfj handle file upload

sub new
{
    my $type = shift;
    my $baseURL = shift;

    my $self = new GetWeb::FormatText (@_);
    $baseURL = new URI::URL($baseURL);
    $$self{baseURL} = $baseURL;

    my $paLink = [];
    if (defined $baseURL)
    {
	push(@$paLink, "[orig] " . $baseURL -> abs -> as_string);
    }
    else
    {
	push(@$paLink,"[orig]");
    }
    $$self{paLink} = $paLink;

    $$self{paForm} = [];
    $$self{formCount} = 'A';
    bless($self,$type);
    $self;
}

sub form_start
{
    my $self = shift;
    my ($elem) = @_;

    my $paForm = $self -> {paForm};
    push(@$paForm,$elem);

    my $letter = $self -> {formCount}++;
    $elem -> {letter} = $letter;

    $self -> vspace(1);
    $self -> out("<GETWEB: FORM $letter>");
    $self -> vspace(1);

    $elem -> {elementCount} = 'a';
    $elem -> {elementList} = [];

    my $ret = $self -> SUPER::form_start(@_);

    my $phphpaInput = $elem -> {phphpaInput};
    my $phpaInput;
    foreach $phpaInput (values %$phphpaInput)
    {
	my $paInput;
	foreach $paInput (values %$phpaInput)
	{
	    $paInput -> [-1] -> {lastInput} = 1;
	}
    }
    $ret;
}

sub escape_braces
{
    my $self = shift;
    my $text = shift;

    if (defined $self -> {current_form})
    {
	$text =~ s/[\\\[\]]/\\$&/g;
	if ($self -> {input})
	{
	    $text =~ s/\d/\\$&/g
		unless $self -> {href};
	}
    }
    $text;
}

sub out
{
    my $self = shift;
    my $text = shift;

    $text = $self -> escape_braces($text);
    $self -> SUPER::out($text);
}

sub pre_out
{
    my $self = shift;
    my $text = shift;

    $text = $self -> escape_braces($text);
    $self -> SUPER::pre_out($text);
}

sub record_form_element
{
    my ($self, $elem) = @_;

    my $form = $self -> {current_form};
    my $elementList = $form -> {elementList};
    my $elementCount = $form -> {elementCount}++;

    push(@$elementList,$elem);

    $elem -> {letter} = $elementCount;
}

sub note_form_element
{
    my $self = shift;
    my ($elem) = @_;

    $self -> record_form_element(@_);
    my $letter = $elem -> {letter};

    # $self -> out(" {$letter} ");
}

sub input_hidden_start
{
    my $self = shift;
    my ($elem) = @_;

    $self -> record_form_element(@_);
    $self -> SUPER::input_hidden_start(@_);
}

sub input_password_start
{
    my $self = shift;
    my ($elem) = @_;

    $self -> out(" <WARNING: INSECURE> ");
    $self -> SUPER::input_password_start(@_);
}

sub input_text_start
{
    my $self = shift;

    my $ret = eval {$self -> SUPER::input_text_start(@_)};
    $ret = $@?1:$ret;
    $self -> note_form_element(@_);
    $ret;
}

sub input_submit_start
{
    my $self = shift;

    my $ret = eval {$self -> SUPER::input_submit_start(@_)};
    $ret = $@?1:$ret;
    $self -> note_form_element(@_);
    $ret;
}

sub button_out
{
    my ($self,$default,$text) = @_;
    $text = $default unless defined $text;

    # my $letter = $self -> {current_form} -> {letter};

    $self -> vspace(0);

    $self -> {input}++;
    $self -> out(" ");
    $self -> {input}--;
    $self -> out(" $text: check preceding box, forward entire document to GetWeb");

    $self -> vspace(0);
}

sub input_reset_start
{
    0;
}

sub note_if_final
{
    my ($self, $type, $elem) = @_;

    my $form = $self -> {current_form};
    
    my $letter = $elem -> {letter};
    if (! defined $letter)
    {
	$letter = $form -> {elementCount}++;
	my $form = $self -> {current_form};
	defined $form or die "no current form";
	my $phpaInput = $form -> {phphpaInput} -> {$type};

	my $name = $elem -> attr('name');
	my $paInput = $phpaInput -> {$name};

	my $input;
	foreach $input (@$paInput)
	{
	    $input -> {letter} = $letter;
	}

	my $elementList = $form -> {elementList};
	push(@$elementList,$elem);
    }
    
    my $text = "";
    my $currentBoxLetter = $form -> {currentBoxLetter};
    if (defined $currentBoxLetter and $currentBoxLetter ne $letter)
    {
	$text = "-$currentBoxLetter,";
    }
    $text .= $letter if $elem -> {lastInput};
    # $self -> out(" {$text} ") if $text ne "";
}

sub input_radio_start
{
    my $self = shift;

    my $ret = eval {$self -> SUPER::input_radio_start(@_)};
    $ret = $@?1:$ret;
    $self -> note_if_final('radio',@_);
    $ret;
}

sub input_checkbox_start
{
    my $self = shift;

    my $ret = eval{$self -> SUPER::input_checkbox_start(@_)};
    $ret = $@?1:$ret;
    $self -> note_if_final('checkbox',@_);
    $ret;
}

sub select_end
{
    my $self = shift;
    
    $self -> note_form_element(@_);
    my $ret = eval{$self -> SUPER::select_end(@_)};
    $ret = $@?1:$ret;
    $ret;
}

sub textarea_end
{
    my $self = shift;

    $self -> note_form_element(@_);
    my $ret = eval{$self -> SUPER::textarea_end(@_)};
    $ret = $@?1:$ret;
    $ret;
}

# give the user visible links to the frames

sub frame_start
{
    my $self = shift;
    my ($elem) = @_;

    my $src = $elem -> attr('src');
    my $name = $elem -> attr('name');

    $elem -> attr('href',$src);

    $self -> vspace(0);
    $self -> out("FRAME: $name");
    $self -> a_end(@_);
    $self -> vspace(0);
}

sub a_end
{
    my $self = shift;

    my $paLink = $$self{paLink};
    my $count = @$paLink;  #number of links; [orig] is link 0

    my $href = $_[0] -> {'href'};
    my $url = new URI::URL $href, $$self{baseURL};

    # jfj handle specially links pointing back to the same document,
    # jfj handle anchors

    my $footnote = "[$count] " . $url -> abs -> as_string;

    my $scheme = $url -> scheme;
    
    if ((defined $scheme) and
	(($scheme eq 'telnet') or ($scheme eq 'news')))
    {
	$footnote .= " (not implemented)";
    }

    push(@{$paLink}, $footnote);

    $self -> {href}++;
    $self -> {input}++;
    $self -> out($count);
    $self -> {input}--;
    $self -> {href}--;

    $self -> HTML::FormatText::a_end (@_);
}

# client-side image maps

sub map_start
{
    my $self = shift;
    $self -> vspace(1);
    $self -> out("[IMAGE MAP]");
    1;
    #shift -> a_start(@_);
    #1;
}

sub map_end
{
    my $self = shift;
    $self -> vspace(1);
}

sub area_start
{
    my $self = shift;

    # jfj abstract linking to another module
    $self -> a_end(@_);
}

sub option_start
{
    my $self = shift;
    my ($elem) = @_;

    my $value = $elem -> attr('value');
    if (! defined $value)
    {
 	my $content = $elem -> content;
 	$value = join('',@$content);
	$value =~ s/\n/ /g;
	$value =~ s/\s+$//;
	$value =~ s/^\s+//;
	$elem -> attr('value',$value);
    }
    $self -> SUPER::option_start(@_);
}

sub conciseStart
{
    my ($self, $element) = @_;

    my $tag = $element -> tag;
    my $abbrev = new HTML::Element $tag;
    my @preserveUs = qw( name type action method rows multiple );
    my $type = lc $element -> attr('type');
    push(@preserveUs,'value') if (grep ($_ eq $type,
				       (qw( submit radio checkbox
					   image hidden )))
				  or $tag eq 'option');

    # push(@preserveUs,'letter') if $tag eq 'form';
    
    my $preserveMe;
    foreach $preserveMe (@preserveUs)
    {
	my $value = $element -> attr($preserveMe);
	defined $value and
	    $abbrev -> attr($preserveMe,$value);
    }
    my $ret = $abbrev -> starttag;
    $ret;
}

sub annotateForm
{
    my ($self,$form) = @_;

    my $phphpaInput = $form -> {phphpaInput};
    my $formLetter = $form -> {letter};
    my $paElement = $form -> {elementList};

    $self -> out($self -> conciseStart($form));
    $form -> traverse(sub
		      {
			  my ($node, $start, $depth) = @_;
			  
			  my $tag = $node -> tag;
			  return 1 unless grep($_ eq $tag,
					       (qw( input option
						   select textarea )));

			  my $out;
			  if ($start eq 1)
			  {
			      $out = $self -> conciseStart($node);
			      if ($tag ne 'option')
			      {
				  $self -> vspace(0);
			      }
			  }
			  else
			  {
			      return 1 unless grep($_ eq $tag,
						   (qw( select textarea )));
			      
			      $out = $node -> endtag;
			  }
			  $self -> out($out);
			  1;
		      },
		      1);
    $self -> out($form -> endtag);
}

sub end
{
    my $self = shift;

    my $paForm = $self -> {paForm};

    if (@$paForm)
    {
	$self -> vspace(1);
	$self -> out(&GetWeb::Util::getFormRefTag);
	$self -> vspace(0);

	my $form;
	foreach $form (@$paForm)
	{
	    $self -> annotateForm($form);
	}
    }

    my $paLink = $$self{paLink};
    if (@$paLink > 1 or
	$self -> {baseURL} -> abs -> as_string ne "")  # skip if no links
    {
	$self -> vspace(1);
	$self -> out("\n" . &GetWeb::Util::getRefTag);
	
	my $count = 0;
	while (@{$paLink})
	{
	    $self -> vspace(0);
	    my $line = shift @$paLink;
	    my $maxLength = $self -> {rm} - $self -> {lm} + 1;
	    if (length($line) > $maxLength)
	    {
		$maxLength -= 5;
		$line =~ s/.{$maxLength}(?=.{5})/$&\\\n/g;
	    }
	    $self -> out($line);
	}
    }

    $self->HTML::FormatText::end (@_);
}

1;
