#!/usr/local/bin/perl
##############################################################
#                                                            #
#   3D Cache Hierarchy Map Generator                         #
#   --- Creating 3D cache hierarchy map --                   #
#          version 0.2-gamma, 30/Jan/1997                    #
#            Kenichi MATSUI (matsui@nemoto.ecei.tohoku.ac.jp)#
#                                                            #
##############################################################

##############################################################
# 3Dhierarchy.pl
#
# Kenichi Matsui, Jan 1997
#
# Usage: 3Dhierarchy.pl squid.conf [outputfile]
#
# A simple perl script to generate 3D cache hierarchy map from
# squid.conf. The 3D map written with VRML1.0.
# Read the line starting "cache_host" and "visible_hostname" from
# squid.conf , and generate hierarchy map.
#
# [TODO]
# 1. Generate map from many server's squid.conf.
# 2. More fascinating layout.
# 3. Support VRML2.0 or later.
#
# [URL]
# ftp://ftp.nemoto.ecei.tohoku.ac.jp/pub/Net/WWW/VRML/converter/
#     3Dhierarchy.pl
# http://www.nemoto.ecei.tohoku.ac.jp/~matsui/VRML/3Dhierarchy/
##############################################################


################## Set up some parameters ####################
### color "R G B"
$background = "0 0 0";		# background
$localhost_color  = "1 1 0";	# localhost(sphere)
$localhost_e_color = "1 1 0";	# localhost(string)
$parent_color     = "1 0 0";	# parent(sphere)
$parent_e_color   = "1 0 0";	# parent(string)
$sibling_color    = "0 0 1";	# sibling(sphere)
$sibling_e_color  = "0 0 1";	# sibling(string)
$localtoparent_line_color = "1 0 0"; # line,localhost -> parent
$localtosibling_line_color = "0 1 1"; # line,localhost -> sibling

### distance (m)
$parent_diameter  = 100;	# localhost <-> parent
$sibling_diameter = 100;	# localhost <-> sibling
$parent_height    = 100;	# parent height
$weight_height    = 10;		# not support yet

### size (m)
$parent_weight_alpha = 1;	# coefficient of line,localhost -> parent
$sibling_weight_alpha = 1;	# coefficient of line,localhost -> sibling
$weight_location  = 0.75;	# not support yet
$radius		  = 10;		# radius of sphere
$default_weight = 1;		# default size of line,localhost -> sibling

### font
# localhost
$localhost_font_size   = 10;	# size
$localhost_font_spacing = 1;	# spacing
$localhost_font_width = 140;	# string width
$localhost_font_transparency = 0; # transparency
$localhost_font_family = "SANS"; # SANS ,SERIF, TYPEWRITER
$localhost_font_style  = "BOLD"; # NONE, BOLD, ITALIC
$localhost_font_justification  = "CENTER"; # LEFT, CENTER, RIGHT
# parent
$parent_font_size   = 10;
$parent_font_spacing = 1;
$parent_font_width = 140;
$parent_font_transparency = 0;
$parent_font_family = "SANS";
$parent_font_style  = "BOLD";
$parent_font_justification  = "CENTER";
# sibling
$sibling_font_size   = 10;
$sibling_font_spacing = 1;
$sibling_font_width = 140;
$sibling_font_transparency = 0;
$sibling_font_family = "SANS";
$sibling_font_style  = "BOLD";
$sibling_font_justification  = "CENTER";

### transparency 
$localhost_transparency = 0;
$parent_transparency    = 0;
$sibling_transparency   = 0;
$local_to_parent_line   = 0;
$local_to_sibling_line  = 0;

### file name
$localhostname    = "localhost"; # localhost name (if not found "visible_hostname")
$outputfile = "3Dhierarchy.wrl"; # outputfile name (if not defined)

### VRML viewer
$viewer = "examiner";		
$title  = "3D cache hierarchy v0.2";
$speed  = "10";

#
#
################## End of Set up some parameters ##################

###################################################################
#                                                                 #
#       If you modify below, it is your own risk.                 #
#                                                                 #
###################################################################

### version
$comment = "3D cache hierarchy map generator version 0.2 (C) Kenichi Matsui (matsui\@nemoto.ecei.tohoku.ac.jp) Jan/1997"; 
$comment2 = "3D cache hierarchy map generator \n\tversion 0.2 Jan/1997 (C)Kenichi Matsui"; 

### pi
$pi               = 3.14159265;

######## signal behavior
$SIG{'HUP'} = 'quit';
$SIG{'INT'} = 'quit';
$SIG{'QUIT'} = 'quit';
$SIG{'KILL'} = 'quit';

######## get name of squid.conf and outputfile
$argc = @ARGV;

if($argc == 0){
    &print_help;
}

if($argc == 1){
    if (($ARGV[0] eq "-h")||($ARGV[0] eq "-help")){
	&print_help;
    }else{
	$conf_file = "$ARGV[0]";
    }
}

if($argc == 2){
    $conf_file = "$ARGV[0]";
    $outputfile = "$ARGV[1]";
}
    
if($argc >= 3){
    &print_help;
}

########
######## START
########
&print_start;

######## get time
&get_localtime;

######## analyze confile
&analyze_confile($conf_file);
&analyze_result;

######## calclating x,y,z 
&calc_xyz;

######## open outputfile
open(OUT,">$outputfile")||die "Can't open $outputfile\n\n";
print "Now creating vrml file... \n";

######## creating VRML file
######## header
%def_titles = ("comment", "$comment",
	       "comment2","$comment2",
	       "background", "$background",
	       "viewer", "$viewer",
	       "title", "$title",
	       "speed", "$speed",
	       "home_x", "$home_x",
	       "home_y", "$home_y",
	       "home_z", "$home_z",
	       "home2_x", "$home2_x",
	       "home2_y", "$home2_y",
	       "home2_z", "$home2_z"
	       );
&vrml_header(*def_titles);

####### localhost sphere
print OUT "DEF localhost Separator \{\n";
undef(%def_title);
%def_title = ("DEF","locahostsphere",
	      "color","$localhost_color",
	      "transp","$localhost_transparency",
	      "radius","$radius");
&Sphere(*def_title);

####### localhost name
$tmp_y = 1.5*$radius;
print OUT  <<EOFILE;
Separator {
    Transform {
	translation 0 $tmp_y $radius
    }
EOFILE
    undef(%def_title);
    %def_title = ("DEF","localhostname",
		  "e_color","$localhost_e_color",
		  "transp","$localhost_font_transparency",
		  "size","$localhost_font_size",
		  "family","$localhost_font_family",
		  "style","$localhost_font_style",
		  "string","$localhostname",
		  "spacing","1",
		  "justi","$localhost_font_justification",
		  "width","$localhost_font_width");
    &AsciiText(*def_title);
print OUT  "\}\n";
print OUT  "\}\n";

foreach (keys %host_and_type){
    ######## parent sphere
    if($host_and_type{$_} eq "parent"){
	print OUT  <<EOFILE;
	Separator {
	    Transform {
		translation $parent_x{$_} $parent_y{$_} $parent_z{$_} 
	    }
EOFILE
    undef(%def_title);
	    %def_title = ("DEF","parentsphere",
			  "color","$parent_color",
			  "transp","$parent_transparency",
			  "radius","$radius");
	    &Sphere(*def_title);
	    print OUT  "\}\n";     
    ######## parent name
	    $tmp_y = $parent_y{$_} + $radius;
	print OUT  <<EOFILE;
	Separator {
	    Transform {
		translation $parent_x{$_} $tmp_y $parent_z{$_} 
	    }
EOFILE
    undef(%def_title);
	    %def_title = ("DEF","parentname",
			  "e_color","$parent_e_color",
			  "transp","$parent_font_transparency",
			  "size","$parent_font_size",
			  "family","$parent_font_family",
			  "style","$parent_font_style",
			  "string","$_",
			  "spacing","$parent_font_spacing",
			  "justi","$parent_font_justification",
			  "width","$parent_font_width");
	    &AsciiText(*def_title);
	    print OUT  "\}\n";
    ######## localhost to parent line
		undef(%def_title);
		%def_title = ("DEF","toparentlinev",
			      "color","$localtoparent_line_color",
			      "transp","$local_to_parent_line",
			      "parts","ALL",
			      "radius","$parent_total_weight_w",
			      "height","$parent_distance{$_}");
	    $tmp_y = 0.5*$parent_height;
		print OUT  <<EOFILE;
		Separator {
		    Transform {
			translation 0 $tmp_y 0
			    }
EOFILE
    &Cylinder(*def_title);
		    print OUT  "\}\n";
		    if($parent_count > 1){
			undef(%def_title);
			%def_title = ("DEF","toparentlineh",
				      "color","$localtoparent_line_color",
				      "transp","$local_to_parent_line",
				      "width" ,"$parent_diameter",
				      "height","$parent_weight_w{$_}",
				      "depth","$parent_weight_w{$_}");
			print OUT  <<EOFILE;
			Separator {
			    Transform {
				translation $parent_line_x{$_} $parent_line_y{$_} $parent_line_z{$_}
				rotation 0 -1 0 $parent_lotation{$_}
	}
EOFILE
&Cube(*def_title);
print OUT  "\}\n";
			}
   ######## sibling sphere
   }elsif(($host_and_type{$_} eq "sibling")||($host_and_type{$_} eq "neighbor")){
       print OUT  <<EOFILE;
       Separator {
	   Transform {
	       translation $sibling_x{$_} $sibling_y{$_} $sibling_z{$_} 
	   }
EOFILE
	   undef(%def_title);
	   %def_title = ("DEF","siblingsphere",
			 "color","$sibling_color",
			 "transp","$sibling_transparency",
			 "radius","$radius");
	   &Sphere(*def_title);
	   print OUT  "\}\n";     
   ######## sibling name
	   $tmp_y = $sibling_y{$_} + $radius;
	   print OUT  <<EOFILE;
	   Separator {
	       Transform {
		   translation $sibling_x{$_} $tmp_y $sibling_z{$_} 
	       }
EOFILE
    undef(%def_title);
	   %def_title = ("DEF","siblingname",
			 "e_color","$sibling_e_color",
			 "transp","$sibling_font_transparency",
			 "size","$sibling_font_size",
			 "family","$sibling_font_family",
			 "style","$sibling_font_style",
			 "string","$_",
			 "spacing","$sibling_font_spacing",
			 "justi","$sibling_font_justification",
			 "width","$sibling_font_width");
	   &AsciiText(*def_title);
 	   print OUT  "\}\n";     

    ######## localhost to sibling line
    undef(%def_title);
    %def_title = ("DEF","tosiblingline",
		  "color","$localtosibling_line_color",
		  "transp","$local_to_sibling_line",
		  "width" ,"$sibling_diameter",
		  "height","$sibling_weight_w{$_}",
		  "depth","$sibling_weight_w{$_}");
    print OUT  <<EOFILE;
Separator {
    Transform {
        translation $sibling_line_x{$_} $sibling_line_y{$_} $sibling_line_z{$_}
	rotation 0 -1 0 $sibling_lotation{$_}
	}
EOFILE
&Cube(*def_title);
print OUT  "\}\n";

}  
}

######## end of creating VRML file

######## close outputfile
close(OUT);

########
######## END
########
&print_end;

###################################################################
#                                                                 #
#                          SubRoutines                            #
#                                                                 #
###################################################################

######## VRML header
sub vrml_header {
    #Subroutine vrml_header
    #first, you define "%" which contains title and info.
    #and this subroutine requires the "%".

    local(*titles) = @_;
print OUT  <<EOFILE;
#VRML V1.0 ascii
#
#    Generate at $date_form
#    by $titles{comment}
#    from "$conf_file"

    DEF InfoSeparator Separator {
	DEF BackgroundColor Info {
	    string "$titles{background}"
	    }
	DEF Viewer Info {
	    string "$titles{viewer}"
	    }
	DEF Title Info {
	    string "$titles{title}"
	    }
	DEF SceneInfo Info {
	    string "$titles{comment}"
	    }
	DEF ViewerSpeed Info {
	    string "$titles{speed}"
	    }
    }
    DEF Cameras Switch {
        whichChild      -1
        DEF Front PerspectiveCamera {
            position    0 0 200
        }
        DEF OverView PerspectiveCamera {
            position    -2 156 294
            orientation -1 -0.04 0.009  0.4
            focalDistance       109
            heightAngle 0.8
        }
        DEF RightSide PerspectiveCamera {
            position   176 224 271
            orientation -0.8 0.6 0.08  0.8
            focalDistance       109
            heightAngle 0.8
        }
        DEF LeftSide PerspectiveCamera {
            position    -170 152 278
            orientation -0.7 -0.6 0.5  0.8
            focalDistance       109
            heightAngle 0.8
        }
    }
	
EOFILE
}

######## Cube
sub Cube {
    #subroutine Cube
    #first, you define "%" which contains title and info.
    #and this subroutine requires the "%".

    local(*titles) = @_;
print OUT  <<EOFILE;
    DEF $titles{DEF} Separator {
	Material {
	    diffuseColor $titles{color}
	    transparency $titles{transp}
	}	    
	Cube {
	    width $titles{width}
	    height $titles{height}
	    depth $titles{depth}
	}
    }
EOFILE
    }

######## Cone
sub Cone {
    #subroutine Cone
    #first, you define "%" which contains title and info.
    #and this subroutine requires the "%".

    local(*titles) = @_;
    print OUT  <<EOFILE;
    DEF $titles{DEF} Separator {
	Material {
	    diffuseColor $titles{color}
	    transparency $titles{transp}
	}	    
	Cone {
	    parts $titles{parts}
	    bottomRadius $titles{bradius}
	    height $titles{height}
	}
    }
EOFILE
    }

######## Sphere
sub Sphere {
    #subroutine Sphere
    #first, you define "%" which contains title and info.
    #and this subroutine requires the "%".

    local(*titles) = @_;
    print OUT  <<EOFILE;
    DEF $titles{DEF} Separator {
	Material {
	    diffuseColor $titles{color}
	    transparency $titles{transp}
	}	    
	Sphere {
	    radius $titles{radius}
	}
    }
EOFILE
    }

######## Cylinder
sub Cylinder {
    #subroutine Cylinder
    #first, you define "%" which contains title and info.
    #and this subroutine requires the "%".

    local(*titles) = @_;
    print OUT  <<EOFILE;
    DEF $titles{DEF} Separator {
	Material {
	    diffuseColor $titles{color}
	    transparency $titles{transp}
	}	    
	Cylinder {
	    parts $titles{parts}
	    radius $titles{radius}
	    height $titles{height}
	}
    }
EOFILE
    }

######## AsciiText & Font
sub AsciiText {
    #subroutine AsciiText
    #first, you define "%" which contains title and info.
    #and this subroutine requires the "%".

    local(*titles) = @_;
    print OUT  <<EOFILE;
    DEF _$titles{DEF} Separator {
	Material {
	    emissiveColor $titles{e_color}
	    transparency  $titles{transp}
	}	    
	FontStyle {
	    size $titles{size}
	    family $titles{family}
	    style $titles{style}
	}
	AsciiText {
	    string "$titles{string}"
	    spacing $titles{spacing}
	    justification $titles{justi}
	    width $titles{width}
	}
    }
EOFILE
    }

######## calc x,y,z
sub calc_xyz {
    $tmp_parent_count  = 1;
    $tmp_sibling_count = 1;
    foreach (keys %host_and_type){
	if($host_and_type{$_} eq "parent"){
	    if($parent_count == 1){
		$parent_x{$_} = 0;
		$parent_y{$_} = $parent_height;
		$parent_z{$_} = 0;
		$parent_lotation{$_} = 2*$pi*($tmp_parent_count/$parent_count);
		$parent_line_x{$_} = 0;
		$parent_line_y{$_} = 0.5*$parent_height;
		$parent_line_z{$_} = 0;
#	        $parent_distance{$_} = tan($parent_diameter/$parent_height);
		$parent_distance{$_} = $parent_height;
		$parent_weight_w{$_} = $parent_weight_alpha*$host_and_weight{$_};
		$parent_total_weight_w = $parent_weight_alpha*$total_parent_weight;
	    }else{
		$parent_x{$_} = $parent_diameter*cos((2*$pi*$tmp_parent_count)/$parent_count);
		$parent_y{$_} = $parent_height;
		$parent_z{$_} = $parent_diameter*sin((2*$pi*$tmp_parent_count)/$parent_count);
		$parent_lotation{$_} = 2*$pi*($tmp_parent_count/$parent_count);
		$parent_line_x{$_} = 0.5*$parent_diameter*cos((2*$pi*$tmp_parent_count)/$parent_count);
		$parent_line_y{$_} = $parent_height;
		$parent_line_z{$_} = 0.5*$parent_diameter*sin((2*$pi*$tmp_parent_count)/$parent_count);
#	        $parent_distance{$_} = tan($parent_diameter/$parent_height);
		$parent_distance{$_} = $parent_diameter;
		$parent_weight_w{$_} = $parent_weight_alpha*$host_and_weight{$_};
		$parent_total_weight_w = $parent_weight_alpha*$total_parent_weight;
		$tmp_parent_count++;
	    }
	}elsif(($host_and_type{$_} eq "sibling")||($host_and_type{$_} eq "neighbor")){
	    $sibling_x{$_} = $sibling_diameter*cos((2*$pi*$tmp_sibling_count)/$sibling_count);
	    $sibling_y{$_} = 0;
	    $sibling_z{$_} = $sibling_diameter*sin((2*$pi*$tmp_sibling_count)/$sibling_count);
	    $weight_x{$_}  = $weight_location*$sibling_x{$_};
	    $weight_y{$_}  = $weight_height;
	    $weight_z{$_}  = $weight_location*$sibling_z{$_};	
	    $sibling_weight_w{$_} = $sibling_weight_alpha*$host_and_weight{$_};
	    $sibling_total_weight_w = $sibling_weight_alpha*$total_sibling_weight;
	    $sibling_lotation{$_} = 2*$pi*($tmp_sibling_count/$sibling_count);
	    $sibling_line_x{$_} = 0.5*$sibling_diameter*cos((2*$pi*$tmp_sibling_count)/$sibling_count);
	    $sibling_line_y{$_} = 0;
	    $sibling_line_z{$_} = 0.5*$sibling_diameter*sin((2*$pi*$tmp_sibling_count)/$sibling_count);
#	    $sibling_distance{$_} = tan($sibling_diameter/$sibling_height);
	    $sibling_distance{$_} = $sibling_diameter;
	    $tmp_sibling_count++;
	}
    }
    $home_x = 0;
    $home_y = 0;
    $home_z = 2*$sibling_diameter;
    $home2_x = 0;
    $home2_y = 2*$sibling_diameter*sin($pi/2);
    $home2_z = 2*$sibling_diameter*cos($pi/2);
}

######## get data from configure file
sub analyze_confile {
    local $filename = $_[0];

    open(CONF,"$filename")||die "\n\tCan't open $filename\n\n";

    print "Now reading $filename... \n";

    while(<CONF>){
	next if /^#/;
	if(/^cache_host\s/){
	    @data = split(/\s+/);
	    $host_and_type{$data[1]} = $data[2];
	    $host_and_weight{$data[1]} = $default_weight;
	    foreach (@data){
		if(/weight/){
		    @weight = split(/=/);
		    $host_and_weight{$data[1]} = $weight[1];
		}else{
		    $host_and_weight{$data[1]} = $default_weight;
		}
	    }
	    if((/\ssibling\s/)||(/\sneighbor\s/)){
		$sibling_count++;
		$total_sibling_weight += $host_and_weight{$data[1]}
	    }elsif(/\sparent\s/){
		$parent_count++;
		$total_parent_weight  += $host_and_weight{$data[1]}
	    }
	}elsif(/^visible_hostname\s/){
	    @data = split(/\s+/);
	    $localhostname = "$data[1]";
	}
    }
    close(CONF);
}
     
######## show data from configure file
sub analyze_result {
    print "\nHOSTNAME\tType\tWeight\n";
    print "-------------------------------------\n";
    foreach (sort keys %host_and_type){
	print "$_ $host_and_type{$_} $host_and_weight{$_}\n";
    }
    print "-------------------------------------\n";
    print "Total Parent  = $parent_count\n";
    print "Total Sibling = $sibling_count\n";
    print "localhostname = $localhostname\n\n";
}


######## get time
sub get_localtime {
    local($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)
        = localtime;
    local($mname) = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
                     "Aug", "Sep", "Oct", "Nov", "Dec")[$mon];
    local($dname) = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
                     "Sat")[$wday]; 
    $mon++;
    $date_form = "$mday/$mname/$year, $hour:$min:$sec";
}

######## START message
sub print_start {
    print "$comment2\n\n";
    print "\tInput confile: $conf_file\n";
    print "\tOutput vrmlfile: $outputfile\n\n";
}

######## END message
sub print_end {
    print "finished.\n";
    print "\t Outputfile is $outputfile\n";
}


####### HELP message
sub print_help {
    print "$comment2\n";
    print "--Creating 3D cache hierarchy map--\n\n";
    print "\tUsage: 3Dhierarchy squid.conf [outputfile]\n";
    print "\t    Options:\n";
    print "\t       -h\t print this message\n";
    print "\t       -help\t print this message\n"; 
    print "\n";
    exit;
}


######## when caught signal
sub quit {
    print "Caught signal. exiting";
    close(CONF);
    print ".";
    close(OUT);
    print ".";
    unlink($outputfile);
    print ".";
    print "done\n";
    exit;
}

########
######## END of whole script
########
exit;

