use strict;
use warnings;

my $jdkconfig = $ENV{HOME}.'/.idea/config/options/jdk.table.xml';

# Default skeleton
my $default = <<EOF;
<?xml version="1.0" encoding="UTF-8"?>
<!-- generated by $0 -->
<application>
  <component name="ProjectJdkTable">
  </component>
</application>
EOF

# Genrate config skeleton, or read existing config file
sub readconfig {
	my $input;
	open ($input, '<'.$jdkconfig)
		? map { chop; $_ } <$input>
		: split (/\n/, $default);
}

# Genrate config skeleton, or read existing config file
sub writeconfig {
	my $output;
	open ($output, '>'.$jdkconfig) or die $!;
	print $output join ("\n", @_);
}

# List of installed JDK roots
sub jdkhomes {
	my $alter;
	open ($alter, '/usr/sbin/update-alternatives --display javac |');
	return grep { not /gcj/ } map { /^(\/\S+)\/bin\/javac/ ? $1 : () } <$alter>;
}


# Craft a nice name for JDK
sub jdkname {
	my $jdkhome = shift;
	my $jdkname;

	my %jdknames = (
		'/java-1\.(\d+)\.\d+-openjdk'	=> 'OpenJDK',
		'/java-1\.(\d+)\.\d+-sun'	=> 'Sun JDK',
		'/java-([\.\d]+)-ibm'		=> 'IBM Java',
		'/java-([\.\d]+)-bea'		=> 'BEA Java',
		'/java-([\.\d]+)-gcj'		=> 'GNU Compiler for Java',
	);

	foreach (keys %jdknames) {
		$jdkhome =~ /$_/ and $jdkname = "$jdknames{$_} $1"
			and last;
	}
	($jdkname) = $jdkhome =~ /([^\/]*)$/ unless $jdkname;

	return $jdkname;
}

# Get JDK libraries
# Mocking findClasses (File, boolean) of
# java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java
sub jdkcp {
	my $jdkhome = shift;
	my @jars = map { <$jdkhome/jre/$_/*.jar> } ('lib', 'lib/ext', 'lib/endorsed');
}

# Get JDK version
sub jdkver {
	my $jdkhome = shift;
	my $version = [ `$jdkhome/bin/java -version 2>&1` ]->[0];
	chomp $version;
	$version =~ s/\"/&quot;/g;
	return $version;
}

# Generate XML config file chunk for a JDK directory
sub jdkconfig
{
	my $jdkhome = shift;

	return ( map { "    $_" }
		'<jdk version="2">',
		'  <name value="'.jdkname ($jdkhome).'" />',
		'  <type value="JavaSDK" />',
		'  <version value="'.jdkver ($jdkhome).'" />',
		'  <homePath value="'.$jdkhome.'" />',
		'  <roots>',
		'    <annotationsPath>',
		'      <root type="composite" />',
		'    </annotationsPath>',
		'    <classPath>',
		'      <root type="composite">',
		(map {
		'        <root type="simple" url="jar://'.$_.'!/" />'
		} jdkcp ($jdkhome)),
		'      </root>',
		'    </classPath>',
		'    <javadocPath>',
		'      <root type="composite" />',
		'    </javadocPath>',
		'    <sourcePath>',
		'      <root type="composite" />',
		'    </sourcePath>',
		'  </roots>',
		'  <additional />',
		'</jdk>',
	);
}

# Process the configuration and construct a new one
my @sourceconfig = readconfig;
my @targetconfig = ();

my @jdks = ();
my $thisjdk;
my $component = '';
foreach (@sourceconfig) {
	if (/<\/jdk/ and defined $thisjdk) {
		undef $thisjdk;
		next;
	}

	elsif (defined $thisjdk) {
		/<(\w+) value="([^"]*)" \/>/
			and $thisjdk->{$1} = $2;
		push @{$thisjdk->{lines}}, $_;
		next;
	}

	elsif (/<jdk version="2"/ and $component eq 'ProjectJdkTable') {
		$thisjdk =  { lines => [] };
		push @jdks, $thisjdk;
		next;
	}

	elsif (/<component name="([^"]*)"/) {
		$component = $1;
	}

	elsif (/<\/component/ and $component eq 'ProjectJdkTable') {
		push @targetconfig,
			# Generated configs for system-wide JVMs
			(map { jdkconfig ($_) } jdkhomes),
			# Already-defined JVMs, sans system-wide
			(map { $_->{homePath} =~ /^\/usr\/lib\/jvm/
				? () : @{$_->{lines}} } @jdks);
		@jdks = ();
	}

	push @targetconfig, $_;
}

# Rewrite the configuration
writeconfig (@targetconfig);
