#!/usr/bin/perl
use strict;
use warnings;

use YAML::PP;
use YAML::PP::Common qw/ YAML_FLOW_SEQUENCE_STYLE YAML_FLOW_MAPPING_STYLE /;
use YAML::PP::Parser;
use YAML::PP::Emitter;
use YAML::PP::Writer;
use Data::Dumper;
use Encode;
use Getopt::Long;
Getopt::Long::Configure('bundling');
GetOptions(
    'help|h' => \my $help,
    'indent=i' => \my $indent,
    'module|M=s' => \my $module,
    'dump|D=s' => \my $emit,
    'verbose' => \my $verbose,
    'flow=s' => \my $flow,
    'width=i' => \my $width,
) or usage(1);

usage(0) if $help;

$module ||= 'YAML::PP';
$emit ||= $module;

$flow ||= 'no';

my ($file) = @ARGV;
my $yaml;

if ($file) {
    open my $fh, '<', $file or die $!;
    $yaml = do { local $/; <$fh> };
    close $fh;
}
else {
    $yaml = do { local $/; <STDIN> };
}
$yaml = decode_utf8($yaml);

if ($emit eq 'YAML::PP::Ref') {
    $emit = 'YAML::PP';
}
my $parserclass = 'YAML::PP::Parser';
my $emitterclass = 'YAML::PP::Emitter';
if ($module eq 'YAML::PP::LibYAML') {
    eval { require YAML::PP::LibYAML } or die "Module $module not supported: $@";
    $parserclass = 'YAML::PP::LibYAML::Parser';
    $emitterclass = 'YAML::PP::LibYAML::Emitter';
}
elsif ($module eq 'YAML::PP::Ref') {
    eval { require YAML::PP::Ref } or die "Module $module not supported: $@";
    $parserclass = 'YAML::PP::Ref::Parser';
}
if ($emit eq 'YAML::PP::LibYAML') {
    eval { require YAML::PP::LibYAML } or die "Module $emit not supported: $@";
    $emitterclass = 'YAML::PP::LibYAML::Emitter';
}

my @events;
my $parser = $parserclass->new(
    receiver => sub {
        my ($self, undef, $event) = @_;
        push @events, $event;
    },
);
eval {
    $parser->parse_string($yaml);
};
if ($@) {
    for (@events) {
        print YAML::PP::Common::event_to_test_suite($_) ."\n";
    }
    warn "Error parsing: $@";
    exit 1;
}
my $writer = YAML::PP::Writer->new;
my $emitter = $emitterclass->new( indent => $indent, width => $width );
$emitter->set_writer($writer);
for my $event (@events) {
    my $type = $event->{name};
    my $str = YAML::PP::Common::event_to_test_suite($event);
    print "$str\n" if $verbose;
    if ($type eq 'sequence_start_event' or $type eq 'mapping_start_event') {
        if ($flow eq 'no') {
            delete $event->{style};
        }
        elsif ($flow eq 'yes') {
            if ($type eq 'sequence_start_event') {
                $event->{style} = YAML_FLOW_SEQUENCE_STYLE;
            }
            else {
                $event->{style} = YAML_FLOW_MAPPING_STYLE;
            }
        }
    }
    $emitter->$type($event);
}
my $out_yaml = $emitter->writer->output;
print encode_utf8 $out_yaml;

sub usage {
    my ($rc) = @_;
    print <<"EOM";
Usage:

    $0 [options] < file
    $0 [options] file

Options:
    --indent=             Number of spaces for indentation
    --width=              Maximum column width (only used in flow style for now)
    --module, -M          YAML::PP, YAML::PP::LibYAML or YAML::PP::Ref
    --dump, -D            YAML::PP, YAML::PP::LibYAML
    --flow                'no' (default, always output block style), 'yes'
                          (always output flow style), 'keep' (output flow
                          style like in the original input)
EOM
    exit $rc;
}
