package RackMan::Template;

use Moose;
use HTML::Template ();
use HTML::Template::Filter::TT2 ();
use NetAddr::IP;
use RackMan;
use namespace::autoclean;


has _ht_obj => (
    is => "ro",
    isa => "HTML::Template",
    handles => {
        param   => "param",
        output  => "output",
    },
);


#
# BUILDARGS()
# ---------
sub BUILDARGS {
    my $class = shift;
    my %param;

    if (@_ % 2 == 0) {
        %param = @_;
    }
    elsif (ref $_[0] eq "HASH") {
        %param = %{$_[0]};
    }
    else {
        RackMan->error("invalid argument: ",
            lc ref $_[0], "ref instead of hashref");
    }

    my $tmpl = eval { HTML::Template->new(
        %param,
        filter   => \&HTML::Template::Filter::TT2::ht_tt2_filter,
        die_on_bad_params => 0,
    ) };
    if (not $tmpl) {
        (my $error = $@) =~ s/ at .*//sm;
        RackMan->error($error);
    }

    return { _ht_obj => $tmpl }
}


#
# populate_from()
# -------------
sub populate_from {
    my ($self, $rackdev, $rackman) = @_;

    # fetch the common name and FQDN of the host
    my $name    = $rackdev->object_name;
    my $fqdn    = $rackdev->attributes->{FQDN};
    RackMan->error("RackObject '$name' lacks a FQDN") unless $fqdn;

    # fetch the list of regular MAC addresses
    my @mac_addrs = $rackdev->regular_mac_addrs;
    RackMan->error("RackObject '$name' lacks a MAC address") unless @mac_addrs;

    # fetch the list of regular IPv4 addresses
    my @ip_addrs = $rackdev->regular_ipv4addrs;
    RackMan->error("RackObject '$name' lacks an IPv4 address") unless @ip_addrs;

    # Host IPv4 network parameters
    my $host_gateway = $rackdev->default_ipv4_gateway;

    # determine the network mask
    my $haddr = NetAddr::IP->new($ip_addrs[0]{addr}, $host_gateway->{masklen});
    my $host_netmask = $haddr->mask;

    # fetch template parameters specific to the current device
    my %devparam = $rackdev->tmpl_params if $rackdev->can("tmpl_params");

    # fetch the list of attributes, and convert their names to be
    # valid identifiers
    my %attr = %{ $rackdev->attributes };
    for my $name (keys %attr) {
        my $param = lc $name;
        s/,.*$//, s:[./]::g, s/\W+/_/g for $param;
        $attr{$param} = delete $attr{$name};
    }

    # fetch the list of DNS servers
    my @dns_servers
        = split / +/, $rackman->config->val(general => "dns_servers");

    # populate the template
    $self->param(
        type             => $rackdev->object_type,
        name             => $name,
        fqdn             => $fqdn,
        if0_mac          => $mac_addrs[0]{l2address_text},
        if0_ip           => $ip_addrs[0]{addr},
        if0_name         => $ip_addrs[0]{iface},
        gateway          => $host_gateway->{addr} || "0.0.0.0",
        network          => $host_gateway->{network},
        netmask          => $host_netmask,
        dns_server_1     => $dns_servers[0],
        dns_server_2     => $dns_servers[1],
        dns_server_3     => $dns_servers[2],
        %devparam,
        %attr,
    );
}


__PACKAGE__->meta->make_immutable

__END__

=pod

=head1 NAME

RackMan::Template - Simple templating module for RackMan

=head1 SYNOPSIS

    use RackMan::Template;

    my $tmpl = RackMan::Template->new(filename => "dhcp.tmpl");
    $tmpl->param(dhcp_server => "192.168.0.13");
    print $tmpl->output;


=head1 DESCRIPTION

This module is a simple Moose-based templating class, based on
HTML::Template and HTML::Template::Filter::TT2. Please read the
documentation of these modules for more details on the syntax.


=head1 METHODS

=head2 new

(delegated to C<HTML::Template>)

Create and return a new object.


=head2 param

(delegated to C<HTML::Template>)

Pass parameters to the template.


=head2 populate_from

Add to the template the parameters documented in L<"TEMPLATE PARAMETERS">
from the C<Rackman::Device> and C<RackMan> objects given in argument.

    my $tmpl = RackMan::Template->new(filename => $tmpl_path);
    $tmpl->populate_from($rackdev, $rackman);


=head2 output

(delegated to C<HTML::Template>)

Generate and return the output from the template and the given parameters.


=head1 TEMPLATE PARAMETERS

When the method C<populate_from()> is called with valid C<RackMan::Device>
and C<Rackman> objects given in arguments, it populates the template object
with the following parameters:

=over

=item *

C<dns_server_1>, C<dns_server_2>, C<dns_server_3> - DNS servers

=item *

C<gateway> - IPv4 address of the default gateway

=item *

C<fqdn> - FQDN of the host

=item *

C<name> - common name of the host

=item *

C<if0_ip> - IPv4 address of the first regular network interface

=item *

C<if0_mac> - MAC address of the first regular network interface

=item *

C<if0_name> - name of the first regular network interface

=item *

C<netmask> - IPv4 network mask

=item *

C<network> - IPv4 network address

=item *

C<type> - RackObject type

=back

The corresponding RackObject attributes are also available, with their
names mogrified to be valid identifiers: units are removed, some 
punctuation characters (dot (C<.>), comma (C<,>)) are removed, the
alphabetical characters are lowercased and the rest of non word
characters are collapsed and converted to underscores (C<_>).

Here is a non authoritative list of known attributes: C<alias>,
C<alive_check>, C<contact_person>, C<cpu>, C<dram> C<flash_memory>,
C<fqdn>, C<has_jumbo_frames>, C<hw_type>, C<hw_warranty_expiration>,
C<hypervisor>, C<max_power>, C<max_current>, C<oem_sn_1>, C<oem_sn_2>,
C<sw_type>, C<sw_version>, C<sw_warranty_expiration>, C<use>, C<uuid>.


=head1 SEE ALSO

L<HTML::Template>, L<HTML::Template::Filter::TT2>


=head1 AUTHOR

Sebastien Aperghis-Tramoni

=cut