#!/usr/bin/perl
#
# Handle installation of source template files into the destination hierarchy,
# with support for pre-processing and installing as another template that
# requires pre-processing on the host system. Type «tmpl help» for more
# information.
#
# Example usage on a Makefile:
#
# ,---
# build:
#	$(TMPL) inst "ce pro" project.conf  etc/project/main.conf
# `---
#

use strict;
use warnings;

use File::Basename;
use File::Path qw(make_path);
use File::Copy qw(cp);
use Template;

my $PROGNAME = 'tmpl';
my $builddir = $ENV{top_builddir} // dirname($0);

sub warning
{
    my @args = @_;

    warn "$PROGNAME: warning: @args\n";
    return;
}

sub error
{
    my @args = @_;

    warn "$PROGNAME: error: @args\n";
    exit 1;
}

sub tmpl_check_tags
{
    my $src = shift;
    my $use_tags;
    my $has_tags = 1;

    open my $fh, '<', $src or error("cannot open file $src: $!");
    while (<$fh>) {
        if (/^\[%-? TAGS ([^\s]+)/) {
            $use_tags = $1;
        } elsif ($use_tags and /\Q$use_tags\E/) {
            $has_tags = 1;
            last;
        }
    }
    close $fh;

    if ($use_tags && ! $has_tags) {
        error("unused TAGS directive in $src");
    }

    return defined $use_tags;
}

sub tmpl_setup_dst
{
    my ($profile, $dst) = @_;
    my $p_dst = "$builddir/build-$profile/$dst";
    my $basedir = dirname($p_dst);

    if (-e $p_dst) {
        error("destination '$p_dst' already exists");
    } elsif (not -d $basedir) {
        make_path($basedir);
    }

    return $p_dst;
}

sub tmpl_repl
{
    my ($src, $dst, $profile) = @_;

    my $vars = {
        uc $profile, 'true',
    };

    my $tt;

    $tt = Template->new(INCLUDE_PATH => '.')
        or error("cannot instantiate template engine: " . $tt->error);

    $tt->process($src, $vars, $dst)
        or error("cannot pre-process template $src: " . $tt->error);

    return;
}

sub tmpl_copy
{
    my ($src, $dst) = @_;

    cp($src, $dst);
    return;
}

sub tmpl_help
{
  print <<'HELP', $PROGNAME;
Usage: %s <action> [<arguments>]

Commands:
  inst <arguments>    Install a file (pre-processing as necessary).

Arguments:
  <profiles> <src> <dst> [<perms>]

Where <profiles> is a space separated list of the ngcp profiles "ce" or "pro";
<src> and <dst> are pathnames; and <perms> the optional pathname permissions.

A metafile is one of .services, .prebuild and .postbuild.
HELP
    return;
}

my $g_action = shift;

if ($g_action eq 'inst') {
    if (@ARGV < 3 && @ARGV > 4) {
        error("less than 3 or more than 4 arguments");
    }
    my ($g_profiles, $g_src, $g_dst, $g_perm) = @ARGV;

    my $g_meta;
    my $g_dst_ext;

    # Sanity check source.
    if ($g_src =~ /\.(prebuild|services|postbuild)$/) {
        $g_meta = 1;
        $g_dst_ext = '';
    } elsif ($g_src =~ /\.tt2$/) {
        error("source $g_src should not have .tt2 extension");
    } else {
        $g_meta = 0;
        $g_dst_ext = '.tt2';
    }

    # Sanity check destination.
    if ($g_dst =~ /\.(prebuild|services|postbuild)$/) {
        if (not $g_meta) {
          error("destination $g_dst is a metafile, but source is not");
        }
    } elsif ($g_dst =~ /\.tt2$/) {
        error("destination $g_dst should not have .tt2 extension");
    } else {
        if ($g_meta) {
          error("source $g_src is a metafile, but destination is not");
        }
    }

    my $use_tags = tmpl_check_tags($g_src);

    foreach my $p (split ' ', $g_profiles) {
        my $p_dst = tmpl_setup_dst($p, "$g_dst$g_dst_ext");

        if ($use_tags) {
            tmpl_repl($g_src, $p_dst, $p);
        } else {
            tmpl_copy($g_src, $p_dst);
        }

        chmod oct($g_perm), $p_dst if defined $g_perm;
    }
} elsif ($g_action eq 'help') {
    tmpl_help();
} else {
    error("unknown action $g_action");
}
