package streambuf;
#
#       history
#
#       09-JAN-2000 IK  new bond shapes >> and >. and b
#       19-JAN-2000 IK  new bond shape ~, bond length now determined by number of type markers,
#                       LaTeX packages can be given to be preloaded
#

#
# creates new buffer
#
sub new
{
   $r_buffer = { "Ptr" => 0,
                 "Anz" => 0,
                 "Buffer" => ""
               };
   bless $r_buffer, 'streambuf';
   return $r_buffer;
}


#
#  $buf->LoadBuffer($file)
#
sub LoadBuffer
{
   my ($r_buff, $file, $texthash) = @_;
   my ($text, $l, $c, $bOutText);

   open(FILE, $file);
   while ($line = <FILE>)
   {
       chop($line);
       $l = "";
       $bOutText = $::true;
       for ($i=$j=0; $i<length($line); $i++)
       {
           $c = substr($line, $i, 1);
           if ($c eq '"')
           {
               if (substr($line, $i+1, 1) eq '"')
               {
                   $c = '"';
                   $i++;
               }
               else
               {
                   $bOutText = !$bOutText;
                   $c = ($bOutText ? "</text>" : "<text>");
               }
           }
           last if ($c =~ /%/ && $bOutText);
           next if ($c =~ /\s/ && $bOutText);
           substr($l, $j) = $c;
           $j += length($c);
       }
       $line = $l;
##+     $line =~ s/\s//g;               # delete white spaces
##+     $line =~ /^([^%]*)%*.*$/;       # cut off comment
##+     $line = $1;
       # write out all TeX text strings
       @textext = ($line =~ m{<text>(.*?)</text>}g);
       if (@textext)
       {
           foreach $text (@textext)
           {
               $texthash->{$text} = "";
           }
       }
       substr($r_buff->{"Buffer"}, $r_buff->{"Anz"}, 0) = $line;
       $r_buff->{"Anz"} += length($line);
   }
   close(FILE);
}


#
#  $buf->GetTextSize(...)
#
sub GetTextSize
{
   my ($r_buff, $tmptex, $texthash, $be_type, $latexcmd, $cFont, $packages) = @_;
   my $text;
   my ($opt, $package, $packagename);

   # get dimensions of text fragments
   if ($be_type == $be::BE_PSLATEX || $be_type == $be::BE_LATEX)
   {
       open(OUT, ">".$tmptex);
       print OUT "\\documentclass[a4paper,12pt]{article}\n";
       print OUT "\\usepackage{german}\n";
       # write each desired LaTeX package in preamble
       while ($package = pop(@$packages))
       {
           ($opt, $packagename) = $package =~ /(\[\S*\])*(\S*)/;
           print OUT "\\usepackage$opt\{$packagename\}\n";
       }
       print OUT "\\newsavebox{\\testbox}\n";
       print OUT "\\newcommand{\\atom}[1]\n";
       print OUT "{\\sbox{\\testbox}{#1}\n";
       print OUT " \\typeout{A:\\the\\wd\\testbox,\\the\\ht\\testbox,\\the\\dp\\testbox}\n";
       print OUT " }\n";
       print OUT "\\newcommand{\\testfont}{$cFont}\n" if $cFont;
       print OUT "\n";
       print OUT "\\begin{document}\n";
       print OUT "\\testfont\n" if $cFont;
       foreach $text (keys %$texthash)
       {
           print OUT "\\atom{$text}\n";
       }
       print OUT "\\end{document}\n";
       close(OUT);

       # build array of TeX text dimensions: LaTeX it!
       my @dimen = grep(/^A:/, `$latexcmd $tmptex`);
       my $i = 0;
       foreach $text (keys %$texthash)
       {
           $texthash->{$text} = $dimen[$i++];
       }
   }
   elsif ($be_type == $be::BE_PS)
   {
       foreach $text (keys %$texthash)
       {
           print "warning: cannot determine text size for $text\n";
       }
   }
}


#
#
#
sub GetPtr
{
   my $r_buff = shift;
   return $r_buff->{"Ptr"};
}

sub SetPtr
{
   my ($r_buff, $ptr) = @_;
   $r_buff->{"Ptr"} = $ptr;
}


#
#  check if next token in buffer is $token, and adjusts buffer pointer
#  Returns zero if there's not this token.
#  BOOL $buf->NextToken($token)
#
sub NextToken
{
   my ($r_buff, $text) = @_;
   my $Ptr = $r_buff->{"Ptr"};
   my $Anz = $r_buff->{"Anz"};
   my $len = length($text);

   return 0 if $len+$Ptr > $Anz;
   if (substr($r_buff->{"Buffer"}, $Ptr, $len) eq $text)
   {
       $r_buff->{"Ptr"} += $len;
       return 1;
   }
   return 0;
}


#
#  Tries to get an signed integer value.
#  BOOL $buf->GetInt($iVar)
#
sub GetInt
{
   my $r_buff = shift;

   if ( $iAnz = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}) =~
                       /^([+-]?\d+)/)
   {
       $_[0] = $1;
       $r_buff->{"Ptr"} += length($1);
   }
   return ($iAnz);
}


#
#  Tries to get an signed fix float value.
#  BOOL $buf->GetReal($rVar)
#
sub GetReal
{
   my $r_buff = shift;

   if ( $iAnz = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}) =~
                       /^([+-]?(\d+(\.\d*)*)|(\d*\.\d*))/)
   {
       $_[0] = $1;
       $r_buff->{"Ptr"} += length($1);
   }
   return ($iAnz);
}


#
#  Tries to get a text enclosed in <text>...</text>
#  BOOL $buf->GetText($cVar)
#
sub GetText
{
   my $r_buff = shift;

   if ( $iAnz = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}) =~
                       m{^<text>(.*?)</text>})
   {
       $_[0] = $1;
       $r_buff->{"Ptr"} += length($1) + 13;
   }
   return ($iAnz);
}


#
#  decodes a type marker like X or Y
#  BOOL $buf->GetType($iType)
#
($T_X, $T_Y) = (0, 1);
sub GetType
{
   my $r_buff = shift;
   my $code = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}, 1);
   my $l;
   $_[0] = -1;
   SWITCH: {
       $l = 1;
       $_[0] = $T_X, last SWITCH if $code eq "X";
       $_[0] = $T_Y, last SWITCH if $code eq "Y";
       $l = 0;
   }
   $r_buff->{"Ptr"} += $l;
   return ($_[0] != -1);
}

#
#  decodes a position marker like T, TR, T, BL, C, ...
#  BOOL $buf->GetPos($iPos)
#
sub GetPos
{
   my $r_buff = shift;
   my $code2 = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}, 2);
   my $code1 = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}, 1);
   my $l;
   $_[0] = -1;
   SWITCH: {
       $l = 2;
       $_[0] = $bbox::SB_BL, last SWITCH if $code2 eq "BL";
       $_[0] = $bbox::SB_BR, last SWITCH if $code2 eq "BR";
       $_[0] = $bbox::SB_TL, last SWITCH if $code2 eq "TL";
       $_[0] = $bbox::SB_TR, last SWITCH if $code2 eq "TR";
       $l = 1;
       $_[0] = $bbox::SB_L, last SWITCH if $code1 eq "L";
       $_[0] = $bbox::SB_R, last SWITCH if $code1 eq "R";
       $_[0] = $bbox::SB_T, last SWITCH if $code1 eq "T";
       $_[0] = $bbox::SB_B, last SWITCH if $code1 eq "B";
       $_[0] = $bbox::SB_C, last SWITCH if $code1 eq "C";
       $l = 0;
   }
   $r_buff->{"Ptr"} += $l;
   return ($_[0] != -1);
}


#
#  decodes a bond length marker like S, N, L and returns length
#  BOOL $buf->GetBondLen($rLen)
#
sub GetBondLen
{
   my $r_buff = shift;
   my $l;
   my $bFound = 0;
   $_[0] = 0;
   while(1)
   {
       my $code = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}, 1);
       $_[0] += $main::rLenN, next if $code eq "N";
       $_[0] += $main::rLenL, next if $code eq "L";
       $_[0] += $main::rLenS, next if $code eq "S";
       $_[0] += $main::rLenN/2, next if $code eq "n";
       $_[0] += $main::rLenL/2, next if $code eq "l";
       $_[0] += $main::rLenS/2, next if $code eq "s";
       next if $code eq "0";
       last;
   } continue { $r_buff->{"Ptr"} += 1; $bFound = 1; }
   return ($bFound);
}
sub GetBondLenOrig
{
   my $r_buff = shift;
   my $code2 = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}, 2);
   my $code1 = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}, 1);
   my $l;
   $_[0] = -1;
   SWITCH: {
       $l = 2;
       $_[0] = 2*$main::rLenN, last SWITCH if $code2 eq "NN";
       $_[0] = 2*$main::rLenS, last SWITCH if $code2 eq "SS";
       $_[0] = 2*$main::rLenL, last SWITCH if $code2 eq "LL";
       $l = 1;
       $_[0] = $main::rLenN, last SWITCH if $code1 eq "N";
       $_[0] = $main::rLenL, last SWITCH if $code1 eq "L";
       $_[0] = $main::rLenS, last SWITCH if $code1 eq "S";
       $_[0] = $main::rLenN/2, last SWITCH if $code1 eq "n";
       $_[0] = $main::rLenL/2, last SWITCH if $code1 eq "l";
       $_[0] = $main::rLenS/2, last SWITCH if $code1 eq "s";
       $_[0] = 0, last SWITCH if $code1 eq "0";
       $l = 0;
   }
   $r_buff->{"Ptr"} += $l;
   return ($_[0] != -1);
}


#
#  decodes a bond type marker like -, =, L and returns type code
#  BOOL $buf->GetBondType($iType)
#
sub GetBondType
{
   my $r_buff = shift;
   my %codes = ( "->" => 10,  "-" => 0,    "<-" => 11,  "s" => 12,
                 "<<" => 2,   "<." => 3,   "o" => 4,
                 "=C" => 5,   "=U" => 6,   "=" => 7,    "3" => 8,
                 "p" =>1,     "t" => 9,    ">>" => 13,  ">." => 14,
                 "b" => 15,   "~" => 16
               );
   $_[0] = -1;

   foreach $elem (keys %codes)
   {
       if (substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}, length($elem)) eq
               $elem)
       {
           $_[0] = $codes{$elem};
           $r_buff->{"Ptr"} += length($elem);
           last;
       }
   }
   return ($_[0] != -1);
}


#
#  decodes a arrow type marker like -, =, ... and returns type code
#  BOOL $buf->GetArrowType($iType)
#
sub GetArrowType
{
   my $r_buff = shift;
   my $code3 = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}, 3);
   my $code2 = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}, 2);
   my $code1 = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}, 1);
   my $l;
   $_[0] = -1;
   SWITCH: {
       $l = 3;
       $_[0] = 3, last SWITCH if $code3 eq "<=>";
       $_[0] = 4, last SWITCH if $code3 eq "<->";
       $_[0] = 5, last SWITCH if $code3 eq "-|>";
       $_[0] = 6, last SWITCH if $code3 eq "<|-";
       $l = 2;
       $_[0] = 1, last SWITCH if $code2 eq "->";
       $_[0] = 2, last SWITCH if $code2 eq "<-";
       $l = 1;
       $_[0] = 0, last SWITCH if $code1 eq "-";
       $l = 0;
   }
   $r_buff->{"Ptr"} += $l;
   return ($_[0] != -1);
}

#
#  decodes a marker type marker like -, =, ... and returns type code
#  BOOL $buf->GetMarkerType($iType)
#
sub GetMarkerType
{
   my $r_buff = shift;
   my $code2 = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}, 2);
   my $code1 = substr($r_buff->{"Buffer"}, $r_buff->{"Ptr"}, 1);
   my $l;
   $_[0] = -1;
   SWITCH: {
       $l = 2;
       $_[0] = 3, last SWITCH if $code2 eq "4f";
       $_[0] = 1, last SWITCH if $code2 eq "of";
       $_[0] = 5, last SWITCH if $code2 eq "rf";
       $_[0] = 7, last SWITCH if $code2 eq "3f";
       $l = 1;
       $_[0] = 0, last SWITCH if $code1 eq "o";
       $_[0] = 2, last SWITCH if $code1 eq "4";
       $_[0] = 4, last SWITCH if $code1 eq "r";
       $_[0] = 6, last SWITCH if $code1 eq "3";
       $_[0] = 8, last SWITCH if $code1 eq "+";
       $_[0] = 9, last SWITCH if $code1 eq "x";
       $_[0] = 10, last SWITCH if $code1 eq "*";
       $_[0] = 11, last SWITCH if $code1 eq "n";
       $l = 0;
   }
   $r_buff->{"Ptr"} += $l;
   return ($_[0] != -1);
}


1;