package Inline::Java::Portable ;

use strict ;
use Exporter ;
use Carp ;
use Config ;
use File::Find ;
use File::Spec ;

our @ISA = qw(Exporter) ;
our $VERSION = '0.53_90' ;

# Here is some code to figure out if we are running on command.com
# shell under Windows.
my $COMMAND_COM =
	(
		($^O eq 'MSWin32')&&
		(
			($ENV{PERL_INLINE_JAVA_COMMAND_COM})||
			(
				(defined($ENV{COMSPEC}))&&
				($ENV{COMSPEC} =~ /(command|4dos)\.com/i)
			)||
			(`ver` =~ /win(dows )?((9[58])|(m[ei]))/i)
		)
	) || 0 ;


sub debug {
	if (Inline::Java->can("debug")){
		return Inline::Java::debug(@_) ;
	}
}


# Cleans the CLASSPATH environment variable and adds
# the paths specified.
sub make_classpath {
	my @paths = @_ ;

	my @list = () ;
	if (defined($ENV{CLASSPATH})){
		push @list, $ENV{CLASSPATH} ;
	}
	push @list, @paths ;

	my $sep = Inline::Java::Portable::portable("ENV_VAR_PATH_SEP_CP") ;
	my @cp = split(/$sep+/, join($sep, @list)) ;

	# Clean up paths
	foreach my $p (@cp){
		$p =~ s/^\s+// ;
		$p =~ s/\s+$// ;
	}

	# Remove duplicates, remove invalids but preserve order
	my @fcp = () ;
	my %cp = map {$_ => 1} @cp ;
	foreach my $p (@cp){
		if ($cp{$p}){
			if ($p =~ /^(.*)\/\*$/){
				my $d = $1 ;
				if (($d)&&(-d $d)){
					Inline::Java::debug(2, "keeping wildcard classpath as '$p'") ;
					my $fp = File::Spec->rel2abs($d) . '/*' ;
					push @fcp, Inline::Java::Portable::portable("SUB_FIX_JAVA_PATH", $fp) ;
				}
			}
			elsif (($p)&&(-e $p)){
				my $fp = File::Spec->rel2abs($p) ;
				push @fcp, Inline::Java::Portable::portable("SUB_FIX_JAVA_PATH", $fp) ;
			}
			else{
				Inline::Java::debug(2, "classpath candidate '$p' scraped") ;
			}
			delete $cp{$p} ;
		}
	}

	my $cp = join($sep, @fcp) ;

	return (wantarray ? @fcp : $cp) ;
}


sub get_jar_dir {
	my $path = $INC{"Inline/Java.pm"} ;
	my ($v, $d, $f) = File::Spec->splitpath($path) ;

	# This undef for the file should be ok.
	my $dir = File::Spec->catpath($v, $d, 'Java', undef) ;

	return File::Spec->rel2abs($dir) ;
}


sub get_server_jar {
	return File::Spec->catfile(get_jar_dir(), 'InlineJavaServer.jar') ;
}


sub get_user_jar {
	return File::Spec->catfile(get_jar_dir(), 'InlineJavaUser.jar') ;
}


sub get_source_dir {
	return File::Spec->catdir(get_jar_dir(), 'sources') ;
}


# This maybe could be made more stable
sub find_classes_in_dir {
	my $dir = shift ;

	my @ret = () ;
	find(sub {
		my $f = $_ ;
		if ($f =~ /\.class$/){
			my $file = $File::Find::name ;
			my $fdir = $File::Find::dir ;
			my @dirs = File::Spec->splitdir($fdir) ;
			# Remove '.'
			shift @dirs ;
			# Add an empty dir to get the last '.' (for '.class')
			if ((! scalar(@dirs))||($dirs[-1] ne '')){
				push @dirs, '' ;
			}
			my $pkg = (scalar(@dirs) ? join('.', @dirs) : '') ;
			my $class = "$pkg$f" ;
			$class =~ s/\.class$// ;
			push @ret, {file => $file, class => $class} ;
		}
	}, $dir) ;

	return @ret ;
}



my $map = {
	_DEFAULT_ => {
		EXE_EXTENSION		=>	$Config{exe_ext},
		GOT_ALARM			=>  $Config{d_alarm} || 0,
		GOT_FORK			=>	$Config{d_fork} || 0,
		GOT_NEXT_FREE_PORT	=>	1,
		GOT_SYMLINK			=>	1,
		GOT_SAFE_SIGNALS	=>	1,
		ENV_VAR_PATH_SEP	=>	$Config{path_sep},
		SO_EXT				=>	$Config{dlext},
		PREFIX				=>	$Config{prefix},
		LIBPERL				=>	$Config{libperl},
		DETACH_OK			=>	1,
		SO_LIB_PATH_VAR		=>	$Config{ldlibpthname},
		ENV_VAR_PATH_SEP_CP	=>	':',
		IO_REDIR			=>  '2>&1',
		MAKE				=>	'make',
		DEV_NULL			=>  '/dev/null',
		COMMAND_COM			=>  0,
		SUB_FIX_JAVA_PATH	=>	undef,
		SUB_FIX_CMD_QUOTES	=>	undef,
		SUB_FIX_MAKE_QUOTES	=>	undef,
		JVM_LIB				=>	"libjvm.$Config{dlext}",
		JVM_SO				=>	"libjvm.$Config{dlext}",
		PRE_WHOLE_ARCHIVE	=>  '-Wl,--whole-archive',
		POST_WHOLE_ARCHIVE	=>  '-Wl,--no-whole-archive',
		PERL_PARSE_DUP_ENV	=>  '-DPERL_PARSE_DUP_ENV',
		BUILD_JNI_BY_DFLT	=>  1,
		J2SDK_BIN			=>  'bin',
		DEFAULT_J2SDK_DIR   =>  sub {
			# for Ubuntu
			require File::Which;
			my $javapath = File::Which::which('java');
			$javapath = readlink $javapath while defined eval { readlink $javapath };
			require File::Basename;
			File::Basename::dirname(File::Basename::dirname($javapath));
		},
		OTHERLDFLAGS		=>  '',
		dynamic_lib			=>  {}
	},
	MSWin32 => {
		ENV_VAR_PATH_SEP_CP	=>	';',
		# 2>&1 doesn't work under command.com
		IO_REDIR			=>  ($COMMAND_COM ? '' : undef),
		MAKE				=>	'nmake',
		DEV_NULL			=>  'nul',
		COMMAND_COM			=>	$COMMAND_COM,
		SUB_FIX_MAKE_QUOTES	=>	sub {
			my $arg = shift ;
			$arg = qq{"$arg"} ;
			return $arg ;
		},
		SO_LIB_PATH_VAR		=>	'PATH',
		DETACH_OK			=>	0,
		JVM_LIB				=>	'jvm.lib',
		JVM_SO				=>	'jvm.dll',
		GOT_NEXT_FREE_PORT	=>	0,
		GOT_SYMLINK			=>	0,
		GOT_SAFE_SIGNALS	=>	0,
		PRE_WHOLE_ARCHIVE	=>  '',
		POST_WHOLE_ARCHIVE	=>  '',
		DEFAULT_J2SDK_DIR   => sub {
                        return $ENV{JAVA_HOME} if $ENV{JAVA_HOME};
                        my $reg;
                        require Win32::TieRegistry;
                        Win32::TieRegistry->import(Delimiter => "/", TiedRef => \$reg, ":KEY_");
                        $reg = $reg->Open('', {Access => &KEY_READ});
                        my $keybase = 'HKEY_LOCAL_MACHINE/SOFTWARE/JavaSoft/JDK';
                        my $key = "$keybase//CurrentVersion";
                        return undef unless my $version = $reg->{$key};
                        my $keyhome = "$keybase/$version//JavaHome";
                        scalar $reg->{$keyhome};
		},
	},
	cygwin => {
		ENV_VAR_PATH_SEP_CP	=>	';',
		SUB_FIX_JAVA_PATH	=>	sub {
			my $arg = shift ;
			if (defined($arg)&&($arg)){
				$arg = `cygpath -w \"$arg\"` ;
				chomp($arg) ;
			}
			return $arg ;
		},
		JVM_LIB				=>	'jvm.lib',
		JVM_SO				=>	'jvm.dll',
		BUILD_JNI_BY_DFLT	=>  1,
		dynamic_lib			=>  { OTHERLDFLAGS => '-Wl,-add-stdcall-alias' },
	},
	hpux => {
		GOT_NEXT_FREE_PORT  =>  0,
	},
	solaris => {
		GOT_NEXT_FREE_PORT  =>  0,
		PRE_WHOLE_ARCHIVE	=>  '-Wl,-zallextract',
		POST_WHOLE_ARCHIVE	=>  '-Wl,-zdefaultextract',
	},
	aix => {
		JVM_LIB				=>	"libjvm$Config{lib_ext}",
		JVM_SO				=>	"libjvm$Config{lib_ext}",
	},
	darwin => {
		# Suggested by Ken Williams, mailing list 2004/07/07
		SO_EXT				=>	$Config{so},
		# Andrew Bruno
		JVM_LIB				=>	"libjvm.dylib",
		JVM_SO				=>	"libjvm.dylib",
		PRE_WHOLE_ARCHIVE	=>  '-Wl',
		POST_WHOLE_ARCHIVE	=>  '-Wl',
		GOT_SYMLINK			=>	1,
		J2SDK_BIN        	=>  'Commands',
		DEFAULT_J2SDK_DIR   =>  sub {
			for my $suffix (qw(Current CurrentJDK)) {
				my $dir = "/System/Library/Frameworks/JavaVM.framework/Versions/$suffix";
				return $dir if -d $dir;
			}
			undef;
		},
		# Tim Bunce:
		OTHERLDFLAGS		=>  '-framework JavaVM',
	},
} ;

sub portable {
	my $key = shift ;
	my $arg = shift ;

	if (! exists($map->{_DEFAULT_}->{$key})){
		croak "Portability issue $key not defined!" ;
	}

	my $val = undef ;
	if ((defined($map->{$^O}))&&(defined($map->{$^O}->{$key}))){
		$val = $map->{$^O}->{$key} ;
	}
	else {
		$val = $map->{_DEFAULT_}->{$key} ;
	}
	$val = $val->() if ref($val) eq 'CODE' and $key !~ /^SUB_/;

	if ($key =~ /^SUB_/){
		my $sub = $val ;
		if (defined($sub)){
			$arg = $sub->($arg) ;
			Inline::Java::Portable::debug(4, "portable: $key => $arg for $^O is '$arg'") ;
			return $arg ;
		}
		else {
			Inline::Java::Portable::debug(4, "portable: $key => $arg for $^O is default '$arg'") ;
			return $arg ;
		}
	}
	else {
		Inline::Java::Portable::debug(4, "portable: $key for $^O is '$val'") ;
		return $val ;
	}
}


1 ;
