#  Copyright (c) 1997-2006
#  Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
#  http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de
#
#  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.
#-----------------------------------------------------------------------------
#  $Project: polymake $$Id: JReality.pm 7574 2007-01-19 12:18:11Z thilosch $

use strict;
use JavaView;
require 'JReality.def';

package JReality::File;

use Struct (
   [ '@ISA' => 'JavaView::File' ],
);

sub header
{
   my ($self) = shift;
   my $obj_name = shift;
   my $title=$self->title;
   if (!length($title)) {
      $title="unnamed";
   }
   return <<".";
/*--------------------HEADER $title--------------------*/
import de.jreality.shader.CommonAttributes;
setAccessibility(true);

synchronized(root) {

$obj_name = new SceneGraphComponent();
$obj_name.setName("$title");
root.addChild($obj_name);
/*----------------------------------------*/
.
}

sub trailer { 
  return <<".";
root.notify();
} 
return;
.
}

sub toString
{
   dbg_print(" called JReality::File->toString .. ") if $Switches::d;

   my ($self) = shift;
   my $n_geom = shift;

   my $obj_name = "geom$n_geom";
   my $text = $self->header($obj_name);

   my $number = 0;

   foreach my $geometry (@{$self->geometries}) {
      $text .= $geometry->toString($number, $obj_name);
      $number++;
   }
   
   $text .= $self->trailer;

   return $text;
}

sub print_it { my ($self) = @_; $self->SUPER::print_it; }

##############################################################################################
#
#  Basis class for all graphical objects handled by jreality
#

package JReality::geometry;

my $thickness_factor = 40;

sub new
{
  my $class = shift;
  my $self = { @_ }; #hashmap
  bless $self, $class;
  $self->initialize;
  return $self;
}

sub initialize
{
   my $self=shift;
   if (! exists $self->{name}) {
      $self->{name}="geometry";
   }
   if (! exists $self->{points}) {
      croak( ref($self), "::initialize: parameter 'points' missing" );
   }
   if (! exists $self->{pointSet_dim}) {
      croak( ref($self), "::initialize: parameter 'pointSet_dim' missing" );
   }
   $self->{pointSet_point_flag} ||= "show";

   if (! exists $self->{points_thickness}) {
      $self->{points_thickness}=$JReality::default::points_thickness;
   }
   $self->{pointSet_thicknesses_flag} ||= exists $self->{points_thicknesses_list} ? "show" : "hide";

   if (! exists $self->{points_color}) {
     $self->{points_color}= $Visual::Color::vertices;
	#$JReality::default::points_color;
   }

   ## JReality "bug" (see above comment)
   #if(! exists $self->{number_of_points}) {
   #  $self->{number_of_points} = @{$self->{points}} ;
   #}
}

sub appearance 
{

  my $self = shift;
  my $obj_name = shift;

  # print "name 2:" . "$obj_name";

  my @colors = ($self->{points_color} =~ /\d+/g);
  map {$_ = $_ / 255} @colors; 

  #FIXME: points thickness
  my $point_thickness = $self->{points_thickness} / $thickness_factor;
  
  my $text =<<".";
appearance = new Appearance();
.
  if($self->{pointSet_point_flag} eq "show") {
    $text .=<<".";
appearance.setAttribute(de.jreality.shader.CommonAttributes.VERTEX_DRAW, true);
appearance.setAttribute(de.jreality.shader.CommonAttributes.SPHERES_DRAW, true);
.
  } else {
    $text .=<<".";
appearance.setAttribute(de.jreality.shader.CommonAttributes.VERTEX_DRAW, false);
appearance.setAttribute(de.jreality.shader.CommonAttributes.SPHERES_DRAW, false);
.
  }
  $text .=<<".";
$obj_name.setAppearance(appearance);
.
  return $text;

}

sub add_point_colors
{
  my $self = shift;
  my $text = "";

  $text .= "colors = new double[]{ ";
  for(my $i=0; $i < (@{$self->{points}}); $i++)
  {

    my @colors;
    
    if (exists $self->{points_colors_list}) {
       @colors = split(" ",${$self->{points_colors_list}}[$i]);
    } else {
       @colors = ($self->{points_color} =~ /\d+/g);
    }
    map {$_ = $_ / 255} @colors; 
  
    $text .= join(",", @colors);
    if($i + 1 != @{$self->{points}}) {
      $text .= ", \n";
    }
  }
  $text .= " }; \n";
  return $text;
}


## scale coordinates such that mean distance to barycenter == 1;
## TODO: for interactive components this does not make sense,
## in particular for the schlegel interactive where coordinates
## are here (1,0,0),(2,0,0),(3,0,0) ...
## TODO: for geometries with multiple elements this does
## not work properly
sub scale
{
  my $self = shift(@_);
  my @coords = @_;

  ## barycenter
  my @c;
  my $N = scalar @{$self->{points}};

  for(my $i=0; $i < $N; $i++)
  {
    for(my $k=0; $k < 3; $k++)
    {
      @c[$k] += 1/$N * @coords[3*$i + $k];
    }
  }
  
  ## mean distance to barycenter
  my $dist = 0;
  for(my $i=0; $i < $N; ++$i)
  {
    my @p;
    for(my $k=0; $k < 3; ++$k)
    {
      @p[$k] += (@c[$k] - @coords[3*$i+$k]);
    }
   
    $dist += sqrt(@p[0]*@p[0] + @p[1]*@p[1] + @p[2]*@p[2]);
  }
  $dist = $dist/$N;

  ## change coordinates
  if($dist !=  0)
  {
    for(my $i=0; $i < $N; $i++)
    {
      for(my $k=0; $k < 3; $k++)
      {
	@coords[3*$i+$k] = @c[$k] + (1/$dist)*(@coords[3*$i+$k] - @c[$k]); 
      }
    }
  }
  return @coords;
}


sub pointSet
{
  my ($self) = @_;
  my $text=<<".";
/*--------------------$self->{name}--------------------*/
pts = new double[][]{
.

  dbg_print ("generating pointset for jreality ...") if $Switches::d; 

  my @points;

  foreach my $point (@{$self->{points}}) {
    #    chomp $point;
    my @coord = split /\s+/, $point;
  
    ## HACK; for Visual::Graph dimension ($pointSet_dim) is set to 3, although
    ## the graph is the Schlegel diagram of a 3-polytope
    if(@coord == 2) {
      push @coord, "0";
    }
    my $line = "{".(join(",", @coord))."}";
    push @points, $line;
  }

  $text .= (join(",\n",@points))."\n};//pts \n";

  $text .= $self->pointLabels;

  return $text;
}

sub pointLabels
{
  my ($self) = @_;
  my $labels = $self->{points_labels_list};
  my $text = "labels = new String[]{";
  my @labels;
  if(defined($labels)) {
    for(my $i=0; $i < @{$self->{points}}; ++$i) {
      push @labels, "\"".($labels->($i))."\"";
    }
  }
  $text .= join(",",@labels);
  $text .= "};\n";
  return $text;
}


# use only for displaying pointsets
sub trailer 
{

  my ($self) = shift;
  my $number = shift;
  my $obj_name = shift;
  my $n_points = @{$self->{points}};

  my $text .=<<".";
psf = new PointSetFactory();
psf.setVertexCount($n_points);
psf.setVertexCoordinates(pts);

if(labels.length == $n_points) psf.setVertexLabels(labels);
.

  $text .= $self->add_point_colors;
  $text .=<<".";
psf.setVertexColors( new DoubleArrayArray.Inlined(colors, 3));
psf.update();
part1=new SceneGraphComponent();
part1.setGeometry(psf.getGeometry());
part1.setName("PointSet $number");
part1.setAppearance(appearance);
$obj_name.addChild(part1);
/*----------------------------------------*/
.
  return $text;
}

# use only for displaying pointsets
sub toString
{
  ## generate data for jreality application

  dbg_print("called JReality::geometry->toString ..") if $Switches::d;

  my ($self) = shift;
  my $number = shift;
  my $obj_name = shift;

  return $self->pointSet . $self->pointLabels . $self->appearance($obj_name) . $self->trailer($number, $obj_name) ;
}



##############################################################################################
#
#  Wire model (e.g. a graph)
#
package JReality::wire;
use vars '@ISA';
@ISA=qw( JReality::geometry );

sub new
{
  my $class = shift;
  my $self = { @_ }; #hashmap
  bless $self, $class;
  $self->initialize;
  return $self;
}

sub appearance
{
  my $self = shift;
  my $obj_name = shift;
  my $text = $self->SUPER::appearance($obj_name);

  if($self->{lines_color} =~ /\s*0\s+0\s+0\s*/) {
  	$self->{lines_color} = "70 70 70";
  }
  
  my @line_colors = ($self->{lines_color} =~ /\d+/g);
  map {$_ = $_ / 255} @line_colors; 
  my $lines_thickness = $self->{lines_thickness} / $thickness_factor;
  if( $self->{lineSet_line_flag} eq "hide") {
  	$text .= "appearance.setAttribute(CommonAttributes.TUBES_DRAW,false);\nappearance.setAttribute(CommonAttributes.EDGE_DRAW,false);\n";
  } else {
  	$text .= "appearance.setAttribute(CommonAttributes.TUBES_DRAW,true);\nappearance.setAttribute(CommonAttributes.EDGE_DRAW,true);\n";
  }
  
  $text .= <<".";
appearance.setAttribute(
  "lineShader.diffuseColor",
  new java.awt.Color((float)@line_colors[0] ,(float)@line_colors[1],(float)@line_colors[2])
);
.

  return $text;

}

sub initialize
{
   my $self=shift;
   $self->SUPER::initialize;
   if (! exists $self->{lines}) {
      croak( ref($self), "::initialize: parameter 'lines' missing" );
   }
   $self->{lineSet_line_flag} ||= "show";
   $self->{lineSet_arrow_flag} ||= "hide";

   if (! exists $self->{lines_thickness}) {
      $self->{lines_thickness}=$JReality::default::lines_thickness;
   }
   $self->{lineSet_thicknesses_flag} ||= exists $self->{lines_thicknesses_list} ? "show" : "hide";
   if (! exists $self->{lines_color}) {
     $self->{lines_color}= $Visual::Color::edges;
	#$JReality::default::lines_color;
   }
}

sub add_line_colors
{
  my $self = shift;
  my $text = "";

  $text .= "lineColors = new double[]{ ";
  for(my $i=0; $i < (@{$self->{lines}}); $i++)
  {
    my @colors;
    
    if (exists $self->{lines_colors_list}) {
       map { s/\s*0\s+0\s+0\s*/70 70 70/; } @{$self->{lines_colors_list}};
       @colors = split(" ",${$self->{lines_colors_list}}[$i]);
    } else {
       @colors = ($self->{lines_color} =~ /\d+/g);
    }
    map {$_ = $_ / 255} @colors; 
  
    $text .= join(",", @colors);
    if($i + 1 != @{$self->{lines}}) {
      $text .= ", \n";
    }
  }
  $text .= " }; \n";
  return $text;
}

sub lineSet {

  my ($self) = @_;
  my $text .="lines = new int[][]{ ";

  dbg_print ("generating lineset for jreality ...") if $Switches::d;

  my @lines = (@{$self->{lines}});
  map { $_=$self->process_line($_)} @lines;

  $text .= join(",", @lines);
  $text .= " }; \n";
  
  return $text;

}

sub process_line {
  my ($self, $line) = @_;
  my $text .= "{ ";

  my @indices = ( $line =~ /\d+/g);
  @indices = reverse @indices;

  $text .= join(",", @indices);
  $text .= " } \n";

  return $text;
}

sub trailer {

  my $self = shift;
  my $number = shift;
  my $obj_name = shift;

  my $n_points = @{$self->{points}};
  my $n_lines = @{$self->{lines}};

  my $text .=<<".";
ilsf = new IndexedFaceSetFactory();
ilsf.setLineCount($n_lines);
ilsf.setVertexCount($n_points);
ilsf.setEdgeIndices(lines);
ilsf.setVertexCoordinates(pts);
if(labels.length == $n_points) ilsf.setVertexLabels(labels);
.
  ## point appearance 
  $text .= $self->SUPER::add_point_colors;
  $text .=<<".";
ilsf.setVertexColors(new DoubleArrayArray.Inlined(colors, 3) );
.

  $text .= $self->add_line_colors;
  $text .=<<".";
ilsf.setEdgeColors( new DoubleArrayArray.Inlined(lineColors, 3) );
ilsf.update();
part1=new SceneGraphComponent();
part1.setGeometry(ilsf.getGeometry());
part1.setAppearance(appearance);
part1.setName("LineSet $number");
$obj_name.addChild(part1);
/*----------------------------------------*/
.
  return $text;
}

sub toString {
  dbg_print("called JReality::wire->toString ..") if $Switches::d;

  my $self = shift;
  my $number = shift;
  my $obj_name = shift;

  my $text = "";
  $text .= $self->SUPER::pointSet;
  $text .= $self->lineSet;
  $text .= $self->appearance($obj_name);
  $text .= $self->trailer($number, $obj_name);
  return $text;
}


################################################################
#
#  Solid 2-d or 3-d body
#

package JReality::solid;
use vars '@ISA';

@ISA=qw( JReality::geometry );

sub initialize
{
   my $self=shift;
   $self->SUPER::initialize;
   if (! exists $self->{faces}) {
      croak( ref($self), "::initialize: parameter 'faces; missing\n" );
   }
   $self->{faceSet_face_flag} ||= "show";
   $self->{faceSet_backface_flag} ||= "hide";
   $self->{faceSet_edge_flag} ||= "show";

   if (! exists $self->{faces_color}) {
     $self->{faces_color}= $Visual::Color::facets;
	#$JReality::default::faces_color;
   }

   if (! exists $self->{edges_thickness}) {
      $self->{edges_thickness}=$JReality::default::lines_thickness;
   }
   
   if (! exists $self->{edges_color}) {
      #$self->{edges_color}=$JReality::default::lines_color;
     $self->{edges_color}=$Visual::Color::edges;
   }

   if(defined $self->{material_flag}) {
     $self->{faceSet_backface_flag}    = $self->{material_flag};
     $self->{material_diffuseColor}  ||= $JReality::default::diffuseColor;
     $self->{material_emissiveColor} ||= $JReality::default::emissiveColor;
     $self->{material_shininess}     ||= $JReality::default::shininess;
     $self->{material_specularColor} ||= $JReality::default::specularColor;
     $self->{material_transparency}  ||= $JReality::default::transparency;
   } else {
     $self->{material_flag} = "hide";
   }
}

sub add_face_colors
{
  my $self = shift;
  my $text = "";

  $text .= "faceColors = new double[]{ ";
  for(my $i=0; $i < (@{$self->{faces}}); $i++)
  {
    my @colors;
    
    if (exists $self->{faces_colors_list}) {
       @colors = split(" ",${$self->{faces_colors_list}}[$i]);
    } else {
       @colors = ($self->{faces_color} =~ /\d+/g);
    }
    map {$_ = $_ / 255} @colors; 
  
    $text .= join(",", @colors);
    if($i + 1 != @{$self->{faces}}) {
      $text .= ", \n";
    }
  }
  $text .= " }; \n";
  return $text;
}

sub appearance {

  my $self = shift;
  my $obj_name = shift;
  my $text = $self->SUPER::appearance($obj_name);

  $text .= <<".";
appearance.setAttribute(CommonAttributes.POLYGON_SHADER +"."+CommonAttributes.SMOOTH_SHADING,false);
.
  if( $self->{material_flag} =~ "show")
  {
    my $transparency = ($self->{material_transparency});
    $text .= <<".";
appearance.setAttribute(CommonAttributes.TRANSPARENCY_ENABLED,true);
appearance.setAttribute(CommonAttributes.TRANSPARENCY,$transparency);
.
  }
    if( $self->{faceSet_edge_flag} eq "hide") {
  	$text .= "appearance.setAttribute(CommonAttributes.TUBES_DRAW,false);\nappearance.setAttribute(CommonAttributes.EDGE_DRAW,false);\n";
  } else {
  	$text .= "appearance.setAttribute(CommonAttributes.TUBES_DRAW,true);\nappearance.setAttribute(CommonAttributes.EDGE_DRAW,true);\n";
  }
  if( $self->{faceSet_face_flag} eq "hide")
    {
    $text .= <<".";
appearance.setAttribute(CommonAttributes.FACE_DRAW, false);
.
    }
  if($self->{edges_color} =~ /\s*0\s+0\s+0\s*/) {
  	$self->{edges_color} = "70 70 70";
  }
  
  my @edge_colors = ($self->{edges_color} =~ /\d+/g);
  
  map {$_ = $_ / 255} @edge_colors; 

  #FIXME: edge thicknesses
  my $edges_thickness = $self->{edges_thickness} / $thickness_factor;
#  $text .= "appearance.setAttribute(CommonAttributes.LINE_SHADER+\".\"+CommonAttributes.TUBE_RADIUS,$edges_thickness);\n";

  $text .= <<".";
appearance.setAttribute(CommonAttributes.LINE_SHADER +"."+ CommonAttributes.DIFFUSE_COLOR,
  new java.awt.Color((float)@edge_colors[0] ,(float)@edge_colors[1],(float)@edge_colors[2]));
.

  return $text;
}

sub faceSet {

  my ($self) = @_;
  my $text .="faces = new int[][]{ ";

  dbg_print ("generating faceset for jreality ...") if $Switches::d;

  my @faces = (@{$self->{faces}});
  map { $_=$self->process_face($_)} @faces;

  $text .= join(",", @faces);
  $text .= " }; \n";
  
  return $text;
}

sub process_face {

  my ($self, $face) = @_;
  my $text .= "{ ";

  my @indices = ( $face =~ /\d+/g);
  @indices = reverse @indices;

  $text .= join(",", @indices);
  $text .= " } \n";

  return $text;
}

sub trailer {

  my $self = shift;
  my $number = shift;
  my $obj_name = shift;

  my $n_faces = @{$self->{faces}};
  my $n_points = @{$self->{points}};

  my $text .=<<".";
IndexedFaceSetFactory ifsf = new de.jreality.geometry.IndexedFaceSetFactory();
ifsf.setGenerateFaceNormals(true);
ifsf.setGenerateVertexNormals(true);
ifsf.setGenerateEdgesFromFaces(true);
ifsf.setVertexCount($n_points);
ifsf.setFaceCount($n_faces);
ifsf.setVertexCoordinates(pts);
ifsf.setFaceIndices(faces);
if(labels.length == $n_points) { ifsf.setVertexLabels(labels); }
.
  $text .= $self->SUPER::add_point_colors;
  $text .=<<".";
ifsf.setVertexColors( new DoubleArrayArray.Inlined(colors, 3));
.
  $text .= $self->add_face_colors;
  $text .=<<".";
ifsf.setFaceColors( new DoubleArrayArray.Inlined(faceColors, 3));
ifsf.update();
part1=new SceneGraphComponent();
part1.setGeometry(ifsf.getGeometry());
part1.setName("FaceSet $number");
part1.setAppearance(appearance);
$obj_name.addChild(part1);
/*----------------------------------------*/
.
  return $text;
}


sub toString {
  dbg_print("called JReality::solid->toString ..") if $Switches::d;

  my $self = shift;
  my $number = shift;
  my $obj_name = shift;

  my $text = "";
  $text .= $self->SUPER::pointSet;
  $text .= $self->faceSet;
  $text .= $self->appearance($obj_name);
  $text .= $self->trailer($number, $obj_name);
  return $text;
}

1

# Local Variables:
# mode:perl
# c-basic-offset:3
# End:

