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

load_module($_) for qw(DOCSIS::ConfigFile YAML::PP);
my $pp = YAML::PP->new;

run(@ARGV);

sub load_module {
  eval "require $_[-1];1" or die "You need to install $_. Example:\n\n\$ cpanm -n $_\n\n";
}

sub run {
  my $self = bless {args => {}}, __PACKAGE__;
  while (@_) {
    my $arg = shift;
    @$self{qw(action file)} = (run_decode => shift) if $arg =~ /^-d/;
    @$self{qw(action file)} = (run_encode => shift) if $arg =~ /^-e/;
    $self->{version}        = 1     if $arg eq '-V' or $arg eq '--version';
    $self->{output}         = shift if $arg =~ /^-o/;
    $self->parse_config(shift) if $arg =~ /^-c/;
  }

  return print "docsis-configfile version $DOCSIS::ConfigFile::VERSION\n" if $self->{version};
  die $self->usage unless $self->{action};
  $self->can($self->{action})->($self);
}

sub parse_config {
  my ($self, $param) = @_;
  die "Example usage: docsis-configfile -c shared_secret=mysecret\n" unless $param;
  return $self->{args}{$1} = $2 if $param =~ /^(.*?)=(.*)$/;
  my $args = $pp->load_file($param);
  $self->{args}{$_} = $args->{$_} for keys %$args;
}

sub run_encode {
  my ($self) = @_;
  die "File missing.\n" unless $self->{file};
  my $data = $self->{file} eq '-' ? $pp->load_string(stdin()) : $pp->load_file($self->{file});
  $self->_write(DOCSIS::ConfigFile::encode_docsis($data, $self->{args}));
}

sub run_decode {
  my ($self) = @_;
  die "File missing.\n" unless $self->{file};
  my $data = $self->{file} eq '-' ? stdin() : \$self->{file};
  $self->_write($pp->dump_string(DOCSIS::ConfigFile::decode_docsis($data)));
}

sub stdin() {
  return do { local $/; <STDIN> };
}

sub usage {
  my ($self) = @_;
  return <<'HERE';
Decode DOCSIS binary file to YAML:

  $ docsis-configfile -d cm.bin > cm.yaml
  $ docsis-configfile -d mta.bin -o mta.yaml

Encode YAML file to DOCSIS config file:

  $ docsis-configfile -e cm.yaml > cm.bin
  $ docsis-configfile -e mta.yaml -o mta.bin
  $ docsis-configfile -e cm.yaml -o cm.bin -c shared_secret=mysecret
  $ docsis-configfile -e mta.yaml -o mta.bin -c mta_algorithm=md5
  $ docsis-configfile -e cm.yaml -o mta.bin -c mta_algorithm=sha1

Options:

  -e in.yaml      - YAML DOCSIS file to encode
  -d in.bin       - Binary DOCSIS file to decode
  -o out.bin      - Which file to write output to. Default is to write to STDOUT
  -c option=value - Extra config parameters for encoding
  -V, --version   - Show version number

See https://metacpan.org/pod/DOCSIS::ConfigFile for more information.

HERE
}

sub _write {
  my ($self, $data) = @_;
  return print $data unless $self->{output};
  open my $FH, '>', $self->{output} or die "Can't write to $self->{output}: $!\n";
  my $written = syswrite $FH, $data;
  die "Can't write to $self->{output}: $!\n" if !defined $written or $written != length $data;
}
