The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Term::Choose::LineFold;

use warnings;
use strict;
use 5.008003;

our $VERSION = '1.504';

use Exporter qw( import );

our @EXPORT_OK = qw( line_fold print_columns cut_to_printwidth );


use Unicode::GCString;



sub print_columns {
    Unicode::GCString->new( $_[0] )->columns();
}


sub cut_to_printwidth {
    my ( $str, $avail_width, $rest ) = @_;
    my $gc_str = Unicode::GCString->new( $str );
    if ( $gc_str->columns() <= $avail_width ) {
        return $str if ! $rest;
        return $str, '';
    }
    my $left = $gc_str->substr( 0, $avail_width );
    my $left_w = $left->columns();
    if ( $left_w == $avail_width ) {
        return $left->as_string if ! $rest;
        return $left->as_string, $gc_str->substr( $avail_width )->as_string;
    }
    if ( $avail_width < 2 ) {
        die "The terminal width is too small.";
    }
    my ( $nr_chars, $adjust );
    if ( $left_w > $avail_width ) {
        $nr_chars = int( $avail_width / 2 );
        $adjust = int( ( $nr_chars + 1 ) / 2 );
        #$nr_chars = int( $avail_width / 4 * 3 );
        #$adjust = int( ( $avail_width + 7 ) / 8 );
    }
    elsif ( $left_w < $avail_width ) {
        $nr_chars = int( $avail_width + ( $gc_str->length() - $avail_width ) / 2 );
        $adjust = int( ( $gc_str->length() - $nr_chars + 1 ) / 2 );
    }

    while ( 1 ) {
        $left = $gc_str->substr( 0, $nr_chars );
        $left_w = $left->columns();
        if ( $left_w + 1 == $avail_width ) {
            my $len_next_char = $gc_str->substr( $nr_chars, 1 )->columns();
            if ( $len_next_char == 1 ) {
                return $gc_str->substr( 0, $nr_chars + 1 )->as_string if ! $rest;
                return $gc_str->substr( 0, $nr_chars + 1 )->as_string, $gc_str->substr( $nr_chars + 1 )->as_string;
            }
            elsif ( $len_next_char == 2 ) {
                return $left->as_string . ' ' if ! $rest;
                return $left->as_string . ' ' , $gc_str->substr( $nr_chars )->as_string;
            }
        }
        if ( $left_w > $avail_width ) {
            $nr_chars = int( $nr_chars - $adjust );
        }
        elsif ( $left_w < $avail_width ) {
            $nr_chars = int( $nr_chars + $adjust );
        }
        else {
            return $left->as_string if ! $rest;
            return $left->as_string, $gc_str->substr( $nr_chars )->as_string;
        }
        $adjust = int( ( $adjust + 1 ) / 2 );
    }
}


sub line_fold {
    my ( $string, $avail_width, $init_tab, $subseq_tab ) = @_;
    for ( $init_tab, $subseq_tab ) {
        if ( $_ ) {
            s/\s/ /g;
            s/\p{C}//g;
            if ( length > $avail_width / 4 ) {
                $_ = cut_to_printwidth( $_, int( $avail_width / 2 ) );
            }
        }
        else {
            $_ = '';
        }
    }
    $string =~ s/[^\n\P{Space}]/ /g;
    $string =~ s/[^\n\P{C}]//g;
    if ( $string !~ /\n/ && print_columns( $init_tab . $string ) <= $avail_width ) {
        return $init_tab . $string;
    }
    my $trailer = '';
    if ( $string =~ /(\n+)\z/ ) {
        $trailer = $1;
    }
    my @paragraph;

    ROW: for my $row ( split "\n", $string ) {
        my @lines;
        $row =~ s/\s+\z//;
        my @words = split( /(?<=\S)(?=\s)/, $row );
        my $line = $init_tab;

        WORD: for my $i ( 0 .. $#words ) {
            my $tab = $i == 0 ? $init_tab : $subseq_tab;
            my $tab_and_word;
            if ( $i == 0 ) {
                $tab_and_word = $init_tab . $words[$i];
            }
            else {
                ( my $tl_word = $words[$i] ) =~ s/^\s+//;
                $tab_and_word = $subseq_tab . $tl_word;
            }

            if ( print_columns( $tab_and_word ) > $avail_width ) {
                if ( $i != 0 ) {
                    push( @lines, $line );
                }
                my ( $tab_and_cut_word, $rest ) = cut_to_printwidth( $tab_and_word, $avail_width, 1 );
                while ( length $rest ) {
                    push( @lines, $tab_and_cut_word );
                    ( $tab_and_cut_word, $rest ) = cut_to_printwidth( $subseq_tab . $rest, $avail_width, 1 );
                }
                if ( $i == $#words ) {
                    push( @lines, $tab_and_cut_word );
                }
                else {
                    $line = $tab_and_cut_word;
                }
            }
            else {
                if ( print_columns( $line . $words[$i] ) <= $avail_width ) {
                    $line .= $words[$i];
                }
                else {
                    push( @lines, $line );
                    ( my $tl_word = $words[$i] ) =~ s/^\s+//;
                    $line = $subseq_tab . $tl_word;
                }
                if ( $i == $#words ) {
                    push( @lines, $line );
                }
            }
        }
        push( @paragraph, join( "\n", @lines ) );
    }
    return join( "\n", @paragraph ) . $trailer;
}



1;

__END__