#  Copyright (c) 1997-2018
#  Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
#  http://www.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
# Options for visualizing fans.
options %geometric_options=(
  # Matrix
  BoundingBox => undef,
);

   # @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);
}
}

# @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.
# @return Visual::PolyhedralFan

user_method VISUAL(%Visual::Polygons::decorations, {CutOff => $Visual::Color::cutoff}) : RAYS, MAXIMAL_CONES, LINEALITY_SPACE {
   my ($this,$decor,$cutoff_decor)=@_;
   $decor->{VertexStyle} ||= "hidden";
   my $cutoff_color = $cutoff_decor->{CutOff};
   my $cutoff_decor3d=$cutoff_color;
   my $cutoff_decor2d=$cutoff_color;
   unless (is_hash($cutoff_color)) {
     $cutoff_decor3d={ FacetColor => $cutoff_color };
     $cutoff_decor2d={ EdgeColor => $cutoff_color };
   }
   my $d=$this->RAYS->cols;
   my $rays=normalized(convert_to<Float>($this->RAYS));
   my $lineality = convert_to<Float>($this->LINEALITY_SPACE);
   my $bbox = $lineality->rows > 0 ?
         polytope::bounding_box<Float>(ones_vector<Float>() | $rays, 6/5, 1)
         : undef;
   my $zero=new Vector<Float>($d);
   my @cells_visual = ();
   my $rays_graph = new props::Graph($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 / convert_to<Float>($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",
                                      );
   push @cells_visual, $rays_vgraph;

   my $ind = $this->MAXIMAL_CONES_INCIDENCES;
   my $i = 0;
 
   foreach (@{$this->MAXIMAL_CONES}) {
      my $maximal_cone=$_;
      my $ftr = $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,$cutoff_decor, {BoundingBox => $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]].
# @return Visual::PolyhedralFan
#
user_method VISUAL(%Visual::Polygons::decorations, {CutOff => $Visual::Color::cutoff}) : POINTS, POLYHEDRAL_COMPLEX.VERTICES, POLYHEDRAL_COMPLEX.GRAPH, MAXIMAL_CELLS {
   my ($this,$decor,$cutoff_decor)=@_;
   my $d=$this->POINTS->cols;
   my $vertices=convert_to<Float>($this->POLYHEDRAL_COMPLEX->VERTICES);
   my $points=convert_to<Float>($this->POINTS);
   my @cells_visual = ();
   my $vpoints=new Visual::PointSet( Name => 'points',
                                     Points => dehomogenize($points),
                                     PointLabels =>$this->lookup("POINT_LABELS"),
                                     PointColor =>"0 0 0",
                                     $decor);
   $decor->{PointLabels} ||= "hidden";
   push @cells_visual, $vpoints;
   my $vgraph = new Visual::Graph( Name =>  "Vertices",
                                   Graph => $this->POLYHEDRAL_COMPLEX->GRAPH,
                                   Coord =>  dehomogenize($vertices),
                                   NodeStyle => "hidden",
                                   EdgeColor => "0 0 0", $decor);
   push @cells_visual, $vgraph;
   foreach (@{$this->MAXIMAL_CELLS}) {
      my $cell=$_;
      my $v=$points->minor($cell,All);
      my $p=new polytope::PointConfiguration<Rational>(POINTS => $v);
      push @cells_visual, (my $pv=$p->VISUAL( $decor ));
   }
   visualize(new Visual::PolyhedralFan( Name => "pcom:".$this->name,
                                        PolyhedralFan => $this,
                                        @cells_visual));
}
precondition : VECTOR_AMBIENT_DIM { $this->VECTOR_AMBIENT_DIM<=4 }

}

object PolyhedralComplex {

# @category Visualization
# Visualizes the polyhedral complex.
# @return Visual::PolyhedralFan
#
user_method VISUAL(%Visual::Polygons::decorations, {CutOff => $Visual::Color::cutoff}, %geometric_options) : VERTICES, MAXIMAL_POLYTOPES, LINEALITY_SPACE {
   my ($this,$decor,$cutoff_decor,$geom)=@_;
   $decor->{VertexLabels} ||= "hidden";
   my $d=$this->VERTICES->cols;
   my $vertices=convert_to<Float>($this->VERTICES);
   my @cells_visual = ();
   my @vcolor = ();
   my $dv = $decor->{VertexColor};
   if (defined($dv) and ref($dv) eq "ARRAY")  {
       @vcolor = @{$decor->{VertexColor}};
   }
   foreach (@{$this->MAXIMAL_POLYTOPES}) {
      my $cell=$_;
      my $v= ($vertices->minor($cell,All) );
      my $p=new polytope::Polytope<Float>(VERTICES => $v, LINEALITY_SPACE => $this->LINEALITY_SPACE);
      my @pcolor = map {$vcolor[$_]} @{$cell};
      my $bbox = defined($geom->{BoundingBox}) ? new Matrix<Float>($geom->{BoundingBox}) : undef;
      my $pv = scalar(@vcolor)>1
             ? $p->VISUAL($decor, VertexColor=>\@pcolor, BoundingBox=>$bbox)
             : $p->VISUAL($decor, BoundingBox=>$bbox);
      if (!defined($pv)) {print "bla not def\n";}
      push @cells_visual, $pv;
      my $dim=$p->DIM;
   }
   visualize(new Visual::PolyhedralFan( Name => "pcom:".$this->name,
                                        PolyhedralFan => $this,
                                        @cells_visual));
}
precondition : FAN_AMBIENT_DIM { $this->FAN_AMBIENT_DIM<=4 }


}

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:
