package Pcore::Core::PerlIOviaWinUniCon;

use Pcore;
use Encode qw[];    ## no critic qw[Modules::ProhibitEvilModules]

use Inline(
    C => <<'C',
void* get_std_handle(U32 std_handle) {
    return GetStdHandle(std_handle);
}

bool is_console(void* handle) {
    CONSOLE_SCREEN_BUFFER_INFO info;

    return GetConsoleScreenBufferInfo(handle, &info);
}

bool write_console(void* handle, wchar_t* buff) {
    U32 write_size;

    return WriteConsoleW(handle, buff, wcslen(buff), &write_size, NULL);
}
C
    ccflagsex => '-Wall -Wextra -Ofast -std=c11',
);

my $ANSI_RE           = qr/\e.+?m/sm;
my $STD_OUTPUT_HANDLE = -11;
my $STD_ERROR_HANDLE  = -12;
my $MAX_BUFFER_SIZE   = 20_000;
my $UTF16             = Encode::find_encoding('UTF-16LE');
my $STD_HANDLE        = {
    $STD_OUTPUT_HANDLE => get_std_handle($STD_OUTPUT_HANDLE),
    $STD_ERROR_HANDLE  => get_std_handle($STD_ERROR_HANDLE),
};

sub PUSHED {
    my $self = shift;
    my $mode = shift;
    my $fh   = shift;

    return bless \*PUSHED, $self;
}

sub UTF8 {
    my $self = shift;

    return 1;
}

sub WRITE {
    my $self = shift;
    my $buf  = shift;
    my $fh   = shift;

    my $handle = $fh->fileno == fileno STDOUT ? $STD_HANDLE->{$STD_OUTPUT_HANDLE} : $STD_HANDLE->{$STD_ERROR_HANDLE};

    if ( is_console($handle) ) {    # console handle

        # PerlIO perform utf8::encode($buf) for scalars without UTF8 flag if filehandle has :utf8 layer
        # so we need to decode
        utf8::decode($buf);    # decode octets to utf-8

        for my $str ( split /($ANSI_RE)/sm, $buf ) {
            next if $str eq $EMPTY;

            if ( substr( $str, 0, 1 ) eq qq[\e] ) {    # ANSII escape sequence

                print {$fh} $str;

                $fh->flush;
            }
            else {                                     #
                while ( length $str ) {
                    write_console( $handle, $UTF16->encode( substr $str, 0, $MAX_BUFFER_SIZE, $EMPTY ) . "\N{NULL}" );
                }
            }
        }
    }
    else {                                             # redirected filehandle, |, >, ...
        $buf =~ s/$ANSI_RE//smg;                       # strip ANSI escape sequencies

        print {$fh} $buf;

        $fh->flush;
    }

    return bytes::length $buf;
}

1;
__END__
=pod

=encoding utf8

=head1 NAME

Pcore::Core::PerlIOviaWinUniCon

=head1 SYNOPSIS

=head1 DESCRIPTION

=cut
