#
# This class is the internal subclass that deals with the G8BPQ switch connections
#
# Written by John Wiseman G8BPQ Jan 2006
#
# Based on AGWMsg.pm Copyright (c) 2001 - Dirk Koopman G1TLH
#

package BPQMsg;

use strict;
use Msg;
use BPQConnect;
use DXDebug;

use vars qw(@ISA @outqueue $send_offset $inmsg $rproc $noports
			%circuit $total_in $total_out);

@ISA = qw(Msg ExtMsg);
@outqueue = ();
$send_offset = 0;
$inmsg = '';
$rproc = undef;
$noports = 0;
%circuit = ();
$total_in = $total_out = 0;

my $GetFreeBuffs;
my $FindFreeStream;
my $SetAppl;
my $SessionState;
my $GetCallsign;
my $SendMsg;
my $GetMsg;
my $RXCount;
my $DeallocateStream;
my $SessionControl;

my @Stream;

my $Buffers;

sub init
{
	return unless $enable;

	eval {
		require Win32::API;
	};
	if ($@) {
		$enable = 0;
		dbg("BPQWin disabled because Win32::API cannot be loaded");
		return;
	} else {
		Win32::API->import;
	}

	$rproc = shift;

	dbg("BPQ initialising...");

	$GetFreeBuffs = Win32::API->new("bpq32", "int _GetFreeBuffs\@0()");
    $FindFreeStream = Win32::API->new("bpq32", "int _FindFreeStream\@0()");
    $SetAppl = Win32::API->new("bpq32", "int _SetAppl\@12(int a, int b, int c)");
    $SessionState = Win32::API->new("bpq32", "DWORD _SessionState\@12(DWORD stream, LPDWORD state, LPDWORD change)");
	$GetCallsign = new Win32::API("bpq32", "_GetCallsign\@8",'NP','N');
	$SendMsg = new Win32::API("bpq32","_SendMsg\@12",'NPN','N');
	$RXCount = new Win32::API("bpq32","_RXCount\@4",'N','N');
	$GetMsg = Win32::API->new("bpq32","_GetMsgPerl\@8",'NP','N');

	$DeallocateStream = Win32::API->new("bpq32","_DeallocateStream\@4",'N','N');
    $SessionControl = Win32::API->new("bpq32", "int _SessionControl\@12(int a, int b, int c)");

	if (!defined $GetMsg) {
		$GetMsg = Win32::API->new("bpqperl","_GetMsgPerl\@8",'NP','N');
	}

	if (!defined $GetMsg) {
		dbg ("Can't find routine 'GetMsgPerl' - is bpqperl.dll available?");
	}

	$Buffers = 0;

	if (defined $GetFreeBuffs && defined $GetMsg) {
		my $s;

		$Buffers = $GetFreeBuffs->Call();

		dbg("G8BPQ Free Buffers = $Buffers") if isdbg('bpq');

		$s = "BPQ Streams:";

		for (my $i = 1; $i <= $BPQStreams; $i++) {

			$Stream[$i] = $FindFreeStream->Call();

			$s .= " $Stream[$i]";

			$SetAppl->Call($Stream[$i], 0, $ApplMask);

		}

		dbg($s) if isdbg('bpq');
	} else {

		dbg("Couldn't initialise BPQ32 switch, BPQ disabled");
		$enable = 0;
	}
}

sub finish
{
	return unless $enable;

	dbg("BPQ Closing..") if isdbg('bpq');

	return unless $Buffers;

	for (my $i = 1; $i <= $BPQStreams; $i++) {
		$SetAppl->Call($Stream[$i], 0, 0);
		$SessionControl->Call($Stream[$i], 2, 0); # Disconnect
		$DeallocateStream->Call($Stream[$i]);
	}
}

sub login
{
	goto &main::login;			# save some writing, this was the default
}

sub active
{
	dbg("BPQ is active called") if isdbg('bpq');
	return $Buffers;
}

sub peerhost
{
	my $conn = shift;
	$conn->{peerhost} ||= 'ax25';
	return $conn->{peerhost};
}


sub connect
{

	return unless $Buffers;

	my ($conn, $line) = @_;
	my ($port, $call) = split /\s+/, $line;


	dbg("BPQ Outgoing Connect  $conn $port $call") if isdbg('bpq');


	for (my $i = $BPQStreams; $i > 0; $i--) {
		my $inuse = $circuit{$Stream[$i]};

		if (not $inuse) {		# Active connection?

			dbg("BPQ Outgoing Connect using stream $i") if isdbg('bpq');

			$conn->{bpqstream} = $Stream[$i];
			$conn->{lineend} = "\cM";
			$conn->{incoming} = 0;
			$conn->{csort} = 'ax25';
			$conn->{bpqcall} = uc $call;
			$circuit{$Stream[$i]} = $conn;

			$SessionControl->Call($Stream[$i], 1, 0); # Connect

			$conn->{state} = 'WC';

			return 1;

		}

	}

	# No free streams
	dbg("BPQ Outgoing Connect - No streams available") if isdbg('bpq');

	$conn->{bpqstream} = 0;		# So we can tidy up
	$circuit{0} = $conn;
	return 0;
}

sub in_disconnect
{
	my $conn = shift;
	dbg( "in_disconnect $conn $circuit{$conn->{bpqstream}}") if isdbg('bpq');
	delete $circuit{$conn->{bpqstream}};
	$conn->SUPER::disconnect;
}

sub disconnect
{

	return unless $enable && $Buffers;

	my $conn = shift;

	delete $circuit{$conn->{bpqstream}};

	$conn->SUPER::disconnect;

	if ($conn->{bpqstream}) {	# not if stream = 0!
		$SessionControl->Call($conn->{bpqstream}, 2, 0); # Disconnect
	}
}

sub enqueue
{

	return unless $Buffers;

	my ($conn, $msg) = @_;

	if ($msg =~ /^D/) {
		$msg =~ s/^[-\w]+\|//;
		#		_sendf('Y', $main::mycall, $conn->{call}, $conn->{bpqstream}, $conn->{agwpid});
		#		_sendf('D', $main::mycall, $conn->{bpqcall}, $conn->{bpqstream}, $conn->{agwpid}, $msg . $conn->{lineend});

		$msg = $msg . $conn->{lineend};

		my $len = length($msg);
		$SendMsg->Call($conn->{bpqstream}, $msg, $len);
		dbg("BPQ Data Out port: $conn->{bpqstream}   length: $len \"$msg\"") if isdbg('bpq');
	}
}

sub process
{
	return unless $enable && $Buffers;

	my $state=0;
	my $change=0;

	for (my $i = 1; $i <= $BPQStreams; $i++) {
		$SessionState->Call($Stream[$i], $state, $change);

		if ($change) {
			dbg("Stream $Stream[$i] newstate $state") if isdbg('bpq');

			if ($state == 0) {
				# Disconnected

				my $conn = $circuit{$Stream[$i]};

				if ($conn) {		# Active connection?
					&{$conn->{eproc}}() if $conn->{eproc};
					$conn->in_disconnect;
				}

			}

			if ($state) {

				# Incoming call

				my $call="            ";

				$GetCallsign->Call($Stream[$i],$call);

				for ($call) {	# trim whitespace in $variable, cheap
			        s/^\s+//;
					s/\s+$//;
				}

				dbg("BPQ Connect Stream $Stream[$i] $call") if isdbg('bpq');

				my $conn =  $circuit{$Stream[$i]};;

				if ($conn) {

					# Connection already exists - if we are connecting out this is OK

					if ($conn->{state} eq 'WC') {
						$SendMsg->Call($Stream[$i], "?\r", 2); # Trigger response for chat script
					}

					# Just ignore incomming connect if we think it is already connected

				} else {

					# New Incoming Connect

					$conn = BPQMsg->new($rproc);
					$conn->{bpqstream} = $Stream[$i];
					$conn->{lineend} = "\cM";
					$conn->{incoming} = 1;
					$conn->{bpqcall} = $call;
					$circuit{$Stream[$i]} = $conn;
					if (my ($c, $s) = $call =~ /^(\w+)-(\d\d?)$/) {
						$s = 15 - $s if $s > 8;
						$call = $s > 0 ? "${c}-${s}" : $c;
					}
					$conn->to_connected($call, 'A', $conn->{csort} = 'ax25');
				}

			}

		}

		# See if data received

		my $cnt = $RXCount->Call($Stream[$i]);

		while ($cnt > 0) {
			$cnt--;

			my $Buffer = " " x 340;

			my $len=0;

			$len=$GetMsg->Call($Stream[$i],$Buffer);

			$Buffer = substr($Buffer,0,$len);

			dbg ("BPQ RX: $Buffer") if isdbg('bpq');

			my $conn = $circuit{$Stream[$i]};

			if ($conn) {

				dbg("BPQ State = $conn->{state}") if isdbg('bpq');

				if ($conn->{state} eq 'WC') {
					if (exists $conn->{cmd}) {
						if (@{$conn->{cmd}}) {
							dbg($Buffer) if isdbg('connect');
							$conn->_docmd($Buffer);
						}
					}
					if ($conn->{state} eq 'WC' && exists $conn->{cmd} && @{$conn->{cmd}} == 0) {
						$conn->to_connected($conn->{call}, 'O', $conn->{csort});
					}
				} else {
					my @lines = split /\cM\cJ?/, $Buffer;
					push @lines, $Buffer unless @lines;
					for (@lines) {
						&{$conn->{rproc}}($conn, "I$conn->{call}|$_");
					}
				}
			} else {
				dbg("BPQ error Unsolicited Data!");
			}
		}
	}
}

1;

