# Pod::Lyx -- Convert POD data to a Lyx file.
#
# Copyright 2000 by Richard Jackson <richardj@1gig.net>
#
# This program is free software; you can redistribute it and/or modify it
# under the same terms as Perl itself.
#
# This module is intended to convert Pod to Lyx format ( the Latex WYSIWYM
# editor ).
#

################################################################################
# Modules and declarations
################################################################################

package Pod::Lyx;

require 5.004;

#use Carp qw(carp croak);
use Pod::Parser;
use Text::Tabs;
use locale;

use strict;
use vars qw(@ISA %ESCAPES $VERSION %LyxCodes $VERSION);
$VERSION = 0.25;        # current module version.
@ISA = qw(Pod::Parser);

################################################################################
# Table of supported E<> escapes
################################################################################
# This table was taken from an example pod2lyx converted supplied by
# Amir Karger. And boy am I glad I didn't have to figure this stuff out.
#

%ESCAPES = (
   'amp'       => '&',              #   ampersand
   'lt'        => '<',              #   left chevron, less-than
   'gt'        => '>',              #   right chevron, greater-than
   'quot'      => '"',              #   double quote

   "Aacute"    => "\\i \\'{A}\n",   #   capital A, acute accent
   "aacute"    => "\\i \\'{a}\n",   #   small a, acute accent
   "Acirc"     => "\\i \\^{A}\n",   #   capital A, circumflex accent
   "acirc"     => "\\i \\^{a}\n",   #   sma a, circumflex accent
   "Agrave"    => "\\i \\`{A}\n",   #   capital A, grave accent
   "agrave"    => "\\i \\`{a}\n",   #   small a, grave accent
   "Aring"     => '\\i \\u{A}\n',   #   capital A, ring
   "aring"     => '\\i \\u{a}\n',   #   small a, ring
   "Atilde"    => '\\i \\~{A}\n',   #   capital A, tilde
   "atilde"    => '\\i \\~{a}\n',   #   small a, tilde
   "Auml"      => '\\i \\"{A}\n',   #   capital A, dieresis or umlaut mark
   "auml"      => '\\i \\"{a}\n',   #   small a, dieresis or umlaut mark
   "Ccedil"    => '\\i \\c{C}\n',   #   capital C, cedilla
   "ccedil"    => '\\i \\c{c}\n',   #   small c, cedilla
   "Eacute"    => "\\i \\'{E}\n",   #   capital E, acute accent
   "eacute"    => "\\i \\'{e}\n",   #   small e, acute accent
   "Ecirc"     => "\\i \\^{E}\n",   #   capital E, circumflex accent
   "ecirc"     => "\\i \\^{e}\n",   #   small e, circumflex accent
   "Egrave"    => "\\i \\`{E}\n",   #   capital E, grave accent
   "egrave"    => "\\i \\`{e}\n",   #   small e, grave accent
   "Euml"      => '\\i \\"{E}\n',   #   capital E, dieresis or umlaut mark
   "euml"      => '\\i \\"{e}\n',   #   small e, dieresis or umlaut mark
   "Iacute"    => "\\i \\'{I}\n",   #   capital I, acute accent
   "iacute"    => "\\i \\'{i}\n",   #   small i, acute accent
   "Icirc"     => "\\i \\^{I}\n",   #   capital I, circumflex accent
   "icirc"     => "\\i \\^{i}\n",   #   small i, circumflex accent
   "Igrave"    => "\\i \\`{I}\n",   #   capital I, grave accent
   "igrave"    => "\\i \\`{i}\n",   #   small i, grave accent
   "Iuml"      => '\\i \\"{I}\n',   #   capital I, dieresis or umlaut mark
   "iuml"      => '\\i \\"{i}\n',   #   small i, dieresis or umlaut mark
   "Ntilde"    => '\\i \\~{N}\n',   #   capital N, tilde
   "ntilde"    => '\\i \\~{n}\n',   #   small n, tilde
   "Oacute"    => "\\i \\'{O}\n",   #   capital O, acute accent
   "oacute"    => "\\i \\'{o}\n",   #   small o, acute accent
   "Ocirc"     => "\\i \\^{O}\n",   #   capital O, circumflex accent
   "ocirc"     => "\\i \\^{o}\n",   #   small o, circumflex accent
   "Ograve"    => "\\i \\`{O}\n",   #   capital O, grave accent
   "ograve"    => "\\i \\`{o}\n",   #   small o, grave accent
   "Otilde"    => "\\i \\~{O}\n",   #   capital O, tilde
   "otilde"    => "\\i \\~{o}\n",   #   small o, tilde
   "Ouml"      => '\\i \\"{O}\n',   #   capital O, dieresis or umlaut mark
   "ouml"      => '\\i \\"{o}\n',   #   small o, dieresis or umlaut mark
   "Uacute"    => "\\i \\'{U}\n",   #   capital U, acute accent
   "uacute"    => "\\i \\'{u}\n",   #   small u, acute accent
   "Ucirc"     => "\\i \\^{U}\n",   #   capital U, circumflex accent
   "ucirc"     => "\\i \\^{u}\n",   #   small u, circumflex accent
   "Ugrave"    => "\\i \\`{U}\n",   #   capital U, grave accent
   "ugrave"    => "\\i \\`{u}\n",   #   small u, grave accent
   "Uuml"      => '\\i \\"{U}\n',   #   capital U, dieresis or umlaut mark
   "uuml"      => '\\i \\"{u}\n',   #   small u, dieresis or umlaut mark
   "Yacute"    => "\\i \\'{Y}\n",   #   capital Y, acute accent
   "yacute"    => "\\i \\'{y}\n",   #   small y, acute accent
   "yuml"      => '\\i \\"{y}\n',   #   small y, dieresis or umlaut mark
#TODO how to do ligatures in LyX? Use latin1?
#  "AElig"     => '\\AE',           #   capital AE diphthong (ligature)
#  "aelig"     => '\\ae',           #   small ae diphthong (ligature)
#  "ETH"       => '\\OE',           #   capital Eth, Icelandic
#  "eth"       => '\\oe',           #   small eth, Icelandic
#  "Oslash"    => "\\O",            #   capital O, slash
#  "oslash"    => "\\o",            #   small o, slash
#  "szlig"     => '\\ss{}',         #   small sharp s, German (sz ligature)
#  "THORN"     => '\\L',            #   capital THORN, Icelandic
#  "thorn"     => '\\l',,           #   small thorn, Icelandic
);

# these are LyX codes that can be embeded into a paragraph.
# note the value means that another word follows the current match.
# A 1 means one word follows a 2 means two words follow ect...

%LyxCodes = (
   "\\protected_separator"    => 0,    # a protected separator
   "\\series"                 => 1,    # the begining of a series change..
   "\\shape"                  => 1,    # a shape change
   "\\family"                 => 1,    # a change in font family
   "\\backslash"              => 0,    # a backslash
   # their are more codes but these are the ones we are using for now.
);


################################################################################
# Initialization
################################################################################
# Initialize the object. Must be sure to call our parent initializer...
#
sub initialize {
   my $self = shift;

   # by default use the article textclass..
   $$self{textclass}    = "article" unless defined $$self{textclass};
   $$self{alt}          = 0 unless defined $$self{alt};
   $$self{indent}       = 0;
   $$self{tab}          = 4 unless defined $$self{tab};
   $$self{MODE}         = "";    # the current translation mode we are in.
   $$self{ListLevel}    = 0;     # the current list level
   $$self{listdata}     = "";    # used to hold a list data item...
   $$self{outbuff}      = "";    # used to hold the output buffer.
   $$self{deeper}       = 0;     # used to indicate that we have forced a
                                 # to a deeper indention level.
   $$self{verbatimspace} = 0 unless defined $$self{verbatimspace};
   $$self{over}         = 0;     # indicates wheter or not we have output a
                                 # over command.
   $$self{paragraph}    = 0;     # How many paragraphs have we output while in
                                 # a list/enumerate/itimize context.

   $self->SUPER::initialize;

}

################################################################################
#===============================================================================
# Core overrides
#===============================================================================
################################################################################

################################################################################
# command
################################################################################
# Called for each command paragraph. Gets the command, the associated paragraph,
# the line number, and a Pod::Paragraph object.
#
# Just dispatches the command to a method named the same as the command. =cut
# is handled internally by Pod::Parser.

sub command {
   my $self = shift;
   my $command = shift;
   return if $command eq 'pod';  # don't need to process the pod command...
   $command = 'cmd_' . $command;
   $self->$command(@_);
}

################################################################################
# verbatim
################################################################################
# Called for verbatim paragraph. Gets the paragraph, the line number, and a
# Pod::Paragraph object.
# NOTES:
#  1. We use Lyx-Code for verbatim text.
#  2. Our incomming paragraph has to be reformated such that multiple spaces
#     must be converted into protected spaces.
#  3. If there is a blank line following a line of code it will be included
#     in the paragraph so we must catch this so a protected newline can be
#     inserted. One problem with this is we end up with extra lines in the
#     document that don't need to be there. I have an idea how to fix this
#     But I haven't implemented it yet.
#

sub verbatim {
   my $self = shift;
   return if $$self{EXCLUDE};
   local $_ = shift;
   my($temp);
   my($newlines) = 0;   # number of newlines to insert after a line of verbatim
                        # code...
   my(@tArray);         # temp array holder...
   my(@lines);          # paragraph lines

   # see if this is our first line of verbatam text...
   if ( !($$self{MODE} eq "verbatim" ) ) {
      $self->mode_switch("verbatim");     # call switching code..
      # on a first pass we need to see what the indention level is on the
      # source code or verbatim paragraph...
      # LyX automaticly indents verbatim paragraphs so we are going to
      # remove any leading spaces..
      $$self{indent} = 0;  # zero our indent setting...
      ###################################################################
      # if the pod only contains code in verbatim blocks we may want to
      # reformat it so that it looks nice.

      if (defined( $$self{vcode} ) ) {
         my @chars = split(//, $_); # split the line so we can count the leading
                                    # spaces...
         while ( $chars[$$self{indent}] eq ' ' ) {
            $$self{indent}++;
         }
      }
      else {
         # set our indent level to the current tab stop
         $$self{indent} = $$self{tab}; # this is so it looks ok under LyX....
      }

   } else {
      if ( !($$self{outbuff} eq "" )) {
         $self->output($$self{outbuff});
         $$self{outbuff} = "";
      }
   }

   # Before we do any thing else we need to see how many newlines are at the
   # end of the paragraph so we can format it correctly..
   $temp = length($_);     # Note is will cause problems if the string is
                           # in a multibyte encoding
   $temp--;                # zero base the length...
   @tArray = split(//, $_);   # split the line up so we can take a look at it..
   while ( ( $tArray[$temp] eq "\n" ) and ( $temp >= 0 ) ) {
      $newlines++;
      $temp--;
   }
   $newlines--;   # The end of paragraph will have at least one new line so
                  # we don't want to count it..

   # Ok we now know how many blank lines follow the paragraph.
   # now we need to format it.

   # first we need to split the paragraph up into lines
   @lines = split(/\n/, $_ );

   foreach ( @lines ) {
      # First remove the leading spaces from the line. The max
      # number of spaces removed will be the indent value we got earler on..
      #### forgot to add tab expantion :( NOTE: According to Perlpod tabs are
      # to be assumed to be 8 char's in length. But if you take a look at
      # Text.pm it assumes a 4 char tab!!! :( Which do I use? Hell I'm gona
      # use the 4 chars that Text.pm uses because the output looks right.
      # I need to make this an option...
      $tabstop = $$self{tab};
      $_ = expand($_);

      $temp = $$self{indent};
      # Now remove indent number of spaces...
      # I know this doesn't look good but we can't hard code the indent value
      # as I've run across some pod's that use 3 spaces and some that use 4 spaces
      # and we only want to remove the indented value and no more!
      while ( $temp > 0 ) {
         $_ =~ s/^\s//;
         $temp--;
      }
      # now output the Lyx command to have this data look right..
      $self->output("\\layout LyX-Code\n\n");

      # now pull any newlines off the end of the line..
      s/\n+//;    # we only want 1 newline so lets make sure we only have one.
      s/\\/\n\\backslash \n/g;      # must replace backslases note the space
                                    # before the newline needs to be there.
      s/\s/\n\\protected_separator \n/g;  # replace all spaces
      $self->output($_ . "\n");
   }

   # output any extra newlines that we need. Note: we don't actual out put
   # this here we just format the output line
   if ($newlines) {
      while ($newlines > 0 ) {
         $$self{outbuff} .= "\\newline \n \n"; # LyX puts a space after newline
         $newlines--;                     # not sure why but it does...so we
      }                                   # are going to as well...
   }

}

################################################################################
# textblock
################################################################################
# Called for regular text block. Gets the paragraph, the line number, and
# a Pod::Paragraph object. Perform interpolation and output the results.
#

sub textblock {
   my $self = shift;
   return if $$self{EXCLUDE};

   local $_ = shift;
   my $line = shift;
   my @input_lines;
   my $buffer;

   # before we do anything else check for \ and replace it...
   s/\\/\n\\backslash \n/g;

   if ( !( $$self{MODE} eq "textblock") ) {
      # ok we have to determin how this was called which can be a little tricky.
      # and we have to make some assumptions about certin things...
      # for all list type items we just dumb the paragraph..
      if ( ( $$self{MODE} eq "itemize" ) or ( $$self{MODE} eq "enumerate") ) {
         $_ = $self->interpolate($_, $line);
         $_ = $self->wrap( $_ );
         $self->output( $_ );
         $$self{paragraph}++;
         $$self{MODE} = "";      # must unset the mode.
         return;
      }
      if ( $$self{MODE} eq "list" ) {  # lists must be handled differantly..
         # Ok I ran across an issue where the list name had multiple words
         # in it which would cause the output to not look right. So we need
         # to account for it.
         # $_ = $$self{listdata} . " " . $_;
         # $$self{listdata} = "";
         $_ = $self->interpolate($_, $line);
         $buffer = $self->wrap( $_ );
         # now lets handle the case where their may be more than one word in
         # the list name.
         $_ = $$self{listdata};
         if ( /\\/ ) {  # if there are any back slashes that means their is more
                        # more than one word...
            $_ = $self->interpolate($_, $line);
            $self->output( $_ . "\n "); # note the space is important.
            $self->output($buffer);
         } else {
            $_ = $self->interpolate($_, $line);
            $self->output( $_ . " " . $buffer);
         }

         $$self{paragraph}++;
         $$self{MODE} = "";      # must unset the mode.
         $$self{listdata} = "";  # must unset the list data.
         return;
      }

      $self->mode_switch("textblock");

   }

   #Now actually interpolate and output the paragraph.
   $_ = $self->interpolate($_, $line);
   $_ = $self->wrap( $_ );
   $self->output( "\\layout Standard\n\n" . $_ );

}

################################################################################
# interior_sequence
################################################################################
# Called for interior sequence. Gets the command, argument, and a
# Pod::InteriorSequence object and is expected to return the resulting text.
# Calls code, bold, italic, file, and link to handle those types of
# sequences, and handles S<>, E<>, X<>, and Z<> directly.

sub interior_sequence {
   my $self = shift;
   my $command = shift;
   local $_ = shift;

   # before we continue we need to reconize certin cases that can cause problems
   # in particular putting bold text on one of the =head1 or =head2 lines.
   # in these cases we just return the text with no modifications...
   if ( ($$self{MODE} eq "head1") or ($$self{MODE} eq "head2") ) {
      return $_;
   }

   # first lets deal with the most common cases..
   #### BOLD #########
   if ( $command eq 'B' ) {
      return ( "\n\\series bold \n" . $_ . "\n\\series default \n");
   }

   #### ITALICS ############
   if ( $command eq 'I') {
      return ( "\n\\shape italic \n" . $_ . "\n\\shape default \n");
   }

   ###### CODE OR TYPEWRITER TEXT ######
   if ( $command eq 'C') {    # ran across a issue where the typewritertext
                              # included a \ in it so we need to check for it.
      #s/\\/\n\\backslash \n/g;   # replace \ with the command "\backslash\n"
      return ( "\n\\family typewriter \n" . $_ . "\n\\family default \n");
   }

   #### NON BREAKING SPACES ##########
   if ( $command eq 'S' ) {
      return cmd_space($_);
   }

   ##### LINK OR CROSS REFERENCE ##########
   if ( $command eq 'L' ) {
      return cmd_link($_);     # for now do nothing. I intend to come back to this
                     # once basic functions are working.
   }

   #### FILE NAME #################
   if ( $command eq 'F' ) {
      return $_;     # for now do nothing. I intend to come back to this
                     # at a later date.
   }

   ##### INDEX ################
   if ( $command eq 'I' ) {
      return;        # for now do nothing. I intend to come back to this.
   }


}

################################################################################
#===============================================================================
# Optional overrides..
#===============================================================================
################################################################################

################################################################################
# begin_pod
################################################################################
# This sub is invoked at the beginning of processing for each POD document that
# is encountered in the input.
#
# This is being overriden so that we can write the LyX header information into
# the output file.
#

sub begin_pod {
   my $self = shift;
   my $out_fh = $self->output_handle();
   my $buffer;

   print $out_fh "#pod2lyx created this file on: " . localtime()  . "\n";
   print $out_fh <<HEAR_DOC;
#LyX 1.1 (C) 1995-2000 Matthias Ettrich and the LyX Team
#For more information on LyX see http://www.lyx.org/
#For more information on pod2lyx see http://.....
\\lyxformat 2.15
\\textclass $$self{textclass}
\\language default
\\inputencoding latin1
\\fontscheme default
\\graphics default
\\paperfontsize default
\\spacing single
\\papersize Default
\\paperpackage a4
\\use_geometry 1
\\use_amsmath 0
\\paperorientation portrait
\\leftmargin 17mm
\\topmargin 12mm
\\rightmargin 17mm
\\bottommargin 12mm
\\secnumdepth 3
\\tocdepth 3
\\paragraph_separation indent
\\defskip medskip
\\quotes_language english
\\quotes_times 2
\\papercolumns 1
\\papersides 1
\\paperpagestyle defualt

HEAR_DOC

   # ok the main preamble is done now lets set the title...
   # note lyx_title is set by the class user when they create the class..
   if (defined $$self{lyx_title} ) {
      print $out_fh "\\layout Title\n\n" . $$self{lyx_title} . "\n";
      if ( (defined $$self{footnote}) and !($$self{footnote}) ) {
         print $out_fh "\\begin_float footnote \n\\layout Standard\n\n";
         $buffer = "Generated by pod2lyx V" . $VERSION . " from " . $$self{podfile};
         # Ok I don't know why this is but if the line wrapping mode for foot
         # notes is differant than every thing else. Basicly if $buffer is
         # greater than 82 char's long then the document doesn't look right
         # in LyX also the page break will not be reconized either.
         my $test = length($buffer);
         if ( length($buffer) > 83 ) {
            substr($buffer, 82, 0) = "\n";
         }
         print $out_fh $buffer . "\n\\end_float \n";
      }
      print $out_fh "\\layout Standard\n";
   }

   # insert a page break if the class user asked for it...
   if (defined $$self{lyx_index_break} and $$self{lyx_index_break} ) {
      print $out_fh "\\pagebreak_bottom ";
   }

   # insert a index if the class user asked for it...
   if ( (defined $$self{lyx_index} ) and $$self{lyx_index} ) {
      # do we want an index?
      print $out_fh "\n\n\\begin_inset LatexCommand \\tableofcontents{}\n\n\\end_inset \n\n\n";
   }


# note the blank line needs to be there...

}

################################################################################
# end_pod
################################################################################
# This method is invoked at the end of processing for each POD document.
# we mearly need to output the LyX end code....

sub end_pod {
   my $self = shift;

   $self->output("\\the_end");
}


################################################################################
#
# Addtional methods specific to Lyx.
#
################################################################################

################################################################################
# output
################################################################################
# Used to output or write the file....
#

sub output {
   my $self = shift;
   my $out_fh = $self->output_handle();

   if( defined($_) ) {
      print $out_fh @_;
   }

}


################################################################################
# cmd_head1
################################################################################
# First level heading paragraph...
# This outputs the heading and generates a label so we can do cross referances
# later on in the code...
#

sub cmd_head1 {
   my $self = shift;
   return if $$self{EXCLUDE};

   local $_ = shift;
   my $label = $_;

   $$self{MODE} = "head1";

   $_ = $self->interpolate($_, shift);

   # if the heading is blank return...
   if( $_ =~ /^\n+/ ) {
      return;
   }

   s/\n+//; # srip the newlines off the end so we don't get multiple
            # blank lines in the output stream... We only one one newline
            # so we force it to that...
   $self->output("\\layout Section\n\n" . $_ . "\n");

   $label =~ s/\n//g;
   $label =~ s/\s/_/g;
   $label = "\L$label";    # convert the section name to lower case.
   $self->output("\\begin_inset LatexCommand \\label{sec:" . $label . "}\n\n\\end_inset \n\n\n");

}

################################################################################
# cmd_head2
################################################################################
# Second Level heading
#

sub cmd_head2 {
   my $self = shift;
   return if $$self{EXCLUDE};

   local $_ = shift;
   my $label = $_;

   $$self{MODE} = "head2";

   $_ = $self->interpolate($_, shift);

   if( $_ =~ /^\n+/ ) {
      return;
   }

   s/\n+//; # srip the newlines off the end. We only want one newline so
            # we force the issue...
   $self->output("\\layout Subsection\n\n" . $_ . "\n");

   $label =~ s/\n//g;
   $label =~ s/\s/_/g;
   $label = "\L$label";    # convert the section name to lower case.
   $self->output("\\begin_inset LatexCommand \\label{sec:" . $label . "}\n\n\\end_inset \n\n\n");

}

################################################################################
# cmd_over
################################################################################
# Start a list!!!
# We need to keep track of the nesting level of lists. We don't realy need
# to worry about how much to indent it but we do need to know the lists level
# for cases of lists of lists.

sub cmd_over {
   my $self = shift;
   $$self{ListLevel}++;    # increase our list level...

   if ( $$self{ListLevel} > 1 ) {
      $self->output("\\begin_deeper\n");
   }
}

################################################################################
# cmd_back
################################################################################
# End a list....
# We need to keep track of the nesting level of lists. All we are doing
# here is decrimenting our list level holder..

sub cmd_back {
   my $self = shift;
   $$self{ListLevel}--; ## decriment out list level..
   # we also have to output the command to LyX to know that we are backing out
   # one level..
   if ($$self{ListLevel} > 0 ) {
      $self->output("\\end_deeper\n");
      $$self{MODE} = "";      # this will wipe out a list context..
   }
   if ($$self{over}) {
      $self->output("\\end_deeper\n");
   }

}

###############################################################################
# cmd_item
###############################################################################
# An individual list item...
#
# The problem: Pod::Parser pases each =item into this procedure so we must
# determin what type of item this is. The types we must determin are:
# * which means a built list
# # where # equals sum number means a numberd list
# [a-z] equals some list item...
# so we have three modes we need to account for
# 1) a numbered list or enumerate in LyX speek.
# 2) a bullet list or itemize in LyX speek.
# 3) a word list. or list in LyX speek.

sub cmd_item {
   my $self = shift;
   local $_ = shift;
   my $tempbuff = "";   # temp buffer

   s/^item //;          # strip the item section off the command. Notice the
                        # space we want that gone as well.
   s/\s+$//;            # strip all trailing spaces and newlines

   if ( $_ eq "*" ) {      # we have a bullet list
      if (!( $$self{MODE} eq "itemize") ) {
         $self->mode_switch("itemize");
      }
      $self->output("\\layout Itemize\n\n"); # note the data for the item will
                                             # be handled by the standard
                                             # paragraph handler...
      $$self{paragraph} = 0;  # reset our paragraph counter..
      return;
   }

   if ( /^\d+/ ) { # we have a numbered list...
      if ( !( $$self{MODE} eq "enumerate") ) {
         $self->mode_switch("enumerate");
      }
      $self->output("\\layout Enumerate\n\n");
      $$self{paragraph} = 0;
      return;
   }

   if ( /\w+/ ) { # do we have a list? NOTE the test for a number list must come
                  # first or this will not work correctly...
      if (!($$self{MODE} eq "list" )) {
         $self->mode_switch("list");
      }
      # Ok I ran into another bug :( Where a list would not have an assiated
      # paragraph so we must take care of it here..
      if ( !( $$self{listdata} eq "") ) {
         $tempbuff = $self->interpolate( $$self{listdata} , 0);
         $self->output( $tempbuff . "\n");
      }

      $self->output("\\layout List\n\\labelwidthstring 00.00.0000\n\n");
      # I'm not sure what the labelwidthstring does but it is needed.
      # also if the list head has spaces in it we must escape them...
      s/\s/\n\\protected_separator\n/g;
      $$self{listdata} = $_;
      $$self{paragraph} = 0;
      return;
   }

}

###############################################################################
# cmd_begin
###############################################################################
# Begin a block for a particular translator. Setting VERBATIM triggers
# special handling in textblock().
# What this does is eliminate __PRIVATE__ text blocks...

sub cmd_begin {
   my $self = shift;
   local $_ = shift;

   my($kind) = /^(\S+)/ or return;
   if ($kind eq 'text') {
      $$self{VERBATIM} = 1;
   } else {
      $$self{EXCLUDE} = 1;
   }

}

################################################################################
# cmd_end
################################################################################
# End a block for a particular translator. We assume that all =begin/=end
# pairs are properly closed..
#

sub cmd_end {
   my $self = shift;
   $$self{EXCLUDE} = 0;
   $$self{VERBATIM} = 0;
}

################################################################################
# cmd_for
################################################################################
# Special format sections: for now we just ignore them.
# But we need a place holder so Pod::Parser won't complain :)
#

sub cmd_for {
   return;
}

################################################################################
# wrap
################################################################################
# This function wraps a paragraph such that LyX will understand it correctly..
#
# If I could use Text::Wrap for this I would but the problem is that the
# paragraph can have embeded control seqences that must be formated correctly
# or we will have problems.
# Actualy Text::Wrap works with the current LyX parser but that is no guantee
# that it will work in the future. So I'm writing this to be on the safe side.
#
# Found a bug in this code in that LyX formats a paragraph such that each line
# will wrap at around 78 not 80 and a sentance ends the wraping. NOTE: I call
# this a bug because LyX does somthing differant when savaing a file than what
# I'm doing here. So I'm trying to fixit such that they match as much as
# posable.

sub wrap {
   my $self = shift;
   local $_ = shift;

   my $cols = 80;    # line length
   my @words;        # array of words
   my $output;       # our output paragraph formated correctly.
   my $val;          # current word we are working on.
   my $curlen = 0;   # the current line length.
   my $code = 0;     # are we processing a LyX code?
   my $lines = 0;        # how many lines have we output?


   # all kinds of stuff can be embeded into a paragraph so we need to make sure
   # to catch it all if possible...
   @words = split( /\s+/, $_ );

   foreach $val (@words) {
      # process key words...
      if ( ( defined( $LyxCodes{$val} ) ) or $code ) {
         if ( $code ) {                # are we in a keyword pair?
            $output .= $val . "\n";   # yes so stick the next key word on the end.
            $code = 0;                 # reset our code value.
            $curlen = 0;               # reset our line length..
            next;                      # go back to the top.
         }
         if ( $LyxCodes{$val} == 0 ) { # is this a single key word?
            if ( $curlen > 0 ) {
               chop $output;           # remove trailing space..
               $lines++;               # increment the line counter
                                       # only if there is already part of a line
                                       # in the output stream...
            }
            $output .= "\n" . $val . "\n";    # yes so dump it...
            $curlen = 0;               # reset our line length..
            next;
         } else {                      # has to be a multiword pair
            if ($curlen > 0) {
               chop $output;           # remove trailing space.
               $lines++;
            }
            $output .= "\n" . $val . " ";
            $code = 1;
            next;
         }
      }
      # Ok this is not a key word so we need to process it...
      # Note this section of code will puke when the input has double byte
      # chars in it...After I get the single byte stuff working I'll look
      # into doing double byte chars..if users ask for it that is.

      # I ran into a case where a control sequance starts a paragraph
      # that a space and or a \n just will get through
      # split so we need to check for it... Well actualy after some work
      # what gets through is '' which if this wasn't here
      # whould put a space into the stream :( So I removed the checkes
      # for \n and ' ' but put a check in for '' so we don't add a space
      # into the output stream.
      if ( $val eq '' ) {
         next;    # don't process it....
      }

      if ( ( $curlen + length($val) + 1 ) > $cols ) {
         chop $output;
         $output .= "\n " . $val . " ";
         $curlen = length($val) + 1;
         $lines++;                  # note control lines don't count!
         if ( $val =~ /\.$/ ) {
            $output .= "\n ";
            $lines++;
         }
      } else {
         if ( ( $curlen == 0 ) and ( $lines > 0 ) ) {
            $output .= " " . $val . " ";
            $curlen += length($val) + 2;
         } else {
            if ( $val =~ /\.$/ ) {    # is this an end of sentance?
               $output .= $val . "\n ";
               $curlen = 1;      # the space...
               $lines++;
               next;
            }
            $output .= $val . " ";
            $curlen += length($val) + 1;
         }
      }


   }
   chop $output;
   $output .= "\n";
   return $output;
}

################################################################################
# cmd_space
################################################################################
# this function handles the S<> interer item..
# the problem is that S<> can contain other commands embeded in it so we need
# to watch out for it. Also what we are doing is replacing spaces which makes
# this a little more dificult because we end up having to process this thing
# one char at a time to make it work :( I'm sure a regular expresion guru could
# make this work better but I'm not their yet...

sub cmd_space {
   local $_ = shift;
   my @lines;        # array of chars...
   my $output = '';  # our output formated correctly.

   @lines = split( /\n/, $_ );      # break the input up into lines....

   foreach (@lines) {
      if ( /^\\/ ) {
         $output .= "\n" . $_ . "\n";
      } else {
         s/\s/ \n\\protected_separator \n/g;
         $output .= $_ . "\n";
      }
   }
   return($output);
}

################################################################################
# cmd_link
################################################################################
# This function handles the L<> interer item..
# Links come in mainly two forms:
# 1) links to items contained in this document
# 2) Links to items outside this document.
#
# We are going to try to handle both of these cases.

sub cmd_link {
   local $_ = shift;
   my $manpage;         # manpage link
   my $section;         # section ref.

   my $output = '';     # out output formated correctly...

   # if the pod contained some text for use to output use it...
   if ( /\|/ ) {
      ($output, $manpage) = split(/\|/, $_, 2);
      return $output;
   }
   # Ok the pod did not provide any link text so lests figure out what we need
   # to do..

   # leading and trailing whitespace isn't important; get rid of it..
   #s/^\s+//;
   #s/\s+$//;



   return $_;
}


################################################################################
# mode_switch
################################################################################
# this function is used when switching modes from one type of paragraph to
# another. It handles a few things that need to be cleaned up when switching
# modes...It also sets the current mode when it needs to be set...
#

sub mode_switch {
   my $self = shift;
   my $Caller = shift;     # who called this function....

   if ($$self{MODE} eq "verbatim" ) {
      # what we are doing here is removing trailing newlines so we get a more
      # compact document. We allow the user of this module to set wether or
      # not we do this..
      if ( $$self{verbatimspace} ) {
         $self->output($$self{outbuff});
      }
      $$self{outbuff} = "";
   }

   if ( ($Caller eq "verbatim") and ( ($$self{MODE} eq "list") or
                                      ($$self{MODE} eq "itemize") or
                                      ($$self{MODE} eq "enumerate")) ) {
      if (!($$self{listdata} eq "" ) ) {
         $self->output("$$self{listdata} \n");
      }

   }

   if ( $$self{ListLevel} ) {
      # We are inside a list so we need to take the apropriate acction..
      # NOTE this function does not get called for the first paragraph in a
      # list.
      # if one of the lists is calling us we don't need to worry about it.
      if ( ($Caller eq "list") or ($Caller eq "itemize") or
           ($Caller eq "enumerate") ) {
         if ( $$self{over} ) {
            $self->output("\\end_deeper\n");
            $$self{over} = 0;
         }
         $$self{MODE} = $Caller;
         return;
      }
      if ( $$self{over} ) {      # have we already moved over?
         $self->output("\\end_deeper\n"); # move back...
         # now we know that this is not in a list context so what we have
         # is another paragraph inside the list so we need to also move it
         # over as well...
         $self->output("\\begin_deeper\n");
         $$self{over} = 1;
         $$self{MODE} = $Caller;
         return;
      }
      # other wise we need to fix some things up..
      $self->output("\\begin_deeper\n");
      $$self{over}++;
      $$self{MODE} = $Caller;
      return;
   }

   $$self{MODE} = $Caller;


}

1;