package MaqIndex;

use strict;
use warnings;
use IO::Socket::INET;
use FileHandle;

sub new {
  my $invocant = shift;
  my $class = ref($invocant) || $invocant;
  my $self = { -host=>'127.0.0.1', -port=>'5321', @_ };
  bless($self, $class);
  $self->init;
  return $self;
}

sub init {
  my $self = shift;
  my $server = qq/$self->{-host}:$self->{-port}/;
  $self->{_fh} = IO::Socket::INET->new(PeerAddr=>$server) || die("[MaqIndex::init] fail to connect server: '$server'");
  my $fh = $self->{_fh};
  binmode($fh);
  my $buf = '';
  syswrite($fh, pack("C", 1));
  read($fh, $buf, 4); # number of reference sequences
  ($self->{_n_ref}) = unpack("III", $buf);
  my $n_ref = $self->{_n_ref};
  my $refs = \%{$self->{_refs}};
  my @refa;
  for (0 .. $n_ref-1) {
	read($fh, $buf, 12);
	my ($len, $high, $nl) = unpack("III", $buf); # by default, Perl cannot really take 64-bit integers?
	read($fh, $buf, $nl); # ref name
	($buf) = unpack("a$nl", $buf);
	@{$refs->{$buf}} = ($_, $len);
	push(@refa, $buf);
  }
  $self->{_lc} = 0;
  $self->{_refa} = \@refa;
}

sub DESTROY {
  my $self = shift;
  if (defined $self->{_fh}) {
	syswrite($self->{_fh}, pack("C", 0));
	close($self->{_fh});
	delete $self->{_fh};
  }
}

sub get {
  my $self = shift;
  my $reg = shift;
  my @array;
  my $a = @_? shift : \@array;
  @$a = ();
  $reg =~ s/,//g;
  $reg =~ s/\s//g;
  unless ($reg =~ /^(\S+):(\d+)-(\d+)$/) {
	warn("[MaqIndex::get] Illegal region format: '$reg'. Skip!\n");
	return;
  }
  my ($chr, $b, $e) = ($1, $2, $3);
  my $ref_id = $self->{_refs}{$chr}[0];
  unless (defined $ref_id) {
	warn("[MaqIndex::get] Fail to find reference '$chr'. Skip!\n");
	return;
  }
  my $fh = $self->{_fh};
  my ($buf, $N, $dumb1, $dumb2);
  syswrite($fh, pack("CI6", 3, $self->{_lc}, $ref_id, $b-1, 0, $e-1, 0), 25);
  read($fh, $buf, 28);
  ($self->{_lc}, $ref_id, $b, $dumb1, $e, $dumb2, $N) = unpack("I7", $buf);
  for (1 .. $N) {
	read($fh, $buf, 120);
	push(@$a, $buf);
	read($fh, $buf, 8); # useless bits
  }
  return @array if (@array);
}

sub struct2array {
  my $self = shift;
  my $buf = shift;
  my $is_verbose = @_? $_ : 1;
  my $ref = $self->{_refa};
  my @ACGT = ('A', 'C', 'G', 'T');
  for my $i (0 .. @$buf-1) {
	my ($sq, @t);
	($sq, @t[7,13,6,9..12,5,8,1,2,4,0]) = unpack("a63cC8I2iZ36", $buf->[$i]);
	@t[1..3] = ($ref->[$t[1]], ($t[2]>>1)+1, ($t[2]&1)?'-':'+', $t[9]&0xf);
	if ($is_verbose) {
	  my @s = unpack("C$t[13]", $sq);
	  my ($seq, $qual);
	  foreach my $p (@s) {
		if ($p == 0) { $seq .= 'N'; }
		else { $seq .= $ACGT[$p>>6]; }
		$p = chr(33 + ($p&0x3f));
	  }
	  push(@t, $seq, join('', @s));
	}
	$buf->[$i] = \@t;
  }
}

sub mapview {
  my ($self, $bufs) = @_;
  $self->struct2array($bufs);
  for my $buf (@$bufs) {
	print join("\t", @$buf), "\n";
  }
}

1;
