use strict;
use warnings;
use utf8;
use Getopt::Long 'GetOptions';
use POSIX 'strftime';

binmode STDOUT, ':encoding(utf8)';

my $tt_version_major = 0;
my $tt_version_minor = 0;
my $tt_version_substr = "";
my $tt_version_h = "../../teraterm/common/tt-version.h";
my $version;
my $svn = "svn";
my $git = "git";
my $out_header = "svnversion.h";
my $out_bat = "sourcetree_info.bat";
my $out_cmake = "";
my $source_root = "..";
my $date = strftime "%Y%m%d", localtime;
my $time = strftime "%H%M%S", localtime;
my $verbose = 0;
my $script_name = $0;
my $header = "This file was generated by buildtools/svnrev/svnrev.pl";
my $overwrite = 0;
my %svninfo = (
	name => '',
	release => 0,
	Revision => ''
	);

sub read_toolinfo {
	my $info = "toolinfo.txt";
	if (!-f $info) {
		return;
	}
	open(my $FD, "<:utf8:crlf", $info);
	while (my $l = <$FD>) {
		chomp $l;
		$l =~ s/^\x{FEFF}//;	# remove BOM
		if ($l =~ /^#/) {
			next;
		}
		if ($l =~ /^svn=\s*(.*)$/) {
			$svn = $1;
		}
		if ($l =~ /^git=\s*(.*)$/) {
			$git = $1;
		}
	}
	close($FD);
}

sub search_svn {
	my @test_list = (
		"C:/Program Files (x86)/Subversion/bin/svn.exe",
		"C:/Program Files/Subversion/bin/svn.exe",
		"C:/Program Files/TortoiseSVN/bin/svn.exe",
		"C:/cygwin64/bin/svn.exe",
		"C:/cygwin/bin/svn.exe",
		"/usr/bin/svn.exe"	# msys, cygwin
		);
	for (my $i = 0; $i < @test_list; $i++) {
		my $test = $test_list[$i];
		if (-e $test) {
			$svn = $test;
			return;
		}
	}
}

sub search_git {
	my @test_list = (
		"C:/Program Files/Git/bin/git.exe",
		"C:/cygwin64/bin/git.exe",
		"C:/cygwin/bin/git.exe",
		"/usr/bin/git.exe"	# msys, cygwin
		);
	for (my $i = 0; $i < @test_list; $i++) {
		my $test = $test_list[$i];
		if (-e $test) {
			$git = $test;
			return;
		}
	}
}

sub dump_info {
	my %info = @_;

	print "SVNREVISION $info{'Revision'}\n";
	print "RELEASE $info{'release'}\n";
	print "BRANCH_NAME $info{'name'}\n";
}

sub read_whole_file {
	my $fname = shift;
	open(my $FD, '<', $fname) or die "error open $fname";

	my $text;
	while (<$FD>) {
		if (/DATE/ || /TIME/) {
			; #pass
		} else {
			$text .= $_;
		}
	}
	close $FD;
	return $text;
}

sub compare_file {
	my ($f1, $f2) = @_;
	if (! -f $f1) {
		return 1;
	}
	if (! -f $f2) {
		return 1;
	}
	my $t1 = read_whole_file($f1);
	my $t2 = read_whole_file($f2);
	if ($t1 eq $t2) {
		# return same
		return 0;
	}
	return 1;
}

sub write_info_header {
	my ($out_header, %svninfo) = @_;
	my $revision = $svninfo{'Revision'};

	open(my $FD, ">$out_header") or die "error $out_header";
	print $FD "/* $header */\n";
	print $FD "/* #define TT_VERSION_STR \"$version\" check teraterm/common/tt-version.h */\n";
	if ($revision ne '') {
		print $FD "#define SVNVERSION $revision\n";
	} else {
		print $FD "#undef SVNVERSION\n";
	}
	if ($svninfo{'release'}) {
		print $FD "#define TERATERM_RELEASE 1\n";
	} else {
		print $FD "#undef TERATERM_RELEASE\n";
	}
	print $FD "#define BRANCH_NAME \"$svninfo{'name'}\"\n";
	close($FD);
}

sub write_info_bat {
	my ($out_bat, %svninfo) = @_;
	my $revision = $svninfo{'Revision'};

	open(my $FD, ">$out_bat") or die "error $out_bat";
	print $FD "\@rem $header\n";
	print $FD "set VERSION=$version\n";
	if ($revision ne '') {
		print $FD "set SVNVERSION=$revision\n";
	} else {
		print $FD "set SVNVERSION=unknown\n";
	}
	print $FD "set RELEASE=$svninfo{'release'}\n";
	print $FD "set DATE=$date\n";
	print $FD "set TIME=$time\n";
	close($FD);
}

sub write_info_cmake {
	my ($out_cmake, %svninfo) = @_;
	my $revision = $svninfo{'Revision'};

	open(my $FD, ">$out_cmake") or die "error $out_cmake";
	print $FD "# $header\n";
	print $FD "set(VERSION \"$version\")\n";
	if ($revision ne '') {
		print $FD "set(SVNVERSION \"$revision\")\n";
	} else {
		print $FD "#set(SVNVERSION \"0000\")\n";
	}
	print $FD "set(RELEASE $svninfo{'release'})\n";
	print $FD "set(DATE \"$date\")\n";
	print $FD "set(TIME \"$time\")\n";
	close($FD);
}

sub read_tt_version_h()
{
	# ヘッダーファイルがない場合
	if (! -f $tt_version_h) {
		printf("no header\n");
		$tt_version_major = 0;
		$tt_version_minor = 0;
		$tt_version_substr = "no_header";
		return;
	}

	open(my $FH, '<', $tt_version_h) or die "error open $tt_version_h";
	while (<$FH>) {
		if (/^\s*#define\s+TT_VERSION_MAJOR\s+(\d+)/) {
			$tt_version_major = $1;
		}
		elsif (/^\s*#define\s+TT_VERSION_MINOR\s+(\d+)/) {
			$tt_version_minor = $1;
		}
		elsif (/^\s*#define\s+TT_VERSION_SUBSTR\s+\"(.+)\"/) {
			$tt_version_substr = $1;
		}
	}
	close $FH;
}

&read_tt_version_h();
&search_svn();
&search_git();
&read_toolinfo();

GetOptions(
	'root=s' => \$source_root,
	'svn=s' => \$svn,
	'git=s' => \$git,
	'header=s' => \$out_header,
	'bat=s' => \$out_bat,
	'cmake=s' => \$out_cmake,
	'verbose' => \$verbose,
	'overwrite' => \$overwrite
);

$git =~ s/"//g;
$git =~ s/\\/\//g;
$svn =~ s/"//g;
$svn =~ s/\\/\//g;

if ($tt_version_substr eq "") {
	$version = "$tt_version_major.$tt_version_minor";
} else {
	$version = "$tt_version_major.$tt_version_minor-$tt_version_substr";
}

if ($verbose != 0) {
	print "root=$source_root\n";
	print "tt_version_major=$tt_version_major\n";
	print "tt_version_minor=$tt_version_minor\n";
	print "tt_version_substr=$tt_version_substr\n";
	print "svn=\"$svn\"\n";
	print "git=\"$git\"\n";
	print "header=\"$out_header\"\n";
	print "bat=\"$out_bat\"\n";
	print "cmake=\"$out_cmake\"\n";
	print "overwrite $overwrite\n";
}

if (-d "$source_root/.svn" && $svn ne "") {
	# svn infoを実行、出力をすべて取り込む
	if (!open(my $FD, "-|", "\"$svn\" info --xml $source_root 2>&1")) {
		# svn が実行できない
		print "$script_name: '$svn' can not execute\n";
	}
	else {
		# 出力をすべて取り込む
		my $text = do { local $/; <$FD> };
		close($FD);

		# xmlパーサがインストールされていない環境もあるので
		# パターンマッチで実装
		if ($text =~ /<commit([^>]+)>/) {
			my $commit = $1;
			if ($commit =~ /revision=\"(\d+)\"/) {
				$svninfo{'Revision'} = $1;
			}
		}
		if ($text =~ /<relative-url>(.+)<\/relative-url>/) {
			my $url = $1;
			my $name = $url;
			$name =~ s/^\^\/(.*)$/$1/; # "\/" を削除する
			$svninfo{'name'} = $name;
		}
	}
}
elsif(-d "$source_root/.git" && $git ne "") {
	my $branch = `\"$git\" rev-parse --abbrev-ref HEAD`;
	if ($branch eq '') {
		# git が実行できない
		print "$script_name: \"$git\" can not execute\n";
	}
	else {
		$branch =~ s/[\r\n]$//g;
		$svninfo{'name'} = $branch;

		if (-d "$source_root/.git/svn") {
			# use git svn log
			my $revision = `\"$git\" svn log --oneline -1`;
			$revision =~ s/^r(\d+).*$/$1/;
			$svninfo{'Revision'} = $1;
		}
		else {
			$svninfo{'Revision'} = '';
		}
	}
}
else {
	# do not use VCS
}

my $release = 0;
if ($tt_version_substr eq "") {
	$release = 1;
}
$svninfo{'release'} = $release;


if ($verbose != 0) {
	&dump_info(%svninfo);
}

my $same;

# output for source(C,C++) header
$same = 0;
my $out_header_tmp = $out_header . ".tmp";
write_info_header($out_header_tmp, %svninfo);
if (!$overwrite && &compare_file($out_header, $out_header_tmp) == 0) {
	# same
	unlink $out_header_tmp;
	$same = 1;
} else {
	#diff
	unlink $out_header;
	rename $out_header_tmp,  $out_header;
}
if ($verbose != 0) {
	printf("%s '$out_header'\n", $same == 1 ? "exists" : "update" );
}

# output for bat file
$same = 0;
my $out_bat_tmp = $out_bat . ".tmp";
write_info_bat($out_bat_tmp, %svninfo);
if (!$overwrite && &compare_file($out_bat, $out_bat_tmp) == 0) {
	# same
	unlink $out_bat_tmp;
	$same = 1;
} else {
	#diff
	unlink $out_bat;
	rename $out_bat_tmp,  $out_bat;
}
if ($verbose != 0) {
	printf("%s '$out_bat'\n", $same == 1 ? "exists" : "update" );
}

# output for cmake
if ($out_cmake ne "") {
	my $out_cmake_tmp = $out_cmake . ".tmp";
	&write_info_cmake($out_cmake_tmp, %svninfo);
	$same = 0;
	if (!$overwrite && &compare_file($out_cmake, $out_cmake_tmp) == 0) {
		# same
		unlink $out_cmake_tmp;
		$same = 1;
	} else {
		# diff
		unlink $out_cmake;
		rename $out_cmake_tmp,  $out_cmake;
	}
	if ($verbose != 0) {
		printf("%s '$out_cmake'\n", $same == 1 ? "exists" : "update" );
	}
}
