#!/usr/bin/perl -w
use strict;

use Term::ReadLine;
use Term::ANSIColor qw(:constants uncolor);

use constant COW_NORMAL => '';
use constant COW_DEAD   => '-d';
 
use constant TRUE => 1;
use constant FALSE => 0;

sub pick_colour {
    # Set the text colour to something pleasant
    my @colours = (RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE);
    my $colour = $colours[int(rand() * @colours)];

    print BOLD if (rand() > 0.5);
    print $colour;
}

sub cowsay {
    my $text = shift;
    my $flags = shift || COW_NORMAL;

    chomp $text;
    return if ($text eq '');

    my $cow_pipe = '';
    my $cow_depth = $ENV{'COW_DEPTH'};
    for (my $i = 1; $i < $cow_depth; $i++) {
        $cow_pipe .= '| cowsay -n';
    }

    pick_colour() unless $ENV{'COW_BORING'};
    open COW, "|cowsay $flags $cow_pipe";
    print COW $text;
    close COW;

    print RESET;
}

sub sensible_cowsay {
    my $lines_ref = shift;  
    my $flags = shift || COW_NORMAL;

    # If any output line contains more than one word
    # then print each line in a separate cow; otherwise
    # join the lines together with spaces.

    my @lines = @$lines_ref;

    my $join_together = TRUE;
    foreach my $line (@lines) {
        my @tokens = split /\s/, $line; 
        if (@tokens > 1) {
            $join_together = FALSE;
            last;
        }
    }

    if ($join_together) {
        cowsay(join(' ', map { chomp; $_ } @lines), $flags);
    }
    else {
        foreach my $line (@lines) {
            cowsay($line, $flags);
        }
    }
}

sub cowexec {
    my $cmd = shift;

    # Check for special commands
    if ($cmd =~ /^\s*cd\s+(.*?)\s*$/) {
        chdir($1) or cowsay('The cow says NO!', COW_DEAD);
    }
    elsif ($cmd =~ /^\s*curdle\s+(\w+)\s+(.*?)\s*$/) {
        $ENV{$1} = $2;
    }
    else {
        pipe OUTRD, OUTWR;
        pipe ERRRD, ERRWR;
        my $pid = fork();
        if (0 == $pid) {
            close OUTRD;
            close ERRRD;
            open STDOUT, ">&OUTWR";
            open STDERR, ">&ERRWR";
            exec '/bin/bash -c "' . $cmd . '"';
        }
        else {
            close OUTWR;
            close ERRWR;
            waitpid($pid, 0);
            my @out_lines = <OUTRD>;
            my @err_lines = <ERRRD>;      
            
            sensible_cowsay(\@out_lines);
            sensible_cowsay(\@err_lines, COW_DEAD);
        }
    }
}

sub prompt {
    my $term = shift;

    # Using the readline prompt causes problems with 
    # our ANSI colours...
    
    print RESET;
    #print BLINK if $ENV{'COW_FLASH'};
    #print BOLD, WHITE, UNDERLINE, "moo?";
    #print RESET, " ";
    print "\n";

    return $term->readline('moo? ');
}

sub main {
    $ENV{'COW_DEPTH'} = 1 unless $ENV{'COW_DEPTH'};
    $ENV{'COW_FLASH'} = TRUE unless $ENV{'COW_FLASH'};
    $ENV{'COW_BORING'} = FALSE unless $ENV{'COW_BORING'};

    my $term = new Term::ReadLine 'Cow Shell';
    my $prompt = 'moo? ';
    my $OUT = $term->OUT || \*STDOUT;
 
    print CLEAR;
    while (defined ($_ = prompt($term))) {
        cowexec($_);
        $term->addhistory($_) if /\S/;
    }

    print RESET, "\n";
    system "cowsay byee\\!";
}

&main;
