#!perl

use strict;
use warnings;
use Shell::Var::Reader;
use TOML         qw(to_toml from_toml);
use JSON         qw(to_json decode_json);
use YAML         qw(Load);
use Getopt::Long qw(:config pass_through);
use Data::Dumper;
use String::ShellQuote;
use Hash::Merge;
use File::Slurp;

my $version = '0.2.0';

my $to_read;
my $format = 'json';
my $pretty = 0;
my $sort   = 0;
my $version_flag;
my $help;
my @includes;
GetOptions(
	'r=s'     => \$to_read,
	'o=s'     => \$format,
	'p'       => \$pretty,
	's'       => \$sort,
	'h'       => \$help,
	'help'    => \$help,
	'v'       => \$version_flag,
	'version' => \$version_flag,
	'i=s'     => \@includes,
);

if ($version_flag) {
	print 'shell_var_reader v. ' . $version . "\n";
	exit 255;
}

if ($help) {
	print 'shell_var_reader v. ' . $version . '

-r <file>     File to read/run
-o <format>   Output formats
              Default: json
              Formats: json,yaml,toml,dumper(Data::Dumper),shell
-p            Pretty print
-s            Sort
-i <include>  Include file info. May be used multiple times.

-h/--help     Help
-v/--version  Version


Include Examples...
-i foo,bar.json       Read in bar.json and include it as the variable foo.
-i foo.toml           Read in foo.toml and merge it with what it is being merged into taking presidence.
-i a.jsom -i b.toml   Read in a.json and merge it, then read in in b.json and merge it.

';
	exit 255;
} ## end if ($help)

if ( !defined($to_read) ) {
	die('No file specified to read via -r');
}

if ( $format ne 'json' && $format ne 'yaml' && $format ne 'toml' && $format ne 'dumper' && $format ne 'shell' ) {
	die( "'" . $format . "' is not a recognized format" );
}

my $found_vars = Shell::Var::Reader->read_in($to_read);

my $merger = Hash::Merge->new('LEFT_PRECEDENT');
foreach my $include (@includes) {
	my ( $include_as, $include_file ) = split( /,/, $include, 2 );
	my $merge = 0;
	if ( !defined($include_file) ) {
		$include_file = $include_as;
		$merge        = 1;
	}

	my $raw_include = read_file($include_file) || die( 'Failed to read "' . $include_file . '"' );

	my $parsed_include;

	if ( $include_file =~ /[Jj][Ss][Oo][Nn]$/ ) {
		eval { $parsed_include = decode_json($raw_include); };
		if ($@) {
			die( 'Parsing "' . $include_file . '" failed... ' . $@ );
		}
	} elsif ( $include_file =~ /([Yy][Mm][Ll]|[Yy][Aa][Mm][Ll])$/ ) {
		eval { $parsed_include = Load($raw_include); };
		if ($@) {
			die( 'Parsing "' . $include_file . '" failed... ' . $@ );
		}
	} elsif ( $include_file =~ /[Tt][Oo][Mm][Ll]$/ ) {
		eval {
			my $err;
			( $parsed_include, $err ) = from_toml($raw_include);
			unless ($parsed_include) {
				die($err);
			}
		};
		if ($@) {
			die( 'Parsing "' . $include_file . '" failed... ' . $@ );
		}
	} ## end elsif ( $include_file =~ /[Tt][Oo][Mm][Ll]$/ )

	if ($merge) {
		my %tmp_hash = %{ $merger->merge( $found_vars, $parsed_include ) };
		$found_vars = \%tmp_hash;
	} else {
		$found_vars->{$include_as} = $parsed_include;
	}
} ## end foreach my $include (@includes)

# print in the requested format
if ( $format eq 'toml' ) {
	print to_toml($found_vars);
} elsif ( $format eq 'yaml' ) {
	if ( !$sort ) {
		$YAML::SortKeys = 0;
	}
	print Dump($found_vars);
} elsif ( $format eq 'json' ) {
	my $json = JSON->new;
	$json->canonical($sort);
	$json->pretty($pretty);
	print $json->encode($found_vars);
	if ( !$pretty ) {
		print "\n";
	}
} elsif ( $format eq 'dumper' ) {
	print Dumper($found_vars);
} elsif ( $format eq 'shell' ) {
	my @keys = keys( %{$found_vars} );
	foreach my $key (@keys) {
		print $key. '=' . shell_quote( $found_vars->{$key} ) . "\n";
	}
}

exit 0;

=head1 NAME

shell_var_reader - Read/run a shell script and return set variable in it.

=head1 SYNOPSIS

shell_var_reader B<-r> <file> [B<-o> <format>] [B<-p>] [B<-s>]

=head1 FLAGS

=head2 -r <file>

The file to read/run.

=head2 -o <format>

The output format.

Default: json

Formats: json,yaml,toml,dumper(Data::Dumper),shell

=head2 -p

Pretty print. Not relevant to all outputs.

=head2 -s

Sort. Not relevant to all outputs.

=head2 -i <include>

Files to parse and include in the produced JSON, TOML, or YAML.

The included file may be either JSON, TOML, or YAML.

If a comma is included, everything before the comma is used as the
key name to include the parsed data as. Otherwise it will me merged.

Include Examples...

    Read in bar.json and include it as the variable foo.
    -i foo,bar.json

    Read in foo.toml and merge it with what it is being merged into taking presidence.
    -i foo.toml

    Read in a.json and merge it, then read in in b.json and merge it.
    -i a.jsom -i b.toml

=cut
