########################################################################
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#  
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor,
#  Boston, MA  02110-1301, USA.
#  
#  ---
#  Copyright (C) 2011-2015, Simon Hampe <simon.hampe@googlemail.com>
#
#  ---
#  Copyright (c) 2016-2021
#  Ewgenij Gawrilow, Michael Joswig, and the polymake team
#  Technische Universität Berlin, Germany
#  https://polymake.org
#
#
#       Contains visualization procedures for Cycle
########################################################################

package Visual::Cycle;
use Polymake::Struct (
   [ '@ISA' => 'Container' ],
   [ '$Cycle' => '#%'],
);

options %Visual::Cycle::BoundingDecorations=(
   %Visual::Polygons::decorations,

   # Flexible<Int> The visualization is always a visualization in affine coordinates. This 
   # chooses the chart
   Chart => 0,

   # String if set to "show", the labels indicating the weights are shown
   WeightLabels => enum("show"),

   # String if set to "show", the lattice lengths of the edges of a one-dimensional cycle are displayed.
   LengthLabels => enum("show"),

   # String If set to "show", the labels indicating the vertex coordinates are displayed, otherwise they are not. Note that this is expensive and significantly increases computation time.
   CoordLabels => enum("show"),

   # Flexible<Rational> The distance of the border of the bounding box from the smallest box 
   # containing the affine points of the complex. This is only relevant, if you do not specify BoundingFacets
   BoundingOffset => 1,

   # Bool make it a bounding cube. This is only relevant, if you do not specify BoundingFacets
	MakeCube => 1,

   # Matrix<Rational> A fixed bounding polytope described by facets.
   BoundingFacets => undef,

   # Array<String> A list of strings to be displayed as labels for the maximal cones. If this is not empty, weight and length labels are suppressed regardless of other options.
   ConeLabels => undef,
);

object Cycle {

   # @category Visualization
   # Displays a (possibly weighted) polyhedral complex by intersecting it with a bounding polytope described by BoundingFacets.
	# If BoundingFacets are not explicitly given one may use the MakeCube and BoundingOffset keywords for further settings of the automatically created bounding box.
   # @options
   # @option Int Chart Which affine chart to visualize, i.e. which coordinate to shift to 0. This is 0 by default.
   # @option String WeightLabels If "hidden", no weight labels are displayed. Not hidden by default.
   # @option String CoordLabels If "show", coordinate labels are displayed at vertices. Hidden by default.
   # @option Rational BoundingOffset The distance from a bounding facet to the nearest bounded vertex
   # @option Matrix<Rational> BoundingFacets describing the bounding polytope
   user_method VISUAL(%Visual::Cycle::BoundingDecorations, {CutOff => $Visual::Color::cutoff}) {
      my ($this,$decor,$cutoff_decor)=@_;

      if (is_empty($this)) {
         die "Cannot draw zero cycle";
      }
      # Take care of custom decor options and delete them
      my $chart = delete $decor->{Chart};
      my $showWeights = delete($decor->{WeightLabels}) eq "show" && defined($this->lookup("WEIGHTS"));
      my $showLengths = delete($decor->{LengthLabels}) eq "show";
      if ($showLengths && $this->PROJECTIVE_DIM != 1) {
         die("Can only display edge lengths for one-dimensional cycles");
      }

      my $showCoordinates = delete($decor->{CoordLabels}) eq "show";
      if (!$showCoordinates) {
         $decor->{VertexLabels} = "hidden";
      }

      my $boundingOffset = delete($decor->{BoundingOffset});
      my $makecube = delete($decor->{MakeCube});
      my $boundingFacets = delete($decor->{BoundingFacets}) // $this->bounding_box_facets($boundingOffset,$makecube,$chart);
                
      my $clabels = delete($decor->{ConeLabels});
      my $haveConeLabels = defined($clabels)
        and $showWeights = 0;

      $clabels //= produce_labels( $showWeights ?  $this->WEIGHTS : new Vector<Integer>(),
                                   $showLengths ? $this->CURVE_EDGE_LENGTHS : new Array<Rational>(),
                                   $showWeights, $showLengths );
        
      # Set some default values
      $decor->{PointThickness} //= 0.1;

      # Create bounding box
      my $affine_version = $this->affine_chart($chart);

      my @result = computeBoundedVisual($this->affine_chart($chart), $boundingFacets, $clabels);
      my @cells_visual;
      my $weightCenters = pop(@result);

      for my $cell (@result) {
         $cell->VERTEX_LABELS = ();

         # Compute coordinate labels if necessary
         if ($showCoordinates) {
            # Create labels for vertices
            my @labels = ();
            my $vtx = $cell->VERTICES;
            for (my $row = 0; $row < $vtx->rows(); ++$row) {
               my $coords = $vtx->row($row)->slice(sequence(1, $vtx->cols()-1));
               @labels = (@labels,"(".$coords.")");
            }
            $cell->VERTEX_LABELS = \@labels;
         }

         push @cells_visual, ($cell->VISUAL($decor));
      }
      if ($showWeights || $showLengths || $haveConeLabels) {
         push @cells_visual, $weightCenters->VISUAL_POINTS(PointThickness=>0);
      }
      visualize(new Visual::Cycle(Name =>$this->name,
                                  Cycle=> $this,
                                  @cells_visual));
   }

   # @category Visualization
   # Same as [[VISUAL]]. Kept for backwards compatibility.
   user_method BB_VISUAL(%Visual::Cycle::BoundingDecorations, {CutOff => $Visual::Color::cutoff}) { 
      warn "BB_VISUAL is considered deprecated. Please use VISUAL instead\n";
      my ($this,@args)=@_;
      $this->VISUAL(@args);
   }

   sub produce_labels {
      my ($wlabels, $llabels, $showWeights, $showLengths) = @_;
      my $total = max($wlabels->dim(), $llabels->size());
      my @clabels = ();
      for my $i (0 .. $total-1) {
         my $str = "# $i: ";
         if ($showWeights) {
            $str = $str."weight ".$wlabels->[$i];
         }
         if ($showLengths) {
            if($showWeights) { $str = $str." / ";}
            $str = $str."length ".$llabels->[$i]
         }
         push @clabels, $str;
      }
      return new Array<String>(\@clabels);
   }
}#END object Cycle

# Local Variables:
# mode: perl
# cperl-indent-level:3
# indent-tabs-mode:nil
# End:
