#!/usr/bin/perl
# make an executable file

use strict;
use warnings;

sub usage; sub mkexec; sub guesstype; sub guesspurpose;
sub getcmd; sub launch_editor;

my $DEFAULT_TYPE = 'sh';
my $DEFAULT_MODE = 0755;
my $DEFAULT_FORCE = 0;
my $DEFAULT_EDIT = 0;


## .... ##   . . .  .   .    .     .      .       .        .         .
## INIT ## -------------------------------------------------------------------
## '''' ##   ' ' '  '   '    '     '      '       '        '         '

my $force = $DEFAULT_FORCE;
my $edit = $DEFAULT_EDIT;
my $name; my $type; my $purpose; my $edit;

foreach (@ARGV) {
    if (m/-f|--force/) {
        $force++;
    } elsif (m/-e|--edit/) {
        $edit++;
    } elsif (defined $type) {
        $purpose = $_;
    } elsif (defined $name) {
        $type = $_;
    } else {
        $name = $_;
    }
}

usage unless defined $name;
$type = guesstype $name unless defined $type;
$purpose = guesspurpose $name unless defined $purpose;

my $bangs = {
    pl   => `which perl`,
    sh   => '/bin/sh',
    py   => `which python`,
    bash => '/bin/bash',
    sed  => `which sed`,
    bc   => `which bc`
};

my $templates = {
    py => {
        ''      => "if __name__ == '__main__':\n",
        test    => "import unittest\n\n\n"
                   . "class TestCase(unittest.TestCase):\n"
                   . "    pass\n\n\n"
                   . "if __name__ == '__main__':\n"
                   ."    unittest.main()\n",
    },
    pl => {
        ''      => "use strict;\nuse warnings;\n",
        test    => "use strict;\nuse warnings;\nuse Test::More;\n\n",
    }
};

my $editors = [ qw/ vim editor / ];
my $cmds;
$cmds->{vim}->{test}    = "vim --version 2>/dev/null";
$cmds->{vim}->{run}     = "vim +\"normal G\$\" '%s'";
$cmds->{editor}->{test} = "editor --version 2>/dev/null";
$cmds->{editor}->{run}  = "editor '%s'";

unless (exists $templates->{$type}->{$purpose}) {
    warn "undefined purpose '$purpose' for type '$type'\n";
    $purpose = "";
}


## ... ##   # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
## RUN ## ----------------------------------------------------------------- #
## ''' ##   # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

if (exists $bangs->{$type}) {
    $force and -e $name
        and (defined `cp "$name" "$name~"` or die $!);
    (not -e $name or $force)
        and mkexec $name, mkbody($type, $purpose);
    chmod $DEFAULT_MODE, $name;
    launch_editor $name if $edit;
} else {
    die "unknown type: $type\n";
}


## .... ##   ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''-.
## SUBZ ## ----------------------------------------------------------------- :
## '''' ##   ..............................................................-'

sub usage {
    print STDERR "usage: $0 [-f|-E] filename [type] [purpose]\n";
    exit 0;
}

sub guesstype {
    my $name = shift;
    my ($ext) = $name =~ m|\.(\w+)$|;
    return ( $ext ? $ext : $DEFAULT_TYPE);
}

sub guesspurpose {
    my $name = shift;
    my $testword = '[tT][eE][sS][tT]';
    return 'test' if $name =~ m|^test_|i;
    return 'test' if $name =~ m|_test.py$|i;
    return 'test' if $name =~ m|_test.pl$|i;
    return 'test' if $name =~ m|\btest\w*/|i;
    return 'test' if $name =~ m|.*Test/|i;
    return 'test' if $name =~ m|\bt/|;
    return ''
}

sub mkbody {
    my $type = shift;
    my $purpose = shift;
    my $tmpl = "";
    $tmpl .= $templates->{$type}->{$purpose} if exists $templates->{$type};
    return sprintf "#!%s\n%s\n", $bangs->{$type}, $tmpl;
}

sub mkexec {
    my ($name, $body) = @_;
    open EXE, ">", $name    or die "cannot open $name for writing: $!\n";
    -W EXE                  or die "file $name is not writable\n";
    print EXE $body;
    close EXE               or die "cannot close $name: $!\n";
}

sub get_cmd {
    foreach (@$editors) {
        return $cmds->{$_}->{run} if `$cmds->{$_}->{test}`
    }
    warn "no supported editor available\n";
    return;
}

sub launch_editor {
    my $name = shift;
    my $form = get_cmd;
    if ($form) {
        my $command = sprintf get_cmd, $name;
        exec "$command";
    } else { return }
    warn "failed to launch editor: $form\n";
}