The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use strict;
use warnings;
use Pod::Parser;

our $VERSION = '0.0.2';

our $indent_seq    =   ' ' x 4;            # spaces on every indent level


convert( \*STDIN, \*STDOUT );

sub convert {
    my  ($in_file, $out_file)   =   @_;
    my  $parser                 =   MyParser->new();

    $parser->parse_from_filehandle( $in_file );

    print $out_file $parser->_dump_as_mdwn();

    return;
}

package MyParser;               ##Violates 'Modules::RequireFilenameMatchesPackage'
use base qw(Pod::Parser);

use Data::Dumper;

sub new {
    my  ($class,@params)    =   @_;
    my  $self               =   $class->SUPER::new( @params );

    if ($self) {
        # add private data as a hash ...
        $self->_private();
    }

    return $self;
}

sub _private {
    my  $self   =   shift;

    if (not exists $self->{_MyParser}) {
        $self->{_MyParser} = {
            Text        =>  [],     # final text
            Indent      =>  0,      # list indent levels counter
            ListType    =>  '-',    # character on every item
            searching   =>  undef,  # what are we search ? (title, author, undef ...)
            Title       =>  undef,  # Page title
            Author      =>  undef,  # Page author
        };
    }

    return $self->{_MyParser};
}

sub _dump_as_mdwn {
    my  $parser     =   shift;
    my  $data       =   $parser->_private();
    my  $lines      =   $data->{Text};
    my  @header     =   $parser->_build_mdwn_head();

    return join("\n" x 2, @header, @{ $lines } );
}

sub _build_mdwn_head {
    my  $parser     =   shift;
    my  $data       =   $parser->_private();
    my  $paragraph  =   '';

    if (defined $data->{Title}) {
        $paragraph .= sprintf '[[meta title="%s"]]', $data->{Title};
    }

    if (defined $data->{Author}) {
        $paragraph .= "\n" . sprintf '[[meta author="%s"]]', $data->{Author};
    }

    return $paragraph;
}

sub _save {
    my  ($parser, $text)  =   @_;
    my  $data   =   $parser->_private();

    $text = $parser->_indent_text( $text );

    push @{ $data->{Text} }, $text;

    return;
}

sub _indent_text {
    my  ($parser, $text)    = @_;
    my  $data               = $parser->_private();
    my  $level              = $data->{Indent};
    my  $indent             = undef;

    if ($level > 0) {
        $level --;
    }
    $indent = ' ' x ($level * 4);

    my @lines = map { $indent . $_; } split(/\n/, $text);

    return wantarray ? @lines : join("\n", @lines);
}

sub _clean_text {
    my  $parser     =   shift;
    my  $text       =   shift;
    my  @trimmed    =   grep { $_; } split(/\n/, $text);

    return wantarray ? @trimmed : join("\n", @trimmed);
}

sub command {
    my ($parser, $command, $paragraph, $line_num) = @_;
    my  $data   =   $parser->_private();

    # cleaning the text
    $paragraph = $parser->_clean_text( $paragraph );

    # is it a header ? 
    if ($command =~ m{head(\d)}xms) {
        my  $level = $1;

        # the headers never are indented 
        $parser->_save( sprintf '%s %s', '#' x $level, $paragraph);

        if ($level == 1) {
            if ($paragraph =~ m{NAME}xmsi) {
                $data->{searching} = 'title';
            }
            elsif ($paragraph =~ m{AUTHOR}xmsi) {
                $data->{searching} = 'author';
            }
            else {
                $data->{searching} = undef;
            }
        }
    }
    # opening a list ? 
    elsif ($command =~ m{over}xms) {
        # update indent level     
        $data->{Indent} ++;
    
    # closing a list ?         
    } elsif ($command =~ m{back}xms) {
        # decrement indent level 
        $data->{Indent} --;
    }
    elsif ($command =~ m{item}xms) {
        $parser->_save( sprintf '%s %s', 
                        $data->{ListType}, 
                        $parser->interpolate($paragraph, $line_num) 
                );
    }                 

    # ignore other commands
    return;
}

sub verbatim {
    my ($parser, $paragraph, $line_num) =   @_;

    $parser->_save( $paragraph );
}

sub textblock {
    my  ($parser, $paragraph, $line_num) = @_;
    my  $data   =   $parser->_private();

    # interpolate the paragraph for embebed sequences 
    $paragraph = $parser->interpolate( $paragraph, $line_num );

    # clean the empty lines
    $paragraph = $parser->_clean_text( $paragraph );

    # searching ?
    if ($data->{searching}) {
        if ($data->{searching} =~ m{title|author}xms) {
            $data->{ucfirst $data->{searching}} = $paragraph;
            $data->{searching} = undef;
        }
    }

    # save the text
    $parser->_save( $paragraph );
}


sub interior_sequence {
    my  ($parser, $seq_command, $seq_argument, $pod_seq) = @_;
    my  $data = $parser->_private();
    my  %interiores = (
        'I'     =>  sub { return '_'  . $_[1] . '_' },       # cursive
        'B'     =>  sub { return '__' . $_[1] . '__' },     # bold
        'C'     =>  sub { return '`'  . $_[1] . '`' },       # monospace
        'F'     =>  sub { return '`'  . $_[1] . '`' },       # system path
        'S'     =>  sub { return '`'  . $_[1] . '`' },       # code
        'E'     =>  sub {
                        my ($seq, $charname) = @_;

                        return '<' if $charname eq 'lt';
                        return '>' if $charname eq 'gt';
                        return '|' if $charname eq 'verbar';
                        return '/' if $charname eq 'sol';
                        return $charname;
                    },
        'L'     =>  \&_resolv_link,
    );

    if (exists $interiores{$seq_command}) {
        my $code = $interiores{$seq_command};

        return $code->( $seq_command, $seq_argument, $pod_seq );
    }
    else {
        return sprintf '%s<%s>', $seq_command, $seq_argument;
    }
}

sub _resolv_link {
    my  ($cmd, $arg, $pod_seq) = @_;

    if ($arg =~ m{^http|ftp}xms) {
        # direct link to a URL
        return sprintf '<%s>', $arg;
    }
    else {
        return sprintf '%s<%s>', $cmd, $arg;
    }
}

1;
__END__
=head1 NAME

pod2mdwn  - Convert POD text to Markdown 

=head1 VERSION

This documentation refers to pod2mdwn version 0.0.2.

=head1 USAGE

    $ pod2mdwn < Pod_File > Markdown_File

=head1 DESCRIPTION

This program uses L<Pod::Parser> for convert POD sources into Markdown sources.
It can be considered as alpha software.

=head1 DIAGNOSTICS

The program yet raise any exceptions.

=head1 CONFIGURATION AND ENVIRONMENT

pod2mdwn no need any configuration or environment settings.

=head1 DEPENDENCIES

=over

=item L<Pod::Parser>

=back

=head1 BUGS AND LIMITATIONS

=over 

=item * Don't create pure Markdown texts. 

=item * The links to manpages are incompleted

=back

Please report problems to the author.

Patches are welcome.

=head1 AUTHOR

Víctor Moral <victor@taquiones.net>

=head1 LICENCE AND COPYRIGHT

Copyright (c) 2008 "Víctor Moral" <victor@taquiones.net>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.