package Cpanel::CPAN::Text::Twiddler;

use warnings;
use strict;
use Carp;

use version; our $VERSION = qv('0.0.1');

use Class::Std;
use Class::Std::Utils;
use Cpanel::CPAN::Locale::Maketext::Pseudo;
use List::Cycle;

{
    my %output_ns : ATTR( :get<output_ns> :init_arg<output_ns> :default<Cpanel::CPAN::Text::Twiddler::CLI>);
    my %longest : ATTR( :get<longest> );
    my %start : ATTR( :get<start>     :init_arg<start>     :default<Starting...> );
    my %text : ATTR( :get<text>      :init_arg<text>      :default<Working...> );
    my %end : ATTR( :get<end>       :init_arg<end>       :default<Done!> );
    my %lang : ATTR(                 :init_arg<lang_obj>  :default<> );
    my %sway : ATTR(                 :init_arg<sway>      :default<0>);
    my %cycle;

    sub START {
        my ( $self, $ident, $arg_ref ) = @_;

        $lang{$ident} = ref $arg_ref->{'lang_obj'} && $arg_ref->{'lang_obj'}->can('maketext') ? $arg_ref->{'lang_obj'} : Locale::Maketext::Pseudo->new();

        if ( $arg_ref->{'output_ns'} ) {
            if (   $arg_ref->{'output_ns'}->can('get_output_pre')
                && $arg_ref->{'output_ns'}->can('get_output_str')
                && $arg_ref->{'output_ns'}->can('get_output_pst') ) {
                $output_ns{$ident} = $arg_ref->{'output_ns'};
            }
            else {
                carp $lang{$ident}->maketext( q{'[_1]' does not have required '[_2]' method, defaulting to '[_3]'}, 'output_ns', 'get_output_*', $output_ns{$ident} );
            }
        }

        $longest{$ident} = length( $start{$ident} ) >= length( $end{$ident} ) ? length( $start{$ident} ) : length( $end{$ident} );
        $longest{$ident} = length( $text{$ident} ) if length( $text{$ident} ) > $longest{$ident};

        my @twiddle_me_this;
        my @letters = split '', $text{$ident};
        for my $lidx ( 0 .. $#letters ) {
            my $part = $letters[0];
            for my $nxt ( 1 .. $lidx ) {
                $part .= $letters[$nxt];
            }
            push @twiddle_me_this, $part;
        }

        push @twiddle_me_this, reverse @twiddle_me_this if $sway{$ident};

        $cycle{$ident} = List::Cycle->new( { 'values' => \@twiddle_me_this } );
    }

    sub get_start_twiddler {
        my ($self) = @_;
        $| = 1;    # TODO: this more robust or localized
        return $self->get_output_pre() . $self->get_output_str( $self->get_start() );
    }

    sub get_next_twiddler {
        my ($self) = @_;
        $self->get_output_str( $self->get_next_frame() );
    }

    sub get_next_frame {
        my ($self) = @_;
        $cycle{ ident $self }->next();
    }

    sub get_end_twiddler {
        my ($self) = @_;
        return $self->get_output_str( $self->get_end() ) . $self->get_output_pst();
    }

    sub get_uniq_str {
        my ($self) = @_;
        return ref($self) . '-' . ident($self);
    }

    sub get_output_pre {
        my ($self) = @_;
        return $output_ns{ ident $self }->get_output_pre($self);
    }

    sub get_output_str {
        my ( $self, $string ) = @_;
        return $output_ns{ ident $self }->get_output_str( $self, $string );
    }

    sub get_output_pst {
        my ($self) = @_;
        return $output_ns{ ident $self }->get_output_pst($self);
    }
}

package Cpanel::CPAN::Text::Twiddler::HTML;

sub get_output_pre {
    my ( $output_ns, $twid ) = @_;
    my $id = $twid->get_uniq_str();
    return qq{<div id="$id"></div>\n};
}

sub get_output_str {
    my ( $output_ns, $twid, $string ) = @_;
    my $id = $twid->get_uniq_str();

    require HTML::Entities;
    $string = HTML::Entities::encode($string);

    return qq{<script type="text/javascript">document.getElementById("$id").innerHTML ="$string"</script>\n};
}

sub get_output_pst {
    my ( $output_ns, $twid ) = @_;
    return '';
}

package Cpanel::CPAN::Text::Twiddler::CLI;

sub get_output_pre {
    my ( $output_ns, $twid ) = @_;
    return '';
}

sub get_output_str {
    my ( $output_ns, $twid, $string ) = @_;
    my $len = $twid->get_longest();
    my $bs  = '';

    if ( $string ne $twid->get_start() ) {
        $bs = "\b" x $len;
    }

    return $bs . sprintf( '%-' . $len . 's', $string );
}

sub get_output_pst {
    my ( $output_ns, $twid ) = @_;
    return "\n";
}

1;

__END__

perl -MText::Twiddler -Mstrict -we 'my $t=Text::Twiddler->new();print $t->get_start_twiddler;sleep 1;for (1 .. 10000) { print $t->get_next_twiddler; }print $t->get_end_twiddler;'

perl -MText::Twiddler -Mstrict -we 'my $t=Text::Twiddler->new({output_ns=>"Text::Twiddler::HTML"});print $t->get_start_twiddler;sleep 1;for (1 .. 10000) { print $t->get_next_twiddler; }print $t->get_end_twiddler;'
