# -*- mode: Perl -*-
# /=====================================================================\ #
# |  graphics                                                           | #
# | Implementation for LaTeXML                                          | #
# |=====================================================================| #
# | Part of LaTeXML:                                                    | #
# |  Public domain software, produced as part of work done by the       | #
# |  United States Government & not subject to copyright in the US.     | #
# |---------------------------------------------------------------------| #
# | Bruce Miller <bruce.miller@nist.gov>                        #_#     | #
# | http://dlmf.nist.gov/LaTeXML/                              (o o)    | #
# \=========================================================ooo==U==ooo=/ #
#**********************************************************************
package LaTeXML::Package::Pool;
use strict;
use warnings;
use LaTeXML::Package;
use LaTeXML::Util::Pathname;
use LaTeXML::Util::Image;

#======================================================================
# (See  LaTeXML::Post::Graphics for suggested postprocessing)
# Package options: draft, final, hiderotate, hidescale, hiresbb

DefParameterType('GraphixDimension', sub {
    my ($gullet) = @_;
    if ($gullet->ifNext(T_OTHER('!'))) {
      $gullet->readToken();
      undef; }    # essentially: let other dimensions determine size.
    else {
      $gullet->readDimension; } },
  optional => 1);

DefConstructor('\scalebox{}[] Digested',
  "<ltx:inline-block angle='#angle' width='#width' height='#height' depth='#depth'"
    . " innerwidth='#innerwidth' innerheight='#innerheight' innerdepth='#innerdepth'"
    . " xscale='#xscale' yscale='#yscale'"
    . " xtranslate='#xtranslate' ytranslate='#ytranslate'>"
    . "#3"
    . "</ltx:inline-block>",
  properties => sub {
    my ($stomach, $xscale, $yscale, $box) = @_;
    $xscale = ToString($xscale);
    $yscale = ($yscale ? ToString($yscale) : $xscale);
    my ($w, $h, $d) = $box->getSize;
    return () unless $w;
    (width => $w->multiply($xscale),
      height     => $h->multiply($yscale),
      depth      => $d->multiply($yscale),
      xscale     => $xscale,
      yscale     => $yscale,
      xtranslate => $w->multiply(($xscale - 1) / 2),
      ytranslate => $h->add($d)->multiply(($yscale - 1) / 2)); },
  mode => 'text');

DefConstructor('\resizebox OptionalMatch:* {GraphixDimension}{GraphixDimension} Digested', sub {
    my ($document, $star, $width, $height, $box, %props) = @_;
    insertBlock($document, $box,
      width  => $props{width},
      height => $props{height},
      depth  => $props{depth}); },    # ?
  properties => sub {
    my ($stomach, $star, $width, $height, $box) = @_;
    (($width ? (width => $width) : ()),
      ($height ? (height => $height) : ()),
      depth => Dimension(0)); },      # ?
  mode => 'text');

# TeX wants to rotate CCW, by default, about the left baseline,
# but some macros allow you to specify the point or corner about which to rotate.
# It then allocates the width of the resulting box.
# CSS3's rotation wants to rotate CW about the CENTER of the box!
# The resulting box should have it's origin at the leftmost corner
# But we're shifting the box from the position CSS thought that it WAS (before rotation!)
# However, I THINK that it is using the GIVEN width & height
# to determine the center!
# Presumably CSS has lost the sense of the box's baseline?
# Note that our transformable-attributes apply: translate, scale, rotate, in that order.

# NOTE: There's a bizarre interaction between these CSS transformations
# and position:absolute that I haven't sorted out!
sub rotatedProperties {
  my ($box, $angle, %options) = @_;
  $angle = $angle->valueOf if ref $angle;
  my $cwangle = -$angle;
  my ($width, $height, $depth) = $box->getSize;
  return () unless $width;
  my ($w, $h, $d) = map { $_->valueOf } $width, $height, $depth;
  my $x0 = 0;
  my $y0 = 0;
  if ($options{x}) { $x0 = $options{x}; $x0 = $x0->valueOf if ref $x0; }
  if ($options{y}) { $y0 = $options{y}; $y0 = $y0->valueOf if ref $y0; }

  if (my $origin = ToString($options{origin})) {
    if    ($origin =~ /l/) { $x0 = 0; }
    elsif ($origin =~ /r/) { $x0 = $w; }
    elsif ($origin =~ /c/) { $x0 = $w / 2; $y0 = ($h - $d) / 2; }
    if    ($origin =~ /t/) { $y0 = $h; }
    elsif ($origin =~ /b/) { $y0 = -$d; }
    elsif ($origin =~ /B/) { $y0 = 0; } }
##  print STDERR "ROTATE ".$width->ptValue." x ".$height->ptValue." + ".$depth->ptValue
##    ." by $angle about ".Dimension($x0)->ptValue.", ".Dimension($y0)->ptValue."\n";

  my $H        = $h + $d;
  my $rad      = $angle * 3.1415926 / 180;      # close enough
  my $s        = sin($rad);
  my $c        = cos($rad);
  my $wp       = abs($w * $c) + abs($H * $s);
  my @cornerys = (
    (-$d - $y0) * $c + (+0 - $x0) * $s + $y0,     # bottom left
    (-$d - $y0) * $c + ($w - $x0) * $s + $y0,     # bottom right
    (+$h - $y0) * $c + ($w - $x0) * $s + $y0,     # top right
    (+$h - $y0) * $c + (+0 - $x0) * $s + $y0);    # top left
  my $hp  = max(@cornerys);
  my $dp  = -min(@cornerys);
  my $xsh = Dimension(($wp - $w) / 2)->ptValue . 'pt';
  #   my $ysh = Dimension(-($hp + $dp - $h - $d) / 2)->ptValue . 'pt';
  #  my $ysh = Dimension(($h + $d - $hp + $dp) / 2)->ptValue . 'pt';
  # Not sure about the geometry here!
  my $ysh = Dimension(($h - $hp + $dp) / 2 + $d)->ptValue . 'pt';
  if ($options{smash}) {
    #    $wp = $hp = $dp = 0; }
    $wp = 0; }
  # Since $dp & $ysh both try to have the same effect
  # (& I don't think the CSS box really has a depth anymore)
  $hp += $dp; $dp = 0;
  my $widthp  = Dimension($wp);
  my $heightp = Dimension($hp);
  my $depthp  = Dimension($dp);
##  print STDERR " => ".$widthp->ptValue." x ".$heightp->ptValue." + ".$depthp->ptValue." => ".$xsh.", ".$ysh."\n";

## print STDERR "\nROTATE $angle of ".ToString($box)."\n"
##  ." == ".ToString($width).' x '.ToString($height).' + '.ToString($depth)."\n"
##  ." => ".ToString($widthp).' x '.ToString($heightp).' + '.ToString($depthp)."\n";
  return (
    angle       => $angle,
    width       => $widthp,
    height      => $heightp,
    depth       => $depthp,
    innerwidth  => $width,
    innerheight => $height,
    innerdepth  => $depth,
    xtranslate  => $xsh,
    ytranslate  => $ysh,
  ); }

# NOTE: Need to implement the origin of rotation!
# [code so far only reads them]
DefKeyVal('Grot', 'origin', '');            # c,l,r,t,b,B
DefKeyVal('Grot', 'x',      'Dimension');
DefKeyVal('Grot', 'y',      'Dimension');
DefKeyVal('Grot', 'units',  '');
DefConstructor('\rotatebox OptionalKeyVals:Grot {Float} Digested',
  "<ltx:inline-block angle='#angle' width='#width' height='#height' depth='#depth'"
    . " innerwidth='#innerwidth' innerheight='#innerheight' innerdepth='#innerdepth'"
    . " xscale='#xscale' yscale='#yscale'"
    . " xtranslate='#xtranslate' ytranslate='#ytranslate'>"
    . "#3"
    . "</ltx:inline-block>",
  afterDigest => sub {
    my ($stomach, $whatsit) = @_;
    my ($kv, $angle, $box) = $whatsit->getArgs();
    $whatsit->setProperties(rotatedProperties($box, $angle, ($kv ? $kv->getHash : ()))); });

DefConstructor('\reflectbox Digested',
  "<ltx:inline-block angle='#angle' width='#width' height='#height' depth='#depth'"
    . " innerwidth='#innerwidth' innerheight='#innerheight' innerdepth='#innerdepth'"
    . " xscale='#xscale' yscale='#yscale'"
    . " xtranslate='#xtranslate' ytranslate='#ytranslate'>"
    . "#1"
    . "</ltx:inline-block>",
  properties => sub {
    my ($stomach, $box) = @_;
    my ($w, $h, $d) = $box->getSize;
    return () unless $w;
    (width => $w,
      height => $h,
      depth  => $d,
      xscale => -1,
      yscale => 1); },
  mode => 'text');

DefConstructor('\graphicspath DirectoryList', sub {
    my ($document, $paths, %props) = @_;
    foreach my $path (@{ $props{paths} }) {
      $document->insertPI('latexml', graphicspath => $path); } },
  properties => sub {
    my ($stomach, $paths) = @_;
    my @paths = ();
    foreach my $dir ($paths->getValues) {
      my $path = pathname_absolute(pathname_canonical(ToString($dir)));
      push(@paths, $path);
      PushValue(GRAPHICSPATHS => $path); }
    return (paths => [@paths]); });

# Basically, we're transforming the graphics options into graphicx format.
DefMacro('\includegraphics OptionalMatch:* [][] Semiverbatim',
  '\@includegraphics#1[#2][#3]{#4}');

DefConstructor('\@includegraphics OptionalMatch:* [][] Semiverbatim',
  "<ltx:graphics graphic='#graphic' candidates='#candidates' options='#options'/>",
  sizer      => \&image_graphicx_sizer,
  properties => sub {
    my ($stomach, $starred, $op1, $op2, $graphic) = @_;
    my $bb = ($op2 ? ToString($op1) . " " . ToString($op2) : ($op1 ? "0 0 " . ToString($op1) : ''));
    $bb =~ s/,/ /g;
    my $options = ($starred ? ($bb ? "viewport=$bb, clip" : "clip") : '');
    $graphic = ToString($graphic); $graphic =~ s/^\s+//; $graphic =~ s/\s+$//;
    my @candidates = pathname_findall($graphic, types => ['*'], paths => LookupValue('GRAPHICSPATHS'));
    if (my $base = LookupValue('SOURCEDIRECTORY')) {
      @candidates = map { pathname_relative($_, $base) } @candidates; }
    (graphic => $graphic,
      candidates => join(',', @candidates),
      options => $options); },
  alias => '\includegraphics');

# Also unimplemented... probably nothing useful to pass thru anyway?
DefConstructor('\DeclareGraphicsExtensions{}',          '');
DefConstructor('\DeclareGraphicsRule{}{}{} Undigested', '');
# Nothing done about the keyval package...
#======================================================================
1;

