package JSON::Conditional;
use 5.006; use strict; use warnings; our $VERSION = '0.01';
use JSON; use Clone qw/clone/;

our $JSON;

BEGIN {
	$JSON = JSON->new->allow_blessed->convert_blessed;
}

sub new {
	bless ($_[1] || {}), $_[0];
}

sub encode {
	$JSON->encode($_[1]);
}

sub decode {
	$JSON->decode($_[1]);
}

sub compile {
	my ($self, $json, $params, $return_struct) = @_;
	
	$json = $JSON->decode($json) unless ref $json;

	$json = $self->itterate($json, $params);

	return $return_struct ? $json : $JSON->encode($json);
}

sub itterate {
	my ($self, $json, $params) = @_;

	my $ref = ref $json;

	if ($ref eq 'HASH') {
		$json = $self->loops(
			$self->conditionals($json, $params),
			$params
		);
		
		for my $key ( keys %{$json} ) {
			my $value = $self->itterate($json->{$key}, $params);
			$value eq 'compiled_null' 
				? delete $json->{$key}
				: do {
					$json->{$key} = $value;
				};
		}

		return keys %{$json} ? $json : 'compiled_null';
	} elsif ($ref eq 'ARRAY') {
		my $i = 0;
		for my $item (@{ $json }) {
			my $value = $self->itterate($item, $params);
			$value eq 'compiled_null'
				? do {
					splice @{$json}, $i, 1;
				}
				: $i++;
		}
	}

	return $json;
}

sub loops {
	my ($self, $json, $params) = @_;

	my %loops = map {
		($_ => delete $json->{$_})
	} qw/for/;

	if ($loops{for}) {
		my $key = delete $loops{for}{key};
		die "no key defined for loop" unless defined $key;
		if ($loops{for}{each}) {
			my @each = ();
			my $map = delete $loops{for}{each};
			die "param $key must be an arrayref" 
				unless (ref($params->{$key}) || "") eq 'ARRAY';
			for (@{$params->{$key}}) {
				my $jsn = $self->conditionals(clone($loops{for}), $_);
				push @each, $jsn if scalar keys %{$jsn};
			}
			$json->{$map} = \@each if scalar @each;
		}
		if ($loops{for}{keys}) {
			my %keys = ();
			my $map = delete $loops{for}{keys};
			die "param $key muse be an hashref"
				unless (ref($params->{$key}) || "") eq 'HASH';
			for my $k (keys %{$params->{$key}}) {
				my $jsn = $self->conditionals(
					clone($loops{for}), 
					$params->{$key}->{$k}
				);
				$keys{$k} = $jsn if scalar keys %{$jsn};
			}
			if (scalar %keys) { 
				$map =~ m/^1$/ ? do {
					for my $k (keys %keys) {
						$json->{$k} = $keys{$k};
					}
				} : do {
					$json->{$map} = \%keys;
				}
			}
		}
	}

	return $json;
}

sub conditionals {
	my ($self, $json, $params) = @_;

	my %keywords = map {
		($_ => delete $json->{$_})
	} qw/if elsif else/;

	if ($keywords{if}) {
		my ($expression) = $self->expressions($keywords{if}, $params);	
		unless ($expression) {
			if ($keywords{elsif}) {
				($expression) = $self->expressions($keywords{elsif}, $params);	
			} 
			unless ($expression) {
				if ($keywords{else}) {
					($expression) = $keywords{else}->{then};
				}
			}
		}
		if ($expression) {
			$json->{$_} = $expression->{$_} for ( keys %{$expression} );
		}
	}
	
	return $json;
}

sub expressions {
	my ($self, $keyword, $params) = @_;

	my $success = 0;
	
	$success = exists $params->{$keyword->{key}}
		if defined $keyword->{exists};	

	my $key = $params->{$keyword->{key}};
	if (defined $key) {
		$success = $key =~ m/$keyword->{m}/
			if !$success && defined $keyword->{m};
		$success = $key !~ m/$keyword->{nm}/
			if !$success && defined $keyword->{nm};
		$success = $key eq $keyword->{eq}
			if !$success && defined $keyword->{eq};
		$success = $key ne $keyword->{ne}
			if !$success && defined $keyword->{ne};
		$success = $key > $keyword->{gt}
			if !$success && defined $keyword->{gt};
		$success = $key < $keyword->{lt}
			if !$success && defined $keyword->{lt}
	}

	($success, $keyword) = $self->expressions($keyword->{or}, $params)
		if ($keyword->{or} && !$success);
	($success, $keyword) = $self->expressions($keyword->{and}, $params)
		if ($keyword->{and} && $success);
	if ($keyword->{elsif} && !$success) {
		$keyword = $keyword->{elsif};
		($success, $keyword) = $self->expressions($keyword, $params);
	}
	($success, $keyword) = ($keyword->{else}->{then}, $keyword->{else}) 
		if ($keyword->{else} && !$success);

	return (($success ? $keyword->{then} : 0), $keyword);
}

1;

__END__

=head1 NAME

JSON::Conditional - The great new JSON::Conditional!

=head1 VERSION

Version 0.01

=cut

=head1 SYNOPSIS

Quick summary of what the module does.

	use JSON::Conditional;

	my $c = JSON::Conditional->new();
	
	my $json = '{
		"for": {
			"key": "testing",
			"each": "foo",
			"if": {
				"m": "test",
				"key": "test",
				"then": {
					"abc": 123
				}
			},
			"elsif": {
				"m": "other",
				"key": "test",
				"then": {
					"def": 456
				}
			},
			"else": {
				"then": {
					"ghi": 789
				}
			}
		}
	}';

	$json = $c->compile($json, {
		testing => [
			{ test => "other" },
			{ test => "test" },
			{ test => "other" },
			{ test => "thing" },
		]
	}, 1);

	...

   	$json = {
		foo => [
			{ def => 456 },
			{ abc => 123 },
			{ def => 456 },
			{ ghi => 789 },
		]
	}; 


=head1 AUTHOR

LNATION, C<< <email at lnation.org> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-json-conditional at rt.cpan.org>, or through
the web interface at L<https://rt.cpan.org/NoAuth/ReportBug.html?Queue=JSON-Conditional>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.

=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc JSON::Conditional


You can also look for information at:

=over 4

=item * RT: CPAN's request tracker (report bugs here)

L<https://rt.cpan.org/NoAuth/Bugs.html?Dist=JSON-Conditional>

=item * CPAN Ratings

L<https://cpanratings.perl.org/d/JSON-Conditional>

=item * Search CPAN

L<https://metacpan.org/release/JSON-Conditional>

=back


=head1 ACKNOWLEDGEMENTS


=head1 LICENSE AND COPYRIGHT

This software is Copyright (c) 2020 by LNATION.

This is free software, licensed under:

  The Artistic License 2.0 (GPL Compatible)


=cut

1; # End of JSON::Conditional
