#!/usr/bin/perl -w

use strict;
use warnings;
use Getopt::Long;
use File::Temp;

my %global_options = (
		      'prefix' => '/usr/local'
		     );
my %global;
$global{'project_identifier'} = 'YASKKSERV';
$global{'version'} = '0.5.2';

# GccIsSupport($gcc_version, 3, 2, 1);
sub GccIsSupport ($$$$) {
    my $gcc_version = $_[0];
    my $major = $_[1];
    my $minor = $_[2];
    my $sub = $_[3];

    if ($gcc_version >= $major * 1000000 + $minor * 1000 + $sub * 1) {
	return !0;
    } else {
	return 0;
    }
}

sub GccCheck ($$;$) {
    my $check_header = $_[0];
    my $check_body = $_[1];
    my $check_option = defined($_[2]) ? $_[2] : '';

    # thanks! KURASHIKI-san and Debian
    my $temporary_filename_base = '/tmp/yaskkserv.configure.XXXXX';
    my ($wfh_dummy, $temporary_filename_o) = File::Temp::mkstemps($temporary_filename_base, '.o');
    my ($wfh, $temporary_filename_c) = File::Temp::mkstemps($temporary_filename_base, '.c');
    print $wfh $check_header;
    print $wfh qq!int main(int argc, char *argv[])\n{\n!;
    print $wfh $check_body;
    print $wfh qq!return 0;\n}\n!;
    close($wfh);

    # thanks! KURASHIKI-san
    my $c = "$global{'gplusplus'} $check_option -c $temporary_filename_c -o $temporary_filename_o >/dev/null 2>&1";
    system($c);
    my $result = $? ? 0 : !0;

    unlink($temporary_filename_o);
    unlink($temporary_filename_c);

    return $result;
}

$global{'data'} .= "# -*- Makefile -*-\n";
$global{'data'} .= "#\n";
$global{'data'} .= "# $0";
foreach (@ARGV) {
    $global{'data'} .= " $_";
}
$global{'data'} .= "\n";
$global{'data'} .= "#\n";
$global{'data'} .= "# " . localtime() . "\n";
$global{'data'} .= "#\n";
$global{'data'} .= "# * DO NOT EDIT! *\n";
$global{'data'} .= "#\n";

GetOptions('gplusplus=s' => \$global_options{'gplusplus'},
           'help' => \$global_options{'help'},
           'enable-google-japanese-input' => \$global_options{'enable-google-japanese-input'},
           'enable-syslog' => \$global_options{'enable-syslog'},
           'disable-syslog' => \$global_options{'disable-syslog'},
           'enable-error-message' => \$global_options{'enable-error-message'},
           'disable-error-message' => \$global_options{'disable-error-message'},
           'precompile' => \$global_options{'precompile'},
           'prefix=s' => \$global_options{'prefix'});

if (defined($global_options{'help'})) {
    print "$0 usage\n";
    print "    --gplusplus=G++                G++ [default path G++]\n";
    print "    --help                         print this message\n";
    print "    --enable-google-japanese-input enable google japanese input (for yaskkserv_hairy)\n";
    print "    --enable-syslog                enable syslog [default]\n";
    print "    --disable-syslog               disable syslog\n";
    print "    --enable-error-message         enable error message [default]\n";
    print "    --disable-error-message        disable error message\n";
    print "    --precompile                   use precompile (only G++ 4.0 or newer)\n";
    print "    --prefix=PREFIX                install root directory [/usr/local]\n";
    die;
}

{
    if (defined($global_options{'gplusplus'})) {
	$global{'gplusplus'} = $global_options{'gplusplus'};
    } else {
	$global{'gplusplus'} = 'g++';
    }
    $global{'data'} .= "export GPLUSPLUS			= $global{'gplusplus'}\n";
}

{
    my $tmp = 'make';
    $_ = `$tmp -v 2>&1`;
    if (defined($_) and /gnu\s*make/im) {
	print "GNU Make ($tmp)\n";
    } else {
	$tmp = 'gmake';
	$_ = `$tmp -v 2>&1`;
	if (defined($_) and /gnu\s*make/im) {
	    print "GNU Make ($tmp)\n";
	} else {
	    $tmp = 'gnumake';
	    $_ = `$tmp -v 2>&1`;
	    if (defined($_) and /gnu\s*make/im) {
		print "GNU Make ($tmp)\n";
	    } else {
		print STDERR "*** Warning : GNU Make not found!\n";
	    }
	}
    }
}

{
    $global{'data'} .= "export MKDIR				= mkdir\n";
}

{
    $global{'data'} .= "export RM				= rm\n";
}

{
    $global{'data'} .= "export PERL				= perl\n";
}

{
    $global{'data'} .= "export INSTALL				= install\n";
}

{
    $global{'data'} .= "export PREFIX				= $global_options{'prefix'}\n";
}

{
    $global{'data'} .= "export PROJECT_IDENTIFIER		= $global{'project_identifier'}\n";
    $_ = $global{'project_identifier'};
    tr/A-Z/a-z/;
    $global{'data'} .= "export PROJECT_IDENTIFIER_LOWER_CASE	= $_\n";
}

{
    $global{'data'} .= "export PROJECT_VERSION			= $global{'version'}\n";
}

{
    $_ = `pwd 2>&1`;
    chomp;
    $global{'project_root'} = $_;
    $global{'data'} .= "export PROJECT_ROOT			= $global{'project_root'}\n";
}

{
    $_ = `uname 2>&1`;
    if (/bsd/i or /darwin/i or /cygwin/i or /linux/i) {
	$global{'architecture'} = 'BSD_CYGWIN_LINUX_GCC';
	$global{'CXXFLAGS_ARCHITECTURE'} = "-D $global{'project_identifier'}_ARCHITECTURE_BSD_CYGWIN_LINUX_GCC";
	$global{'LDFLAGS_ARCHITECTURE'} = '-lrt';
    } else {
	print STDERR "*** Warning : unknown architecture ($_)\n";
	$global{'architecture'} = 'BSD_CYGWIN_LINUX_GCC';
	$global{'CXXFLAGS_ARCHITECTURE'} = "-D $global{'project_identifier'}_ARCHITECTURE_BSD_CYGWIN_LINUX_GCC";
	$global{'LDFLAGS_ARCHITECTURE'} = '';
    }

    if (/cygwin/i) {
	$global{'EXECUTE_FILE_SUFFIX'} = '.exe';
    } else {
	$global{'EXECUTE_FILE_SUFFIX'} = '';
    }

    die "unknown architecture \"$_\"" unless defined($global{'architecture'});

    $global{'data'} .= "export CXXFLAGS_ARCHITECTURE		= $global{'CXXFLAGS_ARCHITECTURE'}\n";
    $global{'data'} .= "export LDFLAGS_ARCHITECTURE		= $global{'LDFLAGS_ARCHITECTURE'}\n";
    $global{'data'} .= "export ARCHITECTURE			= $global{'architecture'}\n";
    $_ = $global{'architecture'};
    tr/A-Z/a-z/;
    $global{'data'} .= "export ARCHITECTURE_LOWER_CASE		= $_\n";
    $global{'data'} .= "export EXECUTE_FILE_SUFFIX		= $global{'EXECUTE_FILE_SUFFIX'}\n";
}

{
    my @gcc_version;
    foreach (split(/[\r\n]+/, `$global{'gplusplus'} -v 2>&1`)) {
	if (/^\s*gcc\s+version/ and m!\s+(\d+)\.(\d+)\.?(\d+)?!) {
	    @gcc_version = ($1, $2, $3);
	    last;
	}
    }

    if ($#gcc_version == -1) {
	$global{'gcc_version'} = 0;
	print STDERR "*** Warning : G++ version failed.\n";
    } else {
	my $pow = $#gcc_version;
	foreach (@gcc_version) {
	    $_ = 0 unless defined($_);
	    $global{'gcc_version'} += $_ * (1000 ** $pow);
	    --$pow;
	}
	my $tmp;
	foreach (@gcc_version) {
	    $tmp .= "$_.";
	}
	chop $tmp;
	print "G++ ($tmp)\n";
    }

    unless (GccIsSupport($global{'gcc_version'}, 3, 3, 0)) {
	print STDERR "*** Warning : G++ is too old. You need version 3.3 or newer.\n";
    }
}

if (defined($global_options{'precompile'})) {
    if (GccIsSupport($global{'gcc_version'}, 4, 0, 0)) {
	$global{'data'} .= "export USE_PRECOMPILE   		= t\n";
    } else {
	print STDERR "*** Warning : G++ is too old. You need version 4.0 or newer.\n";
    }
}

{
    $_ = pack('V', 1);
    if (unpack('C', $_) == 1) {
	$global{'data'} .= "export CXXFLAGS_BYTE_ORDER		= -D $global{'project_identifier'}_ARCHITECTURE_BYTE_ORDER_VAX\n";
    } else {
	$global{'data'} .= "export CXXFLAGS_BYTE_ORDER		= -D $global{'project_identifier'}_ARCHITECTURE_BYTE_ORDER_NETWORK\n";
    }

    $global{'data'} .= "export CXXFLAGS_CONFIG			= \n";
    unless (defined($global_options{'disable-syslog'})) {
	$global{'data'} .= "CXXFLAGS_CONFIG				+= -D $global{'project_identifier'}_CONFIG_ENABLE_SYSLOG\n";
    }

    unless (defined($global_options{'disable-error-message'})) {
	$global{'data'} .= "CXXFLAGS_CONFIG				+= -D $global{'project_identifier'}_CONFIG_ENABLE_ERROR_MESSAGE\n";
    }

    if (defined($global_options{'enable-google-japanese-input'})) {
	$_ = 'iconv.h';
	my $header_symbol = $_;
	$header_symbol =~ tr/a-z/A-Z/;
	$header_symbol =~ s![\./]!_!g;
	if (GccCheck("#include <$_>\n",
		     '')) {
	    $global{'data'} .= "CXXFLAGS_CONFIG				+= -D $global{'project_identifier'}_CONFIG_ENABLE_GOOGLE_JAPANESE_INPUT\n";
	    $global{'data'} .= "CXXFLAGS_CONFIG				+= -D $global{'project_identifier'}_CONFIG_HEADER_HAVE_$header_symbol\n";
	    print "$_ (found)\n";
	} else {
	    print "$_ (not found : DISABLE --enable-google-japanese-include)\n";
	}
    }
}

{
    if (GccCheck("#include <sys/socket.h>\n",
		 "int tmp = MSG_NOSIGNAL;\n")) {
	$global{'data'} .= "CXXFLAGS_CONFIG				+= -D $global{'project_identifier'}_CONFIG_MACRO_HAVE_SYMBOL_MSG_NOSIGNAL\n";
	print "MSG_NOSIGNAL (found)\n";
    } else {
	print "MSG_NOSIGNAL (not found)\n";
    }
}

{
    $_ = `ccache 2>&1`;
    if ($? >= 0) {
	$global{'data'} .= "export CCACHE				= ccache\n";
	print "ccache (found)\n";
    } else {
	print "ccache (not found)\n";
    }
}

{
    my $common = '';
    if (GccCheck('', '', '-march=native')) {
	$common .= '-march=native';
    }
    my $o3_or_o1 = '';
    if (GccIsSupport($global{'gcc_version'}, 4, 0, 0)) {
	$o3_or_o1 .= '-O3';
    } else {
	# gcc3 Ϥʪ꤬ꥵʲ뤤ϵ路ʤС
	# ѥХ򤱤뤿˥ץƥޥ٥򲼤ޤ
	$_ = `cat /proc/meminfo 2>&1`;
	my $memory_size = 0 * 1024;
	if (/^MemTotal:\s+(\d+)\s+kB/) {
	    $memory_size = $1;
	    print "memory ($memory_size kB)\n";
	} else {
	    print "memory (unknown)\n";
	}

	if ($memory_size < 320000) {
	    $o3_or_o1 .= '-O1';
	    print STDERR "*** Warning : optimize level changed (-O3 to -O1)\n";
	} else {
	    $o3_or_o1 .= '-O3';
	}
    }

    {
	$global{'data'} .= 'export CXXFLAGS_OPTIMIZE_SERVER_SIMPLE	= ';

	$global{'data'} .= "$common -Os\n";
    }

    {
	$global{'data'} .= 'export CXXFLAGS_OPTIMIZE_SERVER_NORMAL	= ';

	$global{'data'} .= "$common -Os\n";
    }

    {
	$global{'data'} .= 'export CXXFLAGS_OPTIMIZE_SERVER_HAIRY	= ';

	$global{'data'} .= "$common $o3_or_o1\n";
    }

    {
	$global{'data'} .= 'export CXXFLAGS_OPTIMIZE_TOOL		= ';

	$global{'data'} .= "$common $o3_or_o1\n";
    }
}

{
    my $common = '-Wall -Wextra -W -Weffc++ -Wold-style-cast -Woverloaded-virtual -Wsign-promo -Wsynth -Wundef -Wshadow -Wlarger-than-16384 -Wpointer-arith -Wcast-qual -Wcast-align -Wconversion -Wsign-compare -Waggregate-return -Wmissing-noreturn -Wredundant-decls -Wnon-virtual-dtor -Wreorder -Wswitch-default -Wswitch-enum';
    my $common_4_2_0 = '-Wabi -Wcomments -Wctor-dtor-privacy -Wdeprecated -Wendif-labels -Wfloat-equal -Wformat-extra-args -Wformat-nonliteral -Wformat-security -Wformat-y2k -Wimport -Winvalid-pch -Wmissing-include-dirs -Wmultichar -Wpragmas -Wredundant-decls -Wundef -Wunused-macros -Wvariadic-macros -Winvalid-offsetof -Wnon-template-friend -Wpmf-conversions -Wstrict-null-sentinel';
    {
	$global{'data'} .= 'export CXXFLAGS_WARNING_SERVER_SIMPLE	= ';

	$global{'data'} .= "$common_4_2_0 " if GccIsSupport($global{'gcc_version'}, 4, 2, 0);
	$global{'data'} .= "$common\n";
    }

    {
	$global{'data'} .= 'export CXXFLAGS_WARNING_SERVER_NORMAL	= ';

	$global{'data'} .= "$common_4_2_0 " if GccIsSupport($global{'gcc_version'}, 4, 2, 0);
	$global{'data'} .= "$common\n";
    }

    {
	$global{'data'} .= 'export CXXFLAGS_WARNING_SERVER_HAIRY	= ';

	$global{'data'} .= "$common_4_2_0 " if GccIsSupport($global{'gcc_version'}, 4, 2, 0);
	$global{'data'} .= "$common\n";
    }

    {
	$global{'data'} .= "export CXXFLAGS_WARNING_TOOL		= ";

	$global{'data'} .= "$common_4_2_0 " if GccIsSupport($global{'gcc_version'}, 4, 2, 0);
	$global{'data'} .= "$common\n";
    }
}

{
    open(my $wfh, '>', 'Makefile.config') or die;
    print $wfh $global{'data'};
}
