#!/usr/bin/perl -w

# #######################################################################
#
# Pyro
#
#  The original version of this script is from the book "Mind Performance
#  Hacks" written by Ron Hale-Evans and published by O'Reilly Media.  It
#  was written to perform random nested substitutions to promote creative
#  thought.  Here is Ron's description:
#
#  The pyro script is so named because it is the indirect descendant of a
#  HyperCard stack called Inspirograph, and because while I was writing
#  it, I was reminded of the Creative Fire of the Stoics, and also
#  because it allows "expyration" (breathing out every possible
#  permutation of the data in a file) as well as "inspyration" (breathing
#  in a few permutations and allowing them to inspire you). It occurs in
#  Hack #20, "Force Your Connections" and Hack #49, "Roll the Dice".
#
#  - Ron Hale-Evans
#
#  This script was subsequently hacked and updated by Tracy Atteberry as
#  follows:
#
#  * Usage notes are now printed when script is run without parameters
#  * Eliminated the use of the temporary file
#  * Fixed small bug involving inconsistent regexes
#  * Allow user to specify number of permutations to generate
#  * Write full "expyrations" to specified output file
#
# #######################################################################


# if we don't have at least two argument then print usage notes and bail
if ($#ARGV + 1 < 2)
{
   &usage();
   exit;
}

my $infilename = $ARGV[0];
my $basevar = "\@$ARGV[1]\@";
my $writetofile = 0;
my $quantity = 1;
if ($ARGV[2])
{
    # if the argument is numeric we use it for the quantity
    if ($ARGV[2] =~ /^\d+$/)
    {
        $quantity = $ARGV[2];
    }
    # otherwise we'll write out to a file by this name
    else
    {
       $writetofile = $ARGV[2];
    }
}

for ($i=0; $i < $quantity; $i++)
{
    my $output = &generateOutput($infilename, $basevar, $writetofile);
    print $output;
}

sub generateOutput
{
    my ($infilename, $basevar, $writetofile) = @_;

    # Seed the output file
    if ($writetofile)
    {
        open(OUTFILE, "> $writetofile")
        or die "Couldn't open $writetofile for writing: $!\n";
        print OUTFILE "$basevar\n";
        close(OUTFILE);
    }

    local $/;
    undef $/;

    open(INFILE, "< $infilename")
    or die "Couldn't open $infilename for reading: $!\n";
    $infilecontents = <INFILE>; 
    close(INFILE);

    $outfilecontents = "$basevar\n";
    if ($writetofile)
    {
        open(OUTFILE, "< $writetofile")
        or die "Couldn't open $writetofile for reading: $!\n";
        $outfilecontents = <OUTFILE>;
        close(OUTFILE);
    }

    # changed this from /\@[A-Za-z0-9]+\@/ to match the regex used below
    while ($outfilecontents =~ /\@\w+\@/)
    {
        local $/;
        undef $/;

        if ($writetofile)
        {
            open(OUTFILE, "< $writetofile")
            or die "Couldn't open $writetofile for reading: $!\n";
            $outfilecontents = <OUTFILE>;
            close(OUTFILE);
        }

        # $baseline is the first line in OUTFILE with a variable.
        if ($outfilecontents =~ /^(.*?)(\@\w+\@)(.*?)$/m)
        {
            $baseline = "$1$2$3";
            $varname=$2;
        }
        chomp $varname;

        if ($infilecontents =~ /\#$varname\n(.*?)\n\n/s)
        {
            $varblock = "$1\n";
        }
        else
        {
            die "Did not find variable $varname in $infilename.\n";
        }

        @varblockarr = split (/\n/, $varblock);
        @outlinesarr = ();

        if (!$writetofile)
        {
            # Generate a random string from the input elements
            $randline = $varblockarr [ rand @varblockarr ];
            $curline = $baseline;
            chomp ($curline);
            chomp ($randline);
            $curline =~ s/$varname/$randline/;
            push (@outlinesarr, $curline);
        }
        else
        {
            # Generate all possible combinations of the input elements
            foreach $varline (@varblockarr)
            {
                $curline = $baseline;
                chomp ($curline);
                chomp ($varline);
                $curline =~ s/$varname/$varline/;
                push (@outlinesarr, $curline);
            }
        }

        $outlines = join ("\n", @outlinesarr);
        $outfilecontents =~ s/\Q$baseline\E/$outlines/s
                          or die "baseline not found.\n";
        $outfilecontents =~ s/\n\n/\n/mg;
        if ($writetofile)
        {
            open(OUTFILE, "> $writetofile")
            or die "Couldn't open $writetofile for writing: $!\n";
            print OUTFILE $outfilecontents;
            close(OUTFILE);
        }
    }

    if ($writetofile)
    {
       $outfilecontents = "Done... check \"$writetofile\" for output.\n";
    }

    return $outfilecontents;
}

sub usage
{
   print <<EOF;

   Usage: pyro datafile varname [number | filename]

   datafile   - data file to use as input
   varname    - data file variable to start with
   number     - specifies the number of outputs desired (defaults to 1)
   filename   - if given will generate all possible outputs to filename

   Examples:

      # generate and show two places
      \$ perl pyro utopia.dat place 2

      # generate all possible places and put in file all.txt
      \$ perl pyro utopia.dat place all.txt

      # more examples
      \$ perl pyro utopia.dat place
      \$ perl pyro utopia.dat place all.txt
      \$ perl pyro whattodo.dat activity 20
      \$ perl pyro whattodo.dat funhome
      \$ perl pyro whattodo.dat funout

EOF
}
