package DX::Deparse;

use Scalar::Util qw(blessed);
use curry;
use DX::Class;

sub indent_by { '    ' }

sub format {
  my ($self, $thing) = @_;
  local our $Indent_Level = 0;
  $self->_format($thing);
}

sub _format {
  my ($self, $thing) = @_;
  my ($as, $data) = @{blessed($thing) ? $thing->for_deparse : $thing};
  $self->${\"_format_as_${as}"}($data);
}

sub _format_indented {
  my ($self, $cb) = @_;
  our $Indent_Level;
  local $Indent_Level = $Indent_Level + 1;
  my $unindented = $cb->();
  (my $indented = $unindented) =~ s/^/${\$self->indent_by}/mg;
  return $indented;
}

sub _format_as_string {
  my ($self, $val) = @_;
  # TODO: multiline handling
  if ($val =~ /^\w+$/) {
    qq{'${val}'}
  } else {
    qq{{'${val}'}}
  }
}

sub _format_as_symbol { $_[1] }

sub _format_as_maybe_bareword {
  my ($self, $maybe_bareword) = @_;
  # should stringify if required
  return $maybe_bareword;
}

sub _format_as_number { $_[1] }

sub _format_as_boolean { $_[1] ? 'true' : 'false' }

sub _format_as_unset { 'unset' }

sub _format_as_array {
  my ($self, $members) = @_;
  join ' ', '{[', (map $self->_format($_), @$members), ']}';
}

sub _format_as_dict {
  my ($self, $members) = @_;
  join ' ', '{{', (
    map +(
      $self->_format_as_maybe_bareword($_),
      $self->_format($members->{$_}),
    ), sort keys %$members
  ), '}}';
}

sub _format_as_statement {
  my ($self, $parts) = @_;
  join ' ', map $self->_format($_), @$parts;
}

sub _format_as_value_path {
  my ($self, $parts) = @_;
  join '.', map $self->_format_as_maybe_bareword($_), @$parts;
}

sub _format_as_list {
  my ($self, $members) = @_;
  join "\n", '{', (
    map $self->_format_indented($self->curry::_format($_)), @$members
  ), '}';
}

sub _format_as_pairs {
  my ($self, $members) = @_;
  join "\n", '{', (
    map $self->_format_indented(sub {
          $self->_format_as_maybe_bareword($_->[0])
            .' '.$self->_format($_->[1])
        }), @$members
  ), '}';
}

sub _format_as_block {
  my ($self, $members) = @_;
  join "\n", '{', (
    map $self->_format_indented($self->curry::_format($_)), @$members
  ), '}';
}

1;
