#! /usr/bin/perl -w
#
# Written by Oron Peled <oron@actcom.co.il>
# Copyright (C) 2007, Xorcom
# This program is free software; you can redistribute and/or
# modify it under the same terms as Perl itself.
#
# $Id: zapconf 2536 2007-05-17 21:47:40Z tzafrir $
#
use strict;
BEGIN { my $dir = $0; $dir =~ s:/[^/]+$::; unshift(@INC, "$dir", "$dir/zconf"); }

use Zaptel;
use Zaptel::Xpp;

my %default_context = (
	FXO	=> 'from-pstn',
	FXS	=> 'from-internal',
	IN	=> 'astbank-input',
	OUT	=> 'astbank-output'
	);

my %default_group = (
	FXO	=> '0',
	FXS	=> '5',
	IN	=> '5',
	OUT	=> '5'
	);

my @zaptel_default_vars = qw(
		base_exten
		lc_country
		context_lines
		context_phones
		context_input
		context_output
		group_lines
		group_phones
	);

my $fxs_immediate = 'no';
my $fxs_default_start;
my $lc_country = 'us';
my $loadzone;
my $defaultzone;

sub map_zaptel_defaults {
	my %defaults = @_;

	$default_group{FXO} = $defaults{group_lines}
			if defined $defaults{group_lines};
	$default_group{FXS} = $default_group{IN} = $default_group{OUT} = $defaults{group_phones}
			if defined $defaults{group_phones};
	$default_context{FXO} ||= $defaults{context_lines};
	$default_context{FXS} ||= $defaults{context_phones};
	$default_context{IN} ||= $defaults{context_input};
	$default_context{OUT} ||= $defaults{context_output};
	$fxs_immediate ||= $defaults{fxs_immediate};
	$fxs_default_start ||= $defaults{fxs_default_start} || 'ls';
	$lc_country ||= $defaults{lc_country};
	$loadzone = $defaultzone = $lc_country;
}


my $zapconf_file;
my $zapata_file;

my @spans = Zaptel::spans();

sub bchan_range($) {
	my $span = shift || die;
	my $first_chan = ($span->chans())[0];
	my $first_num = $first_chan->num();
	my $range;

	if($span->is_bri()) {
		$range = sprintf "%d-%d", $first_num, $first_num + 1;
	}
	return $range;
}

sub gen_zaptel_signalling($) {
	my $chan = shift || die;
	my $type = $chan->type;
	my $num = $chan->num;

	if($type eq 'FXO') {
		printf "fxsks=%d\n", $num;
	} elsif($type eq 'FXS') {
		printf "fxo%s=%d\n", $fxs_default_start, $num;
	} elsif ($type eq 'IN') {
		printf "# astbanktype: input\n";
		printf "fxo%s=%d\n", $fxs_default_start, $num;
	} elsif ($type eq 'OUT') {
		printf "# astbanktype: output\n";
		printf "fxo%s=%d\n", $fxs_default_start, $num;
	} elsif ($type eq 'BRI_TE') {
		printf "# termtype: te\n";
	} elsif ($type eq 'BRI_NT') {
		printf "# termtype: nt\n";
	} elsif($type eq 'EMPTY') {
		printf "# channel %d, %s, no module.\n", $num, $chan->fqn;
		return;
	}
}

my $bri_te_last_timing = 1;

sub gen_zaptel_digital($) {
	my $span = shift || die;
	my $num = $span->num();
	my $termtype = $span->termtype() || die "$0: Span #$num -- unkown termtype [NT/TE]\n";
	my $timing;
	my $lbo = 1;
	my $framing = $span->framing() || die "$0: No framing information for span #$num\n";
	my $coding =  $span->coding() || die "$0: No coding information for span #$num\n";
	my $span_yellow = $span->yellow();
	$span_yellow = (defined $span_yellow) ? ",$span_yellow" : '';

	$timing = ($termtype eq 'NT') ? 0 : $bri_te_last_timing++;
	printf "span=%d,%d,%d,%s,%s\n",
			$num,
			$timing,
			$lbo,
			$framing,
			"$coding$span_yellow";
	printf "# termtype: %s\n", lc($termtype);
	printf "bchan=%s\n", bchan_range($span);
	my $dchan = $span->dchan();
	printf "dchan=%d\n", $dchan->num();
}

sub gen_zaptelconf($) {
	my $file = shift || die;
	rename "$file", "$file.bak"
		or $!{ENOENT}
		or die "Failed to backup old config: $!\n";
	open(F, ">$file") || die "$0: Failed to open $file: $!\n";
	my $old = select F;
	printf "# Autogenerated by %s on %s -- do not hand edit\n", $0, scalar(localtime);
	print <<"HEAD";
# Zaptel Configuration File
#
# This file is parsed by the Zaptel Configurator, ztcfg
#
HEAD
	foreach my $span (@spans) {
		printf "# Span %d: %s %s\n", $span->num, $span->name, $span->description;
		if($span->is_bri()) {
			gen_zaptel_digital($span);
		} else {
			foreach my $chan ($span->chans()) {
				if(1 || !defined $chan->type) {
					my $type = $chan->probe_type;
					my $num = $chan->num;
					die "Failed probing type for channel $num"
						unless defined $type;
					$chan->type($type);
				}
				gen_zaptel_signalling($chan);
			}
		}
		print "\n";
	}
	print <<"TAIL";
# Global data

loadzone	= $loadzone
defaultzone	= $defaultzone
TAIL
	close F;
	select $old;
}

sub gen_zapata_channel($) {
	my $chan = shift || die;
	my $type = $chan->type;
	my $num = $chan->num;
	my $sig;
	my $immediate;
	my $callerid = sprintf "\"Channel %d\" <4%03d>", $num, $num;
	my $context = $default_context{$type};
	my $group = $default_group{$type};

	if ($type eq 'FXO') {
		$sig = "fxs_ks";
		$callerid = 'asreceived';
	} elsif($type eq 'FXS') {
		$sig = "fxo_" . $fxs_default_start;
		$immediate = 'yes' if $fxs_immediate eq 'yes';
	} elsif($type eq 'IN') {
		$sig = "fxo_" . $fxs_default_start;
		$immediate = 'yes';
	} elsif($type eq 'OUT') {
		$sig = "fxo_" . $fxs_default_start;
	} elsif($type eq 'EMPTY') {
		return;
	}
	die "Unknown signalling for channel $num type $type\n"
		unless defined $sig;
	printf ";;; line=\"%d %s\"\n", $num, $chan->fqn;
	printf "signalling=$sig\n";
	printf "callerid=$callerid\n";
	printf "mailbox=4%03d\n", $num unless $type eq 'FXO';
	if(defined $group) {
		printf "group=%d\n", $group;
	}
	printf "context=$context\n";
	printf "immediate=$immediate\n" if defined $immediate;
	printf "channel => %d\n", $num;
	# Reset following values to default
	printf "callerid=\n";
	printf "mailbox=\n" unless $type eq 'FXO';
	if(defined $group) {
		printf "group=\n";
	}
	printf "context=default\n";
	printf "immediate=no\n" if defined $immediate;
	print "\n";
}

sub gen_zapataconf($) {
	my $file = shift || die;
	rename "$file", "$file.bak"
		or $!{ENOENT}
		or die "Failed to backup old config: $!\n";
	open(F, ">$file") || die "$0: Failed to open $file: $!\n";
	my $old = select F;
	printf "# Autogenerated by %s on %s -- do not hand edit\n", $0, scalar(localtime);
	print <<"HEAD";
; Zaptel Channels Configurations (zapata.conf)
;
; This is not intended to be a complete zapata.conf. Rather, it is intended
; to be #include-d by /etc/zapata.conf that will include the global settings
;

HEAD
	foreach my $span (@spans) {
		printf "; Span %d: %s %s\n", $span->num, $span->name, $span->description;
		foreach my $chan ($span->chans()) {
			gen_zapata_channel($chan);
		}
		print "\n";
	}
	close F;
	select $old;
}

# Use the shell to source a file and expand a given list
# of variables.
sub do_source($@) {
	my $file = shift;
	my @vars = @_;
	my @output = `env -i sh -e -c '. $file; export @vars; env'`;
	die "$0: Sourcing '$file' exited with $?" if $?;
	my %vars;
	
	foreach my $line (@output) {
		chomp $line;
		my ($k, $v) = split(/=/, $line, 2);
		$vars{$k} = $v if grep /^$k$/, @vars;
	}
	return %vars;
}

sub set_defaults {
	my $zaptel_boot_debian = $ENV{ZAPTEL_BOOT_DEBIAN} || "/etc/default/zaptel";
	my $zaptel_boot_fedora = $ENV{ZAPTEL_BOOT_FEDORA} || "/etc/sysconfig/zaptel";

	# Source default files
	my %source_defaults;
	foreach my $defaults ($zaptel_boot_debian, $zaptel_boot_fedora) {
		%source_defaults = do_source($defaults, @zaptel_default_vars) if -r $defaults;
	}
	map_zaptel_defaults(%source_defaults);
	$zapconf_file = $ENV{ZAPCONF_FILE} || "/etc/zaptel.conf";
	$zapata_file = $ENV{ZAPATA_FILE} || "/etc/asterisk/zapata-channels.conf";
}

set_defaults;
gen_zaptelconf $zapconf_file;
gen_zapataconf $zapata_file;

__END__

=head1 NAME

zapconf - Generate configuration for zaptel channels.

=head1 SYNOPSIS

zapconf

=head1 DESCRIPTION

