#  Copyright (c) 1997-2021
#  Ewgenij Gawrilow, Michael Joswig, and the polymake team
#  Technische Universität Berlin, Germany
#  https://polymake.org
#
#  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, or (at your option) any
#  later version: http://www.gnu.org/licenses/gpl.txt.
#
#  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.
#-------------------------------------------------------------------------------

# @category Visualization
options %Visual::Graph::TightSpanDecorations=(
   %Visual::Graph::decorations,

   # Array<String> Labels for the taxa of the metric.
   Taxa=>undef,
);

object PolyhedralComplex{
   # @category Visualization
   # This is a variation of [[Polytope::VISUAL_BOUNDED_GRAPH]] for the special case of a tight span.
   # The vertices are embedded according to the metric, the others are hung in between.
   # This only produces meaningful results for extended tight spans produced from metrics, e.g. through
   # [[metric_extended_tight_span]].
   # @options %Visual::Graph::Decorations
   # @option Array<String> Taxa Labels for the taxa of the metric.
   # @option Int seed random seed value for the string embedder
   # @option String norm which norm to use when calculating the distances between metric vectors ("max" or "square")
   # @return Visual::Graph

user_method VISUAL_METRIC_TIGHT_SPAN(%Visual::Graph::TightSpanDecorations, { norm => enum("max=default", "square"), seed => undef }) {
   my ($this, $decor, $options)=@_;
   my $BG=$this->GRAPH;
   my $metric = new Matrix($this->VERTICES->cols-1,$this->VERTICES->cols);
   my @tentacle_indices = ();
   my @tentacles = ();
   my $labels = new Array<String>([ (" ") x ($this->VERTICES->rows+$this->VERTICES->cols)]);

   my $Taxa = $$decor{"Taxa"};
   if(!defined $Taxa) {
      $Taxa = new Array<String>([map {"".$_} (0 .. $this->VERTICES->cols()-2)]);
   }
   delete($$decor{"Taxa"});

   for (my $i=0; $i<$this->N_VERTICES; ++$i) {
      if ( $BG->ADJACENCY->degree($i) == 1) {
         for( my $j=1; $j<$this->VERTICES->cols; ++$j ){
            if($this->VERTICES->[$i][$j]==0){
               $metric->row($j-1) = $this->VERTICES->row($i);
               push @tentacle_indices, $i;
               push @tentacles, $j-1;
               $labels->[$i] = $Taxa->[$j-1];
               last;
            }
         }
      }
   }
   for( my $j=0; $j<$this->VERTICES->cols-1; ++$j ){
      if( is_zero($metric->row($j) )){
         for (my $i=0; $i<$this->N_VERTICES; ++$i){
            if($this->VERTICES->[$i][$j+1]==0){
               for (my $k=1;$k<$this->VERTICES->cols;++$k){
                  if($metric->[$j][$k]==0){
                     $metric->[$j][$k] = $this->VERTICES->[$i][$k];
                  }
                  else{
                     $metric->[$j][$k] = min($metric->[$j][$k], $this->VERTICES->[$i][$k]);
                  }
                  # $labels->[$i] = $this->VERTEX_LABELS->[$i]; This is not unique
               }
            }
         } 
      }
   }
   my ($tentacle_graph, $edge_weights)=graph::tentacle_graph(\@tentacles, new Matrix($metric->minor(All,~[0])));
   my $node_colors=sub { $BG->ADJACENCY->degree($_[0])==1 ? $Visual::Color::vertices : $Visual::Color::non_tax_vertices };
   my $float_vertices = convert_to<Float>($this->VERTICES/dense( zero_vector($this->VERTICES->cols-1)|unit_matrix($this->VERTICES->cols-1) ));

   my $Graph=new Visual::Graph($decor,
      Name => "Tight Span of " . $this->name,
      Graph => $BG,
      Coord => bounded_embedder($BG->ADJACENCY,$float_vertices,new Set($this->VERTICES->rows..$this->VERTICES->rows+$this->VERTICES->cols-2),new Set(@tentacle_indices), spring_embedding_3d($tentacle_graph, 'edge-weights' => $edge_weights, $options)->compute, $options->{norm} eq "max"),
      NodeLabels => $labels,
      NodeColor => $node_colors,
   );
   visualize($Graph);
}

# Visualizes the polyhedral complex.
# @options %Visual::Polygons::Decorations
# @options %geometric_options
# @return Visual::PolyhedralFan

user_method VISUAL(%Visual::Polygons::decorations, %geometric_options, {CutOff => $Visual::Color::cutoff}) : VERTICES, MAXIMAL_POLYTOPES, LINEALITY_SPACE {
   my ($this,$decor,$geom,$cutoff_decor)=@_;
   my $d = $this->VERTICES->cols;
   my $vertices=$this->VERTICES;
   my @default_labels = (0..$vertices->rows-1);
   $decor->{VertexLabels} //= $this->lookup("POINT_LABELS") // \@default_labels;
   my @cells_visual = ();
   my $bbox = defined($geom->{BoundingFacets}) ? new Matrix<Scalar>($geom->{BoundingFacets}) : polytope::bounding_box_facets($vertices, surplus_k=>6/5, fulldim=>1);
   my @codedecor = Visual::get_code_decor_keys($decor, "Vertex");
   foreach (@{$this->MAXIMAL_POLYTOPES}) {
      my $v = $vertices->minor($_,All);
      my $p = new polytope::Polytope<Scalar>(VERTICES => $v, LINEALITY_SPACE => $this->LINEALITY_SPACE);
      my $pdecor = scalar(@codedecor) ? Visual::decor_subset($decor,$_,\@codedecor) : { %$decor };
      if ($p->DIM<2) {
         delete @$pdecor{"FacetColor","FacetStyle","FacetLabels","FacetTransparency"};
      }
      my $pv = $p->VISUAL($pdecor, $geom, BoundingFacets=>$bbox);
      push @cells_visual, $pv;
   }
   visualize(new Visual::PolyhedralFan( Name => "pcom:".$this->name,
                                        PolyhedralFan => $this,
                                        @cells_visual));
}
precondition : FAN_AMBIENT_DIM { $this->FAN_AMBIENT_DIM<=4 }

}

# @topic objects/Visual::PolyhedralFan
# @category Visualization
# Visualization of a polyhedral fan as a graph
# @super Visual::Container
# @relates PolyhedralFan

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

# where to keep the view transformation matrix etc.
method representative { $_[0]->PolyhedralFan }

object PolyhedralFan {

# @category Visualization
# Visualizes the fan, intersected with the unit ball.
# @options %Visual::Polygons::Decorations
# @options %geometric_options_linear
# @return Visual::PolyhedralFan

user_method VISUAL(%Visual::Polygons::decorations, %geometric_options_linear, {CutOff => $Visual::Color::cutoff}) : RAYS, MAXIMAL_CONES, LINEALITY_SPACE {
   my ($this,$decor,$geom,$cutoff_decor)=@_;
   $decor->{VertexStyle} ||= "hidden";
   my $cutoff_color = $cutoff_decor->{CutOff};
   my $cutoff_decor3d=$cutoff_color;
   my $cutoff_decor2d=$cutoff_color;
   unless (is_like_hash($cutoff_color)) {
     $cutoff_decor3d={ FacetColor => $cutoff_color };
     $cutoff_decor2d={ EdgeColor => $cutoff_color };
   }
   my $d=$this->RAYS->cols;
   my $rays=Visual::transform_float(normalized(convert_to<Float>($this->RAYS)),$geom->{Transformation},$geom->{Offset});
   
   my $lineality = Visual::transform_float(normalized(convert_to<Float>($this->LINEALITY_SPACE)),$geom->{Transformation},$geom->{Offset});
   my $bbox = $lineality->rows > 0 ?
         polytope::bounding_box_facets<Float>(ones_vector<Float>() | $rays, surplus_k=>6/5, fulldim=>1)
         : undef;
   my $zero=new Vector<Float>($d);
   my @cells_visual = ();
   my $rays_graph = new GraphAdjacency($rays->rows+1);
   $rays_graph->edge(0,$_) for 1 .. $rays->rows;
   my @labels;
   if ((@labels=@{$this->lookup("RAY_LABELS")})==undef) {
     @labels=0..$this->N_RAYS;
   }
   unshift(@labels," ");
   my $coords = $zero / $rays;
   my $graph = new Graph(ADJACENCY=>$rays_graph);
   my $rays_vgraph = new Visual::Graph(Name =>  "Rays",
                                       Graph => $graph,
                                       Coord =>  $coords,
                                       NodeStyle => "show",
                                       VertexLabels => \@labels,
                                       EdgeColor => "0 0 0",
                                       Explodable => 0,
                                      );
   push @cells_visual, $rays_vgraph;

   my $ind = $this->MAXIMAL_CONES_INCIDENCES;
   my $i = 0;
 
   foreach (@{$this->MAXIMAL_CONES}) {
      my $maximal_cone=$_;
      my $ftr = transpose($ind->[$i]->minor(All, $maximal_cone));

      my $cone = new polytope::Cone<Rational>(
          RAYS => $this->RAYS->minor($maximal_cone,All),
          FACETS_THRU_RAYS => $ftr,
          LINEALITY_SPACE => $this->LINEALITY_SPACE);
      my $visual_cone = $cone->VISUAL($decor,$geom,$cutoff_decor, {BoundingFacets => $bbox});
            push @cells_visual, $visual_cone;
      $i++;
   }
   visualize(new Visual::PolyhedralFan( Name => "fan:".$this->name,
                                        PolyhedralFan => new PolyhedralFan(RAYS=>(ones_vector<Float>() | $rays)),
                                        @cells_visual));
}
precondition : FAN_AMBIENT_DIM { $this->FAN_AMBIENT_DIM<=3 }


}


object SubdivisionOfPoints {

# @category Visualization
# Visualizes the [[SubdivisionOfPoints]].
# @options %Visual::Polygons::Decorations
# @options %geometric_options
# @return Visual::PolyhedralFan
#
user_method VISUAL(%Visual::Polygons::decorations, {CutOff => $Visual::Color::cutoff}, %geometric_options) : POINTS, POLYHEDRAL_COMPLEX.VERTICES, POLYHEDRAL_COMPLEX.GRAPH, MAXIMAL_CELLS {
   my ($this,$decor,$cutoff_decor,$geom)=@_;
   my $d=$this->POINTS->cols;
   my $vertices=$this->POLYHEDRAL_COMPLEX->VERTICES;
   my $points=$this->POINTS;
   my @cells_visual = ();
   my $ncpoints = new Set(range(0,$this->N_POINTS-1));
   foreach (@{$this->MAXIMAL_CELLS}) {
      $ncpoints-=$_;
   }
   my @default_labels = (0..$points->rows-1);
   $decor->{VertexLabels} //= $this->lookup("POINT_LABELS") // \@default_labels;
   my @codedecor = Visual::get_code_decor_keys($decor, "Vertex");
   if ($ncpoints->size) {
      # add an extra Pointset with points that are not in maximal cells
      my $psdecor = scalar(@codedecor) ? Visual::decor_subset($decor,$ncpoints,\@codedecor) : { %$decor };
      $psdecor->{VertexColor} //= "0 0 0";
      my $ncpointset=new Visual::PointSet(Name => 'noncellpoints',
                                       Points => Visual::transform_float(dehomogenize(convert_to<Float>($points->minor($ncpoints,All))),$geom->{Transformation},$geom->{Offset}),
                                       $psdecor);
      push @cells_visual, $ncpointset;
   }
   # add all other cell visuals 
   foreach (@{$this->MAXIMAL_CELLS}) {
      my $v=$points->minor($_,All);
      my $pdecor = scalar(@codedecor) ? Visual::decor_subset($decor,$_,\@codedecor) : $decor;
      my $p=new polytope::PointConfiguration<Scalar>(POINTS => $v);
      push @cells_visual, (my $pv=$p->VISUAL( $pdecor, $geom ));
   }
   visualize(new Visual::PolyhedralFan( Name => "pcom:".$this->name,
                                        PolyhedralFan => $this,
                                        @cells_visual));
}
precondition : VECTOR_AMBIENT_DIM { $this->VECTOR_AMBIENT_DIM<=4 }

}

object HyperplaneArrangement {

# @category Visualization
# Visualizes the fan, intersected with the unit ball.
# @options %geometric_options_linear
# @return Visual::PolyhedralFan

user_method VISUAL( @ ) {
   my ($this, @args)=@_;
   $this->CHAMBER_DECOMPOSITION->VISUAL(@args);
}
}

package Visual::Color;

# Color for the vertices of a tight span not corresponding to taxa
custom $non_tax_vertices="255 255 0";

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