#!/usr/bin/perl -wCSD

use strict;
use warnings;

use Safe;
use YAML::XS;
use Scalar::Util qw(looks_like_number);

my ($file, @data) = @ARGV;

unless (defined($file)) {
    print {*STDERR} ("1st parameter 'file' is not defined\n");
    exit (1);
}

unless (@data) {
    print {*STDERR} ("2nd parameter 'data' is not defined\n");
    exit (1);
}

my $yaml = YAML::XS::LoadFile($file);
my $compartment = Safe->new();

foreach my $option_data (@data) {
    my ($option, $value) = $option_data =~ m/^([\w\.\[\]]+)=(.+)$/;
    if (!(defined($option)) || !(defined($value))) {
        print {*STDERR} ("Error: can't parse '$option_data'\n");
        exit (1);
    }
    else {
        set_value($option, $value);
    }
}

YAML::XS::DumpFile($file, $yaml);

sub log_debug {
    my $message = shift;
    if (defined($ENV{DEBUG})) {
        print "$message\n";
    }
}

sub set_value {
    my $option = shift;
    my $value = shift;

    my $valref = \$yaml;

    if ($value =~ /^['"]+.*['"]+$/) {
        log_debug ('$value is already quoted.');
    }
    elsif (looks_like_number($value)) {
        log_debug ('Not quoting $value for integers.');
    }
    elsif ($value =~ /^\[.*\]$/) {
        log_debug ('Not quoting $value for arrays.');
        if ($value =~ /^\[\]$/) {
          log_debug ('Empty array, do not check quoting');
        }
        elsif ($value !~ /^\[['"].+['"]\]$/) {
            print {*STDERR} ("Error: Elements in array '$value' should be quoted\n");
            exit (1);
        }
    }
    elsif ($value =~ /^\{.*\}$/) {
        log_debug ('Not quoting $value for hashes.');
    }
    else {
        $value = "\"$value\"";
    }

    # If the value is a float number like N.0 - reval turns it into int N
    # even if it's quoted.
    if ($value =~ /^['"]*(\d+\.\d+)['"]*$/ ) {
      $value = $1;
      log_debug ('$value is a float number so no need to reval() it');
    }
    else {
      $value = $compartment->reval($value);
    }

    for my $component (split(/\./, $option)) {
        if (ref($valref) eq 'SCALAR' && defined(${$valref})) {
            print {*STDERR} ("Key resolved to a SCALAR at '$component'; cannot continue.\n");
            exit (1);
        }
        elsif (looks_like_number($component) && (! defined(${$valref}) || ref(${$valref}) eq 'ARRAY')) {
            $valref = \${$valref}->[$component];
        }
        elsif ($component eq 'APPEND' && ref($$valref) eq 'ARRAY') {
            $valref = \${$valref}->[$#{$$valref}+1];
        }
        elsif (! defined(${$valref}) || ref(${$valref}) eq 'HASH') {
            $valref = \${$valref}->{$component};
        }
        else {
            print {*STDERR} ("Key resolved to a " . ref(${$valref}) . " reference; refusing to overwrite.\n");
            exit (1);
        }
    }

    if (! defined(${$valref}) || ref($valref) eq 'SCALAR') {
        ${$valref} = $value;
    }
    elsif (ref(${$valref}) eq 'ARRAY' && ref($value) eq 'ARRAY') {
        ${$valref} = $value;
    }
    elsif (ref(${$valref}) eq 'HASH' && ref($value) eq 'HASH') {
        ${$valref} = $value;
    }
    else {
        print {*STDERR} ("Key resolved to a " . ref(${$valref}) . " reference; refusing to overwrite.\n");
        exit (1);
    }
}
