#!/usr/bin/perl

use strict;

sub usage()
{
	print STDERR "$0 <svccall file> <kernel construction file> <output Makefile>";
}

sub check_precondition($$$)
{
	my $file1 = shift;
	my $file2 = shift;
	my $file3 = shift;
	my $ret = 1;
	
	# ե¸ߥå
	if(!(-e $file1))
	{
		print STDERR "Service call definition file not found.\n";
		$ret = 0;
	}
	if(!(-e $file2))
	{
		print STDERR "Kernel construction definition file not found.\n";
		$ret = 0;
	}
	
	return $ret;
}

sub get_svccalls($)
{
	my $svcdef_file = shift;
	my ($line , $lineno);
	my (@elems , @errmsgs);
	my $error = 0;
	my @svcs;
	
	open(IN , "<$svcdef_file");
	
	$lineno = 1;
	while($line = <IN>)
	{
		chomp $line;
		
		$lineno += 1;
		
		if($line eq "")
		{
			next ;
		}
		
		# use API 'servicecall name'|
		@elems = split(/\s/ , $line);
		if($elems[0] ne "use")
		{
			unshift @errmsgs , "Line $lineno : line does not start 'use'.\n";
			$error = 1;
		}
		elsif($elems[1] eq "API")
		{
			# add servicecall name
			unshift @svcs , $elems[2];
		}
		else
		{
			unshift @errmsgs , "Line $lineno : predicate must be 'API'.\n";
			$error = 1;
		}
	}
	
	close(IN);
	
	# 顼åɽ
	if($#errmsgs >= 0)
	{
		print @errmsgs;
	}
	
	return ($error , \@svcs);
}

sub get_dependencies($)
{
	my $knlcnst_file = shift;
	my ($line , $lineno);
	my @errmsgs;
	my $error = 0;
	my (%defs , %reqs , %patches);
	my ($apiname , $predicate);
	
	open(IN , "<$knlcnst_file");
	
	$lineno = 1;
	
	while($line = <IN>)
	{
		chomp $line;
		
		$lineno += 1;
		
		if($line eq "")
		{
			next ;
		}
		
		# use API 'servicecall name'|
		my @names;
		($apiname , $predicate , @names) = split(/\s/ , $line);
		if($predicate eq "define")
		{
			# add function compile macro
			$defs{$apiname} = \@names;
		}
		elsif($predicate eq "require")
		{
			# add patch files
			$reqs{$apiname} = \@names;
		}
		elsif($predicate eq "patch")
		{
			# add patch files
			$patches{$apiname} = \@names;
		}
		else
		{
			unshift @errmsgs , "Line $lineno : predicate must be 'define' or 'require'.\n";
			$error = 1;
		}
	}
	
	close(IN);
	
	my $key;
	my @elems;
	# 顼åɽ
	if($#errmsgs >= 0)
	{
		print @errmsgs;
	}
	
	return ($error , \%defs , \%reqs , \%patches);
}

sub addlf($)
{
	my $line = shift;
	my $mod = length($line) % 100;
	
	if((length($line) % 90) > 60)
	{
		$line .= "\t\\\n\t"
	}
	
	return $line;
}

sub gen_compilemacro($$)
{
	my $defines = shift;
	my $svcs = shift;
	my $macrodef = "";
	my $array;
	my @macros;
	
	# ޥΥꥹȤ
	foreach my $key (keys (%$defines))
	{
		if(grep(/^$key$/ , @$svcs))
		{
			$array = $$defines{$key};
			unshift @macros , @$array;
		}
	}

	# ʣϢ
	foreach my $macro (get_uniary(\@macros))
	{
		$macrodef .= "-D" . $macro . "\t\\\n\t";
	}
	
	return ("CDEFS := \$(CDEFS)\t\\\n\t" . $macrodef);
}

sub get_uniary($)
{
	my $array = shift;
	my %tmp;
	
	foreach my $elem (@$array)
	{
		$tmp{$elem} = 1;
	}
	
	return keys(%tmp);
}

sub gen_kernelobj($$)
{
	my $requires = shift;
	my $svcs = shift;
	my $objdef;
	my $array;
	my @objs;

	$objdef = "KERNEL_COBJS := \$(KERNEL_COBJS) ";
	
	# ֥ȤΥꥹȤ
	foreach my $key (keys (%$requires))
	{
		if(grep(/^$key$/ , @$svcs))
		{
			$array = $$requires{$key};
			unshift @objs , @$array;
		}
	}
	
	# ʣϢ
	foreach my $objname (get_uniary(\@objs))
	{
		$objdef .= $objname . " ";
		$objdef = addlf($objdef);
	}
	
	return $objdef . "\n\n";

}

sub gen_patchcommand($$)
{
	my $patches = shift;
	my $svcs = shift;
	my $makecommand;
	my (@apply , @remove);
	my $array;
	
	foreach my $key (keys (%$patches))
	{
		if(grep(/^$key$/ , @$svcs))
		{
			$array = $$patches{$key};
		
			unshift @apply , "\tpatch $$array[0] $$array[1]";
			unshift @remove , "\tpatch -R $$array[0] $$array[1]";
		}
	}
	
	$makecommand = "apply_patch : \n" . join("\t\t\\\n" , @apply) . "\n\n";
	$makecommand .= "remove_patch : \n" . join("\t\\\n" , @remove) . "\n\n";
	
	return $makecommand;
}

sub header()
{
	my $string = << 'END';
# 
#  SSP kernel configuration file
# 
#   This file generated by config_kernel.pl
#   DO NOT EDIT!
# 


END

	return $string;

}

sub main($$$)
{
	my $svcdef_file = shift;
	my $knlcnst_file = shift;
	my $makefile = shift;
	my $error;
	my ($svccalls , $defines , $requires , $patches);
	
	# ϡϤΥå
	if(($#ARGV != 2)
		&& (check_precondition($svcdef_file , $knlcnst_file , $makefile) == 0))
	{
		usage();
	}
	
	# ӥ
	($error , $svccalls) = get_svccalls($svcdef_file);
	if($error != 0)
	{
		exit(1);
	}
	
	# ޥѥåե
	($error , $defines , $requires , $patches) = get_dependencies($knlcnst_file);
	
	if($error != 0)
	{
		exit(1);
	}
	
	# 
	if(open(OUT , ">$makefile") == 0)
	{
		print STDERR "Can't open $makefile.\n";
		exit(1);
	}
	
	print OUT header();
	print OUT gen_compilemacro($defines , $svccalls) . "\n";
	print OUT gen_kernelobj($requires , $svccalls) . "\n";
	print OUT gen_patchcommand($patches , $svccalls) . "\n";
	
	close(OUT);
}

main($ARGV[0] , $ARGV[1] , $ARGV[2]);
